mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-06 11:41:37 -07:00
Compare commits
1758 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d6125f05d4 | |||
| ad22eda87c | |||
| 8d31ba492d | |||
| 8f611b3399 | |||
| 618f858930 | |||
| 185277a2b6 | |||
| 8e5c3141f6 | |||
| 87aaa9c3f0 | |||
| 095605b7db | |||
| 6836b501af | |||
| d949d5a046 | |||
| 064516bf0b | |||
| eaf88bcc4d | |||
| 76d3bab955 | |||
| 02f9668185 | |||
| abda75feee | |||
| a779cb2798 | |||
| 5e42675b42 | |||
| 11c565e199 | |||
| 4689a6b341 | |||
| 30a5808460 | |||
| 8ba1c9a6cd | |||
| 65b78b1aa9 | |||
| c369719362 | |||
| 40a12cca53 | |||
| d8940f5fd7 | |||
| 74dee82dbf | |||
| f1cf724bd4 | |||
| fc809d3fad | |||
| 42d135aade | |||
| ab12ca69af | |||
| 17748f18b9 | |||
| 3803bdff5f | |||
| 0a94a67190 | |||
| 8dcb14fbc2 | |||
| f62f40e508 | |||
| 980aa37bee | |||
| 1e9cc058a0 | |||
| 824efc82b9 | |||
| 1cfe226686 | |||
| ea0ce7bd2c | |||
| 342adb627d | |||
| a70635d2f6 | |||
| 5cb16c4cd1 | |||
| 538004ba5f | |||
| 8663c64e47 | |||
| bb3ffdbdfb | |||
| fcf7cf973b | |||
| fb2e1c32bd | |||
| ae0b8fce44 | |||
| d2e55f9ffa | |||
| d5e2b387fa | |||
| 922b511a24 | |||
| 9de9111082 | |||
| 5a7fe2e3d9 | |||
| 1e8c12c2a3 | |||
| b92c70e55c | |||
| 352494cbb4 | |||
| 797c41950a | |||
| e036a321a0 | |||
| 18471f358e | |||
| 55d09251d8 | |||
| d66fd71d21 | |||
| f10fc2a494 | |||
| 055d8c5370 | |||
| 955472bd21 | |||
| a2d5f660ed | |||
| 8a4b9ddaa3 | |||
| 54a2b6f00e | |||
| abe728dbbb | |||
| 574ceb37a9 | |||
| dbdc4471c2 | |||
| af95381bf8 | |||
| 41f2539484 | |||
| 8f57bb0799 | |||
| 84a4188c72 | |||
| 31f1e0aeae | |||
| 4b8cbb5a3b | |||
| 3e7aabe6d8 | |||
| d4df325e0a | |||
| 9f8a63cb43 | |||
| 6f745677b4 | |||
| 86649e6b44 | |||
| 3ca887a60a | |||
| 312a1e8a94 | |||
| 9a311ab9e7 | |||
| 7d92eaaeb2 | |||
| 582f423b67 | |||
| c2524464f9 | |||
| 55e472cdb6 | |||
| e6ad773a88 | |||
| 82eae32bca | |||
| b0e21bd616 | |||
| 7b88bed098 | |||
| 28abf5d33b | |||
| fb8ad338d0 | |||
| ee60be0137 | |||
| 9efd4c5097 | |||
| f30e6f50bd | |||
| 8ec2ca9dcd | |||
| f19bb0f4d4 | |||
| 76bb82a726 | |||
| 8e62f9c83c | |||
| 696f758435 | |||
| 6421152104 | |||
| 1e989f5c10 | |||
| 3eb42ecb55 | |||
| 286cb60c45 | |||
| 28e9a4f79c | |||
| e79fb68291 | |||
| 16eaa17ed9 | |||
| 9491c13a5c | |||
| 8d8adac1b4 | |||
| ba6c63e366 | |||
| 963b1a12d7 | |||
| 89890d7900 | |||
| 661fcad895 | |||
| cef16feb0b | |||
| 84b851f578 | |||
| 785c27daa7 | |||
| 795a5910cf | |||
| 4c21f9495d | |||
| f119fd1ee1 | |||
| 46ab822316 | |||
| 104aea9f42 | |||
| a2f2041aa6 | |||
| 599d18f26f | |||
| 61084d832d | |||
| b957996577 | |||
| f41150f8e9 | |||
| 21adad4e09 | |||
| 47f9e8f850 | |||
| ba3f2a6a0c | |||
| b9888f8f68 | |||
| 32cda5d56a | |||
| a4d9847f45 | |||
| 8aa6d70dec | |||
| ea6b222430 | |||
| 91b4bf3daa | |||
| 8c943eb054 | |||
| 446703ba75 | |||
| 266b5d7d85 | |||
| d90adb7a8e | |||
| 2573da12fc | |||
| bbc9c6a93c | |||
| 673134185a | |||
| 3bf00e6125 | |||
| 8fd4fe25d6 | |||
| ea1dc5373d | |||
| b8d6dd4eb7 | |||
| 4d80f8598d | |||
| bc4591fc1e | |||
| 6465fb8ec7 | |||
| 46204ed7f0 | |||
| 0f91ffe28f | |||
| 4b46d691f8 | |||
| 599d0cf6ac | |||
| d7dba495fd | |||
| c05f6b4a31 | |||
| a0a578c72a | |||
| 55fc2c00c0 | |||
| 6944e2fc04 | |||
| c8b353b6d8 | |||
| 6a8a31824d | |||
| 275b8d2e8a | |||
| 8cfdd9805d | |||
| 7d304f4e8b | |||
| b65e5d5e03 | |||
| b110153b51 | |||
| b261937233 | |||
| 4455ba5b65 | |||
| e8aa767c8d | |||
| bb7bd94b0a | |||
| 4a886a1bc5 | |||
| c21c754b6f | |||
| e948fcd2f1 | |||
| 0c46d06e63 | |||
| 44b6e7c45d | |||
| 284a2631fd | |||
| 3f156bcb4b | |||
| d35a9e65b6 | |||
| c440296ae8 | |||
| 79f115d6a7 | |||
| 39eccdf6b9 | |||
| 71cfa0c9fe | |||
| e82f8214e8 | |||
| b8f8d4c3a1 | |||
| 9e2eb717fe | |||
| f0c9f82e1b | |||
| cc1d3a7b19 | |||
| 159daba759 | |||
| d7e85725e1 | |||
| 247f4f3293 | |||
| 8758afdf4e | |||
| 362f07d7c5 | |||
| fe74b479c6 | |||
| 44ee0f2cdc | |||
| 50035f257e | |||
| 92abddddc5 | |||
| 4d580ed693 | |||
| 9c54ed5792 | |||
| 5108ebf015 | |||
| 86dbcb1b20 | |||
| 3ac934dd15 | |||
| 26ce40c188 | |||
| b34e4a9fd1 | |||
| 28561ef5f5 | |||
| 10b798456c | |||
| a580cdbe7b | |||
| e82e4398b6 | |||
| e0225c3579 | |||
| 8d044fd442 | |||
| 6ef96d3300 | |||
| 2e633f57a0 | |||
| 50369cf19c | |||
| 385385364b | |||
| 9ccdc40f65 | |||
| df3512b112 | |||
| ce08e832f7 | |||
| 24c89b3eee | |||
| 85e5be03d1 | |||
| b1cfeb6c95 | |||
| fffb83282b | |||
| 1cf6b6679d | |||
| 7886798156 | |||
| 0ad599675e | |||
| 5d03bc61b8 | |||
| 5e31c04a01 | |||
| 7b7081d607 | |||
| 5ae564cc8c | |||
| 0507b9ebb7 | |||
| f7b1290fe9 | |||
| b01c2e1017 | |||
| 63b54ce7c6 | |||
| b8a35c658c | |||
| 465b6139d5 | |||
| b37d344eb2 | |||
| d4ee1a9e19 | |||
| be12f12c3c | |||
| 2e5f57d8a1 | |||
| d5c3f99655 | |||
| 083e9e2053 | |||
| c6c5af527b | |||
| 5420cc083d | |||
| c81f7b31c6 | |||
| fdd0c726bc | |||
| da9cabc334 | |||
| d962cfdc6b | |||
| dfb8143149 | |||
| d2fcbf0e1d | |||
| e08f39ec28 | |||
| d353f97f91 | |||
| 8144e82c6c | |||
| d0f7cf74e9 | |||
| e1c2528d87 | |||
| eac61fb536 | |||
| 20f7e61363 | |||
| 8bb6651dda | |||
| 8ff80dec58 | |||
| f31b09212a | |||
| 073bd3f6c9 | |||
| 9a4d942b0b | |||
| ff6fdc7812 | |||
| c85c5ec38c | |||
| d70b5475eb | |||
| 42138d1ad5 | |||
| 47f97b8f89 | |||
| 902be0d09c | |||
| 502ad0c10b | |||
| 27f5aadd5a | |||
| da7a9a3584 | |||
| 03862d1f48 | |||
| 15ce8b0454 | |||
| 4f51d74fc2 | |||
| b6a1ec0229 | |||
| 60685369b9 | |||
| 0f2a9e6143 | |||
| 278de3a92f | |||
| abdf54800e | |||
| 579b317be8 | |||
| 08360e2337 | |||
| f11f84964d | |||
| bb9c1d04db | |||
| f3c4d97250 | |||
| afb6ebd21e | |||
| 6fa5eb9e1f | |||
| 94570a24c1 | |||
| 17001bf38c | |||
| b83e67f45f | |||
| a5eea97edb | |||
| 866d5302fe | |||
| 5d70289b69 | |||
| d76ba7895b | |||
| dfe1dedeb1 | |||
| d027a5f1a4 | |||
| 746e880eb5 | |||
| e4286d0ff9 | |||
| 3a556eb304 | |||
| b44a56118e | |||
| 0d044eabac | |||
| 3cf26af600 | |||
| 9a39ce6b75 | |||
| 3a5245dcce | |||
| ca0f36be42 | |||
| 59b3c472ca | |||
| 357f66fcee | |||
| 050f128554 | |||
| 1a3784c4df | |||
| 518ecaf9c9 | |||
| 77e2e1e1d0 | |||
| 448f834b28 | |||
| 9212e3176a | |||
| 3160a6a12c | |||
| ef7b72d14c | |||
| 7d534769d6 | |||
| 0a81fcc2af | |||
| 2451014b6e | |||
| 348d25c43a | |||
| 9854dbe889 | |||
| 038337fd07 | |||
| cf0bf1a7cb | |||
| 364e6c9573 | |||
| 52d38ae42b | |||
| 2dbdff07c5 | |||
| 2ad8f99790 | |||
| 87a1833862 | |||
| 0a0ba95e85 | |||
| 433d742183 | |||
| b845db1618 | |||
| ced7700cdb | |||
| 406d2ab6ba | |||
| 327116b84b | |||
| fd56b48825 | |||
| 1ef9b094b6 | |||
| 0659a11a1a | |||
| 956b59d48c | |||
| 97b537f800 | |||
| ae907bf724 | |||
| db1ef078ff | |||
| c2603297ee | |||
| a02bcd3bfd | |||
| 0e972296a4 | |||
| 5ec82623ab | |||
| 47ff50a92d | |||
| a1558b3398 | |||
| 5f617c56e1 | |||
| 7ef30355cb | |||
| 06f1eaa153 | |||
| 58e023e0cf | |||
| 222bab501c | |||
| 9c6ab05981 | |||
| bee637aef1 | |||
| d5844dfd2a | |||
| 3751322521 | |||
| 71ac145f49 | |||
| e50ef33c2a | |||
| 3061dd2497 | |||
| 088f4b5039 | |||
| 002184cd49 | |||
| 02da4d61ae | |||
| 102c29575b | |||
| 24fce21d90 | |||
| afe185ee22 | |||
| ffffb04d85 | |||
| 17390cd317 | |||
| 7ac80544a1 | |||
| 96b60d8779 | |||
| 24f2d97c0e | |||
| f6d98d2a16 | |||
| ab5460e2e2 | |||
| cd1658e56c | |||
| e7e1866e50 | |||
| 51b198aa56 | |||
| 94fd59e6f0 | |||
| c07ab5f9ad | |||
| 06e0da97b7 | |||
| 04535b2913 | |||
| 6667b78c12 | |||
| 7e0c78eae7 | |||
| 6d1eb473cf | |||
| 898245431f | |||
| 43942a6199 | |||
| 53c7e65c57 | |||
| a38586420c | |||
| 1aa9c49172 | |||
| 5a88fef483 | |||
| 43b55223af | |||
| aa846d0ea5 | |||
| 8d0428a8b3 | |||
| 86c1aed20d | |||
| 1d628a5e3d | |||
| af3784ebb6 | |||
| ff933ca37f | |||
| bbf79fc1d4 | |||
| 2af8e956a6 | |||
| 9ed826f6fb | |||
| 07f9e551c1 | |||
| df4404e093 | |||
| 583fd89fcf | |||
| 2691c394e0 | |||
| 4662bad59c | |||
| 7086b49ae6 | |||
| 72aa0e6e38 | |||
| cb2e6ea31d | |||
| 74c683e908 | |||
| 6383714bff | |||
| a25e796b00 | |||
| 833784e196 | |||
| 66a70b9d27 | |||
| cd125c0f41 | |||
| ff620422fa | |||
| 5685a8d885 | |||
| b8327ad00d | |||
| 1bdce19c18 | |||
| 041ff13672 | |||
| 60a2eaf666 | |||
| ec2db568c1 | |||
| 1387fd9047 | |||
| fb6288a54e | |||
| 4d00058858 | |||
| 5c26cf9d73 | |||
| 6048d25d36 | |||
| 748f094e15 | |||
| 184c686c7e | |||
| 240a568eb9 | |||
| ad953c4670 | |||
| 0f9b0c2052 | |||
| cde334ada3 | |||
| 2b4010998d | |||
| 4272fe5e85 | |||
| c29b643a58 | |||
| 87bfd31664 | |||
| 5fba548aa0 | |||
| aafbf5bac7 | |||
| 951e940470 | |||
| 3b5b845ea6 | |||
| 3f9ad6ba09 | |||
| c9b82a21e9 | |||
| bd31a99bf7 | |||
| f10f7ebbf1 | |||
| 17bbb48d8a | |||
| ac53577e9b | |||
| 896057b5a7 | |||
| 01db195b47 | |||
| d33350e3a5 | |||
| 8a86c4c7cc | |||
| 1c9f48031d | |||
| 7fcd5ecbd5 | |||
| b68b090f13 | |||
| 542098dc6f | |||
| 0a66f8c9a3 | |||
| 158e4c0b6c | |||
| 47c7dc6a9b | |||
| 65551ba8fb | |||
| 40ed7e48d0 | |||
| ab53900c95 | |||
| c7d92562c2 | |||
| 5b3802b8ca | |||
| 43589a56b7 | |||
| 2fb0383e82 | |||
| de1838c0cb | |||
| 9b8563dfc0 | |||
| b18e8898d8 | |||
| ca112c0b6d | |||
| 6eb64f2bdc | |||
| 25c3b6dc95 | |||
| 8077d5f565 | |||
| 6b45856e81 | |||
| 28e5df6e7f | |||
| 22867ec6f0 | |||
| d0a50ff0a3 | |||
| e9b1d4f633 | |||
| ba9f0dd553 | |||
| c3b3194a00 | |||
| 201cad51a9 | |||
| d21e522208 | |||
| f1bdd2e60f | |||
| 3464784e5e | |||
| f1dc7c0b27 | |||
| 0eb75886d1 | |||
| 3612569624 | |||
| c83d13d0cb | |||
| f98c4d6837 | |||
| a4d0092b16 | |||
| eb49b5f84a | |||
| 9d949ef82e | |||
| c708f649ec | |||
| f7b8b3a340 | |||
| 2cbb3443d3 | |||
| 11bd518f36 | |||
| ae04af4e4e | |||
| 3baddd4116 | |||
| a721802337 | |||
| 6ae468828c | |||
| b59e110fec | |||
| 4282688e60 | |||
| 9b3fb25a41 | |||
| 2ce9873464 | |||
| 986844a0f0 | |||
| 7c1f538c02 | |||
| 2f1c896290 | |||
| 5a8c7b4f90 | |||
| f17fc05ff2 | |||
| a12db382e0 | |||
| 22daa50374 | |||
| a09300fb0e | |||
| 8827818b1d | |||
| bab4e5531a | |||
| e19958152a | |||
| 05a181d719 | |||
| d24784074c | |||
| cd2476e084 | |||
| ebbc33fdb5 | |||
| 1de928df78 | |||
| 3a4373641b | |||
| 9adec628c1 | |||
| aded272b33 | |||
| b0721f1e0c | |||
| 7e90ece84a | |||
| 1f563f2810 | |||
| 3e54cddf64 | |||
| d602709133 | |||
| c46317c00b | |||
| cb4b7efd3e | |||
| 411beaa3bf | |||
| 1cb3a31f32 | |||
| c99afd04ad | |||
| 3157467e4b | |||
| 2001384ae6 | |||
| b09e53d7fe | |||
| 8e1120bc09 | |||
| d046c9c072 | |||
| ebed29fc09 | |||
| bee8beb2c9 | |||
| 12296a0dcc | |||
| 2e8da35851 | |||
| 2942e45ff5 | |||
| aca5c7c1c6 | |||
| a1d871360c | |||
| 1d3458cadb | |||
| 2b36069924 | |||
| 9139ca4673 | |||
| 4fb4ada2c7 | |||
| e3ee005404 | |||
| e8bf1f2064 | |||
| e0e6bba865 | |||
| 1156bd9dd0 | |||
| 52c193316b | |||
| 2db1fe7429 | |||
| 5bc9e4e4f7 | |||
| 2d99a348b8 | |||
| 011c8d5cda | |||
| 8169cb4853 | |||
| 18ab7fb68b | |||
| 3eaf3327d4 | |||
| d721ffa6fa | |||
| 535048e2b3 | |||
| 050d6f670e | |||
| ae7d7d33d4 | |||
| 19470683e7 | |||
| 394e4fcbad | |||
| f722cceef0 | |||
| 66fb2a94ee | |||
| f78b2a6090 | |||
| 46bf39cae1 | |||
| 46c5c7d1ec | |||
| bcda9ddee7 | |||
| 4a66cbb1f4 | |||
| f0392d0c75 | |||
| 3bd39886a0 | |||
| cef698afd6 | |||
| 0fc04a939e | |||
| 3e1b4c17ac | |||
| b3e788b9d9 | |||
| 63bb26c0e0 | |||
| b3dce34bc6 | |||
| 491b282615 | |||
| 54e6c50769 | |||
| a00d7cc705 | |||
| 9757af5e4a | |||
| 3359d0323a | |||
| fbd01660ee | |||
| bc5e80d7d6 | |||
| c429c85ade | |||
| 02bbac0903 | |||
| b8658547e0 | |||
| 2c14accfd0 | |||
| 439e2c9242 | |||
| 6fb0868517 | |||
| e1588c9322 | |||
| 8fcc6ca464 | |||
| faa0240900 | |||
| 66941aed0a | |||
| fc8c94324e | |||
| 8c20ac1bf0 | |||
| 371094b067 | |||
| c1e125a009 | |||
| 48767301a4 | |||
| 253a3edd30 | |||
| 8239cd34eb | |||
| 818942b72e | |||
| 9bac9af022 | |||
| 342761e83a | |||
| 87bc9275fe | |||
| 5ade037b7d | |||
| 1e587d6635 | |||
| e21d716720 | |||
| d62ff39e60 | |||
| 0ccf103160 | |||
| 01c2662de4 | |||
| bd607b131e | |||
| 43ac6ca8f4 | |||
| 62d2656f69 | |||
| a611cf545d | |||
| 7463ad23d8 | |||
| 6271d10af7 | |||
| c5c82e0551 | |||
| 29ed1d5459 | |||
| 50b439daa8 | |||
| 23c68b4f7a | |||
| 615104afd1 | |||
| d3665331c7 | |||
| 04be6d190e | |||
| 6668fd44cf | |||
| 0007ac63ed | |||
| f656968aea | |||
| 24e7b5ea78 | |||
| f4d0f74dbb | |||
| 7c951cbf0a | |||
| 2d0e2625ac | |||
| 1aecd7f5ff | |||
| 5faf859a56 | |||
| 7028797d53 | |||
| 6c9d3715d8 | |||
| 989a03dc77 | |||
| 7a045bd957 | |||
| a5329ae48c | |||
| 2ec3326653 | |||
| bec42e8dd3 | |||
| d969d0b264 | |||
| 0e3ecc350a | |||
| 295c1550a8 | |||
| fc1a352285 | |||
| 082381284c | |||
| ccd078620b | |||
| 08f7d3edbe | |||
| c62980eaea | |||
| d88d6a1fdd | |||
| f1e13bdddb | |||
| 331167e91f | |||
| e229882fde | |||
| 300f988905 | |||
| 7f6a43f0af | |||
| 06c5a7fb3e | |||
| 781f784375 | |||
| ccd91aee5c | |||
| bf46a3f1d3 | |||
| 8ab1b32fe1 | |||
| 0355908af8 | |||
| fd7d70954e | |||
| cb0bd80f50 | |||
| 5a67e6ecbd | |||
| c8a823a387 | |||
| 178e891c78 | |||
| bda9eb7348 | |||
| 2b806f7463 | |||
| 6de0b68928 | |||
| bb6e6fb4ef | |||
| 5f5dfdc00e | |||
| 88b8052532 | |||
| 2c0ddfb5db | |||
| 2f4902c9b9 | |||
| d28f6144a4 | |||
| 3337412228 | |||
| e3f9d99a3b | |||
| 359ae29d98 | |||
| 453b756edc | |||
| d80670fe0c | |||
| 04e0cacd30 | |||
| b94ab4dc05 | |||
| 85fd8aaf37 | |||
| 6412a18ae1 | |||
| acefb2edbc | |||
| 0d1addf0d4 | |||
| f5142aaec4 | |||
| db0ac2ba98 | |||
| 468ad4837b | |||
| 2aa588209e | |||
| 10f91a0d3f | |||
| 8cc4cba06c | |||
| 8f6439fa6b | |||
| 81efd2dc64 | |||
| b5c0c68615 | |||
| a60d22fefd | |||
| 8123ff83a3 | |||
| 4e2a2ac80a | |||
| d23c10d80e | |||
| ba62bcf116 | |||
| 84bb349900 | |||
| 3ff7bded64 | |||
| 8784b79482 | |||
| d0f923ba74 | |||
| 4e8ec4ce38 | |||
| 4f1fb73f32 | |||
| cefde23232 | |||
| 083cec6c88 | |||
| 45cd5c0b1c | |||
| 572b9d1b3f | |||
| 0ae1b96182 | |||
| 1988856eed | |||
| 15dbc99cb9 | |||
| 17eb0ce525 | |||
| 619a977856 | |||
| b727b6438b | |||
| 75f127af7c | |||
| c2794b62a9 | |||
| 4e63ab5edc | |||
| 2a1bec9fbb | |||
| 6faf024ab4 | |||
| 35edf45514 | |||
| 667aa3671e | |||
| b428e9ee14 | |||
| 868117cddd | |||
| 9e0210f7f6 | |||
| 302742689b | |||
| 6ffb4d2683 | |||
| 4f298a5314 | |||
| f49278b5f4 | |||
| a2557142cc | |||
| fa09640e7e | |||
| 1f9a2df42b | |||
| ee04dde7c2 | |||
| 88599eeac1 | |||
| 54194af71c | |||
| 575c7ff031 | |||
| 7b4ddd9b18 | |||
| ac155d9036 | |||
| d772710ae7 | |||
| 172fc9a46c | |||
| 8182d9e828 | |||
| d0ac9f605d | |||
| 3eae69effc | |||
| 53e4c536a1 | |||
| 84deee75cc | |||
| 82275082ff | |||
| fc48e9638a | |||
| 4a69bfada1 | |||
| 4d13a43590 | |||
| b2fdb53e26 | |||
| 58422711d1 | |||
| ba817d153c | |||
| 2d88c6c6a5 | |||
| a6fdb5eb5e | |||
| 425bb0579e | |||
| a9af8b093d | |||
| 8f9111e552 | |||
| 731e6ca3a6 | |||
| 79eb46bce3 | |||
| d39d238754 | |||
| 0e9dee62bf | |||
| 533dde4648 | |||
| 6988b12012 | |||
| aa93305015 | |||
| 41b854f192 | |||
| 28f279329d | |||
| fe33a64b2e | |||
| f95185a16e | |||
| 01313eec7f | |||
| a1693c0184 | |||
| 657c0ac4f5 | |||
| 908a403d78 | |||
| 28ed310f31 | |||
| a652c24ac3 | |||
| 595885d3db | |||
| efdee0dafb | |||
| f332cf498d | |||
| 6ae619c0a6 | |||
| 7ea86f9c91 | |||
| 0f11c47579 | |||
| 5cced38a6e | |||
| d5dfa14f18 | |||
| fa02dc9736 | |||
| 92431c83ec | |||
| f5418837f0 | |||
| 825c5a963f | |||
| f50ea36c70 | |||
| de9b6e3f6e | |||
| 415379e1cf | |||
| d168c7936e | |||
| aed63a6e8b | |||
| 98521618e6 | |||
| e64eb74eef | |||
| 31528f5ea2 | |||
| ac184957db | |||
| 92e23ec397 | |||
| c71c86f563 | |||
| 773be83cad | |||
| e789e1eac1 | |||
| 00ebc877a1 | |||
| 3224869c29 | |||
| 38c6182280 | |||
| 24db60eb4b | |||
| 53d8ec4d15 | |||
| 2990b3efd5 | |||
| c653e400db | |||
| e884f8c165 | |||
| 3bcccf88d5 | |||
| 9e8c46b8de | |||
| fba4bfb2d5 | |||
| 79432be784 | |||
| 56980e51da | |||
| 37253f2621 | |||
| da380d6fc4 | |||
| bf183b2c2c | |||
| a07d199a48 | |||
| fa0e4c1294 | |||
| 60fe3c9ec6 | |||
| a6ce417a35 | |||
| 6523aefdcc | |||
| 31c4a9d770 | |||
| 9fa6427a18 | |||
| 6d5bd8bead | |||
| 98ad912509 | |||
| bd6ead32f8 | |||
| 3558848da8 | |||
| 720f2c7c61 | |||
| e6dfc22b8a | |||
| 1470cab842 | |||
| a31b992d2b | |||
| 5faa560f82 | |||
| 6c2b93d519 | |||
| 2957da538d | |||
| 089eccb245 | |||
| cbd4aa5392 | |||
| 1d333fedaa | |||
| 82d63306c4 | |||
| 416051f17b | |||
| f6e2d74d10 | |||
| aa2562fe7c | |||
| c17d17a9b4 | |||
| 404c9e40ee | |||
| 34788bfced | |||
| cffbd2d276 | |||
| 9250e4d3df | |||
| 60b9ef1f5d | |||
| f64584b92a | |||
| 9eb12889f4 | |||
| 6f6a3f2ccf | |||
| a574ebe5ec | |||
| 9be0272c13 | |||
| 5923470e35 | |||
| 934809397f | |||
| b6aa549eb8 | |||
| 168532ae8d | |||
| d7a5767aa3 | |||
| 15bf57e4e1 | |||
| 83cd2473f2 | |||
| 510d1f2518 | |||
| 7b55068b04 | |||
| b9aa94ee0d | |||
| 96d8971d87 | |||
| 98c8fde098 | |||
| 1ac84b09a1 | |||
| 21d8562923 | |||
| c8360f4fff | |||
| fe7e4e88c6 | |||
| c06528d702 | |||
| ea60db9f53 | |||
| dff4c6b666 | |||
| 0e9b357a5d | |||
| 1eb1bffd89 | |||
| e8653ac47e | |||
| f0683c9102 | |||
| ca4657fd31 | |||
| d94414b0f4 | |||
| 80fead1de5 | |||
| 069c6cac3f | |||
| 1a8ea6113a | |||
| 76e6624dbb | |||
| 09a52b7ee6 | |||
| 7b36174c17 | |||
| a82c902f99 | |||
| 14ae2e0a8d | |||
| 2ab6cdb98e | |||
| 21c1abe15a | |||
| 4c41045133 | |||
| 0ed1300bd6 | |||
| dd63665b58 | |||
| 9b22583397 | |||
| 63f2480951 | |||
| edbb3e3b32 | |||
| ecf324e213 | |||
| 7268c80d64 | |||
| fd453be831 | |||
| 2a232f2397 | |||
| c4cb7170ee | |||
| b6bbe29c8f | |||
| 5d0db4c63a | |||
| 05a3bdad58 | |||
| 0ef02e6737 | |||
| 047586afc6 | |||
| 5acdcfb57c | |||
| 6e04b1c429 | |||
| 828fe2ce56 | |||
| 18da0b3418 | |||
| 334a8c46e8 | |||
| e0fb081cbd | |||
| bca28e94ec | |||
| 875415daa9 | |||
| 0cf3ae93c0 | |||
| c4172e3914 | |||
| 8e08ae47fb | |||
| d9343ae997 | |||
| 4d30b88927 | |||
| 7389ffba6d | |||
| a6301ab58a | |||
| 996c854071 | |||
| a9c1811027 | |||
| 32b1537aa3 | |||
| 2d0c0a7a83 | |||
| 400816ebf7 | |||
| ef417017f0 | |||
| 7c1c9d22b4 | |||
| 5803fcc158 | |||
| f4f2b8f5ad | |||
| dd4f8e9747 | |||
| f06f33872c | |||
| 9e13416312 | |||
| 5ab7ec5b16 | |||
| b922398a96 | |||
| 57f5976ac1 | |||
| 7fbc081119 | |||
| 1dfe0cdd1d | |||
| 97c66c9c73 | |||
| 085972bb2c | |||
| 39bed567b3 | |||
| 2e0fd34e59 | |||
| 9959a069fc | |||
| c77098a91c | |||
| 89cceb910a | |||
| f36c5de746 | |||
| ca10248740 | |||
| aa859c2187 | |||
| 8591655334 | |||
| 70f5d7a1c9 | |||
| 1acd5acd70 | |||
| dbaf4d24f6 | |||
| 9c1c9693f2 | |||
| 776c3f4e90 | |||
| a55568b0c4 | |||
| b08ec005b2 | |||
| 91758656f6 | |||
| 4762d2f2d1 | |||
| 67bc35e882 | |||
| 4e370e6ed8 | |||
| 0b30c3dc5a | |||
| 9e42cb1595 | |||
| 4379917c7d | |||
| dc8e895d72 | |||
| a2461bdf6b | |||
| 5cc9ddd40b | |||
| acef96bde7 | |||
| e0c028329b | |||
| 740411f207 | |||
| fa50c8d7d3 | |||
| 2d878de84d | |||
| 58f768cb4f | |||
| 260e28c0af | |||
| a14ea5b2b5 | |||
| 63e4a2341f | |||
| 6416a6f39c | |||
| 87956676f5 | |||
| 913eac13b1 | |||
| 3b7fd4711f | |||
| fef5172278 | |||
| a39921aa6a | |||
| 9408eb9580 | |||
| ef2f204c58 | |||
| 30765475c6 | |||
| 2bd0fd932a | |||
| 11577db6a8 | |||
| ede25a88f8 | |||
| aa5cb7f6a9 | |||
| d730c5b334 | |||
| 34ca4e92eb | |||
| 7442f4bab6 | |||
| 4f303603e7 | |||
| a0fbc56317 | |||
| d90fc18bef | |||
| 00192bb349 | |||
| edd9d5e50b | |||
| fea8c63ab3 | |||
| 6f71d1f2a9 | |||
| ca4b0527e8 | |||
| adf6c85fd3 | |||
| 939878df50 | |||
| 5fafb17d81 | |||
| 3a229397e4 | |||
| db0bcbc4f4 | |||
| 0d6f5cec37 | |||
| 17ec3e72a6 | |||
| 30d922b059 | |||
| 3c1c10a178 | |||
| 9f93042163 | |||
| a64ea342df | |||
| fa284f9a5a | |||
| 78b6948ff2 | |||
| a6c5c203ab | |||
| 70b8b3c7dd | |||
| 6ee4f51670 | |||
| e8da2ef65e | |||
| 6c0fa24e5d | |||
| 27d0660546 | |||
| 0eba4b46b5 | |||
| 6b6f1a5283 | |||
| 3bed5fad77 | |||
| 301e1d2ff3 | |||
| 49e140f4db | |||
| 95b94a0f56 | |||
| ee3b3fe6a3 | |||
| eea3a1ecd3 | |||
| 31f4590f4b | |||
| 7909a9f5a5 | |||
| c26cb470a2 | |||
| 3d5e180dec | |||
| 44baf56d62 | |||
| 11a4dc8ee3 | |||
| 19e2f7b7bf | |||
| 817db70bde | |||
| ef27528ace | |||
| a239fcfa3a | |||
| a77976533c | |||
| 06b2a9a3e7 | |||
| 7a7fddd1df | |||
| 140bb3ebfc | |||
| 6fab181c0d | |||
| 17271f74c7 | |||
| 39044b5bb5 | |||
| 94277fe41c | |||
| b5a619e975 | |||
| 7c5247f5fb | |||
| 9ac6d45bd6 | |||
| 4cc84002db | |||
| c1d3c3f926 | |||
| 85b2da2e2a | |||
| 968aa2f688 | |||
| 0784b0ac7f | |||
| 44cfa3484f | |||
| 54fbdabc38 | |||
| 81398d21ed | |||
| 8c46582667 | |||
| f29f7351e9 | |||
| fc7c0ca3b0 | |||
| 8158239d76 | |||
| f3108493fb | |||
| 7694a89d38 | |||
| 75e8e5e06f | |||
| 59b8748c2c | |||
| 2231f02f61 | |||
| f9eadc3e98 | |||
| d6057a7ec6 | |||
| 532d307a75 | |||
| 6ae8f5a62d | |||
| a5c6487bc1 | |||
| 6a808235fe | |||
| 61d7cdfe92 | |||
| 45815fd26e | |||
| c7a2d6970b | |||
| 6912ef1bc1 | |||
| 08fae7360a | |||
| 6772cb0f3b | |||
| ce35de47e4 | |||
| 213864a50c | |||
| 4629754f7c | |||
| 0061e63db0 | |||
| 5fa488f163 | |||
| 71ef27fef9 | |||
| 6ea000b648 | |||
| 05b58bdbb1 | |||
| 4c4112fbc7 | |||
| feeb342c00 | |||
| c4dbc88a12 | |||
| f4b4cd0433 | |||
| 4e5b8f2c61 | |||
| 0be6ddc7e2 | |||
| b1bdd95f19 | |||
| 063c0548b0 | |||
| 41f6a684e1 | |||
| 289908e0b8 | |||
| 5a28a7e0f5 | |||
| f8e697dbbb | |||
| 7fc3030c63 | |||
| edd802e1ff | |||
| de13584be2 | |||
| 8806283cf7 | |||
| ec5934705c | |||
| fa33476438 | |||
| dfac45942c | |||
| 1b4bccbc94 | |||
| 062ae56f25 | |||
| 6eeb12a986 | |||
| a65553293f | |||
| 8f28312705 | |||
| 3a4f0d85a6 | |||
| c604e359c7 | |||
| 66c648e5e0 | |||
| e83af93394 | |||
| dd4c02a1b9 | |||
| bc2151f529 | |||
| 252a864308 | |||
| 9513cf1ac6 | |||
| c35d533ce5 | |||
| 464e733b88 | |||
| 519dcc2b76 | |||
| 1818ceb4f2 | |||
| e95addb1f4 | |||
| d343e47a86 | |||
| 1d21740afb | |||
| 9ae3879956 | |||
| 6b83159b06 | |||
| d72a01a714 | |||
| 2ed6785b4a | |||
| f7e40b8572 | |||
| a71cbd4087 | |||
| 2ebb94fa72 | |||
| 96a0125599 | |||
| 626d0736f4 | |||
| e26f63b448 | |||
| b9076b0d5b | |||
| c43b8ef387 | |||
| b9f511954a | |||
| 4936713a5e | |||
| 0bd38ba1f6 | |||
| 49eaeccd84 | |||
| 840af6825a | |||
| 3ccaf47566 | |||
| 8cc5cd1494 | |||
| 6b634d5e46 | |||
| 903ba1c5d8 | |||
| b42c7f9161 | |||
| 654b24e514 | |||
| fc4fdd588e | |||
| 4844dd4d96 | |||
| 609af3a651 | |||
| 20e4548999 | |||
| 7490ac2cfd | |||
| 10afc58d7d | |||
| f5b18482f6 | |||
| 97d7d8301a | |||
| 3a3d415295 | |||
| 33003d1bc1 | |||
| 59d9821bd9 | |||
| f530c80216 | |||
| 94b27aa64e | |||
| c92860e142 | |||
| 4cf240ca05 | |||
| ebad8e8990 | |||
| 2475c69f00 | |||
| c9dcb7242f | |||
| 9d976b04bc | |||
| 6f1e774a42 | |||
| b94dde2817 | |||
| 92cd50d3f0 | |||
| 1ef1407f02 | |||
| edbad0709e | |||
| bfc7d74967 | |||
| 9d91c46cb4 | |||
| 17b3c2ed4c | |||
| 8d637f5139 | |||
| f48a873954 | |||
| 4930a9c1a4 | |||
| 195dbd658d | |||
| 0cd681e6b7 | |||
| 3211e60018 | |||
| 775bd66382 | |||
| a13eed9894 | |||
| e9c598a426 | |||
| 65787381c1 | |||
| dd7b29e681 | |||
| 577be4a8ae | |||
| ac126a8cd7 | |||
| d53725fe14 | |||
| b8bebf4511 | |||
| 2f32b8fb3d | |||
| 1fd1203d8b | |||
| ccb7c3513b | |||
| bf215377de | |||
| d317d3d8b3 | |||
| 77829af9de | |||
| 97846d8390 | |||
| 079b887313 | |||
| 6aa708a455 | |||
| 8f38b06c60 | |||
| 993fc577b2 | |||
| 824491f410 | |||
| c1149dbdee | |||
| 7e625f6ee7 | |||
| 2dfa3a5df7 | |||
| 1064622ff7 | |||
| f5b8609160 | |||
| 7f48cf8405 | |||
| 7e08bccc9a | |||
| 9fd9130891 | |||
| 9f1f194d18 | |||
| 0fe47a2f1b | |||
| 31989c93ac | |||
| 7b0ba76915 | |||
| bd8ca64a30 | |||
| 32fda798f3 | |||
| 12895de9ac | |||
| 0216ade2f9 | |||
| 14e6cdb1a0 | |||
| 4daf098a3a | |||
| 914c99a15e | |||
| d31c63c0ae | |||
| c783840eab | |||
| d3eca21e40 | |||
| df3690c32a | |||
| 999d1a022d | |||
| 1f2f3cb315 | |||
| 8de70ce73d | |||
| 3820f27c7c | |||
| 7309cfdbb2 | |||
| ea19f3e77f | |||
| ae2fc5830e | |||
| 3946f357e8 | |||
| b9b2b90f0c | |||
| a6a7395be7 | |||
| 16b50a2f57 | |||
| e7f54d25d6 | |||
| 033b46def8 | |||
| 1f571f48e5 | |||
| ec178825ec | |||
| 6158a49618 | |||
| 674aeffee4 | |||
| 546759bdef | |||
| fa903fb4df | |||
| 5164d1d866 | |||
| b6e33a1e64 | |||
| f5248a8d9d | |||
| 74ed779616 | |||
| 9f24188ec8 | |||
| 5551404be4 | |||
| 9fd35776fd | |||
| 3385fcecb0 | |||
| 804b96a985 | |||
| 9cc20954a3 | |||
| 91ff3e5bca | |||
| 6d032e6f1a | |||
| 16abfd56e8 | |||
| d1d5f61230 | |||
| 5b7e1cd6f6 | |||
| cbfe3571ab | |||
| b04bc21ec6 | |||
| 2945feecde | |||
| 36cac87387 | |||
| a7e38dc063 | |||
| 0aaab84938 | |||
| 3a1492fc99 | |||
| ca818fb857 | |||
| 13e5cb8cc4 | |||
| 7ae54ae3b4 | |||
| b2beb9512f | |||
| 96ad6d2084 | |||
| 86f1574f78 | |||
| 6565a49e34 | |||
| ce85b73eca | |||
| fc65aee307 | |||
| ede135a08f | |||
| c18fc2da9f | |||
| c8a3937ea0 | |||
| f9a6777e3a | |||
| 83eabe2140 | |||
| c4067372dd | |||
| b4e976364a | |||
| 642afa4f88 | |||
| f65fa0e4a6 | |||
| 65b9dbe13f | |||
| cb52f6986a | |||
| 94ba182691 | |||
| 5b7c83d871 | |||
| 4059542afb | |||
| 9da1347c21 | |||
| a8c10c58b4 | |||
| 6d9b2ec5b4 | |||
| d749f5c704 | |||
| 83f553cd89 | |||
| 730f9ee0b3 | |||
| 51e50d3e31 | |||
| 1b2cc739a9 | |||
| 7bdd8094d6 | |||
| 323fc7a6a9 | |||
| 92363da58e | |||
| 0ea64182f1 | |||
| 8478b887a5 | |||
| 86448ee044 | |||
| 56a52a1d06 | |||
| 858c5f8fd8 | |||
| f92b4c7849 | |||
| 9fdebebd28 | |||
| 2b18f43b65 | |||
| 53e1512978 | |||
| b5f9c95ce7 | |||
| 231f91304a | |||
| d7b3f3a0e7 | |||
| bb81d4869f | |||
| b633a42bc2 | |||
| 6bd05ef5d7 | |||
| 251a3a690e | |||
| f22b014db0 | |||
| a6edcf4f18 | |||
| 2a598aa061 | |||
| 910ab7b3d4 | |||
| 2b27300387 | |||
| 0db1ebb4e5 | |||
| 0013033f9a | |||
| f00123742c | |||
| 509e8b512f | |||
| 2562df5387 | |||
| 0ab356aa4e | |||
| b3c51a8b44 | |||
| fb3aeccd36 | |||
| 9495e3d10c | |||
| 0f711b8719 | |||
| 2a6130b7e1 | |||
| 34384cc2f1 | |||
| aeafed0218 | |||
| 3a4bbfcfae | |||
| 01c84b32a6 | |||
| 240fc65d4d | |||
| c9d3b95b0d | |||
| 9f596d6f34 | |||
| 2a684830f9 | |||
| c4ba3f1c83 | |||
| 724c776dbe | |||
| ad8048b615 | |||
| 781395ada1 | |||
| 9635ea8706 | |||
| 413188bab2 | |||
| 5fa54fd2cc | |||
| b79e1b6b5c | |||
| 8dff0f7ef5 | |||
| f85f3dce12 | |||
| 5edb1e8a28 | |||
| f70939ab4f | |||
| 08e271720a | |||
| c3ebc49ad2 | |||
| 9cccb20f49 | |||
| 6985d72fda | |||
| 0056cdffa7 | |||
| a229f9825b | |||
| a6ff48a5de | |||
| 6648dc9ed1 | |||
| bbbf7168f1 | |||
| c15ed44f82 | |||
| 58466a6f3b | |||
| be76c860a5 | |||
| 4533c02cc7 | |||
| 01d12770fa | |||
| 82b559c32b | |||
| 474d79dcf1 | |||
| 61db6c1234 | |||
| e641d714a0 | |||
| f305726015 | |||
| 57b09b2ffb | |||
| e86fde3cb1 | |||
| 99944f3b02 | |||
| a9295d33ab | |||
| 4acae2af44 | |||
| 036429bf2f | |||
| d0920804c3 | |||
| 97d3d4ff6f | |||
| 6ea0910213 | |||
| b8d33165c9 | |||
| 6b3ee447f0 | |||
| 9b1cc6e94f | |||
| 0ffb718a91 | |||
| a7dd686859 | |||
| 87a6d695d6 | |||
| a89633e3ed | |||
| 59a0bbb385 | |||
| a3fa647bfd | |||
| 1a4e2196bd | |||
| d8f9daac95 | |||
| f992e36f9a | |||
| 11dd971e13 | |||
| 5ed4bac16f | |||
| 04ceb5d20c | |||
| 0b67de47d6 | |||
| 15c4077806 | |||
| 37a3abdaaa | |||
| ac709b8afb | |||
| d80049bfd2 | |||
| 239f3c0418 | |||
| 5b11a842a8 | |||
| 626b4740b1 | |||
| 437d5e28cd | |||
| 6c7fc4212a | |||
| 8589af1458 | |||
| bf5ce04b24 | |||
| 8057af9e09 | |||
| d8bc2030e6 | |||
| 5eaef091e2 | |||
| 84fd40de77 | |||
| 1d7f99cbdf | |||
| 3d4a3730b0 | |||
| 1f31d573e4 | |||
| 237962182b | |||
| e84406a895 | |||
| 1a0614b0d7 | |||
| 876820789e | |||
| 4e21a4d46a | |||
| 6ac0fb80a6 | |||
| 098699103d | |||
| d6bcc25b69 | |||
| a2c50d3666 | |||
| f06efffe22 | |||
| e42f7008fc | |||
| 26c9d17040 | |||
| 2be87169ef | |||
| 732c745bb3 | |||
| c035fc337f | |||
| cbfbff7240 | |||
| 99e6eba20f | |||
| 7d11ee605c | |||
| 4671207c61 | |||
| aa029fe8d9 | |||
| ef539696b9 | |||
| 26fe9b0212 | |||
| fd6e2ed5de | |||
| 7bd1039a49 | |||
| 76a230372f | |||
| 86b613fb15 | |||
| aacf524a47 | |||
| 8ad3a1f41f | |||
| d09ac8943a | |||
| 8e63c94393 | |||
| 0b16ed7c85 | |||
| 89d824216a | |||
| 3f23a0b3b0 | |||
| 7819fec0a1 | |||
| 96ed948590 | |||
| 3bd01d3a02 | |||
| c4b492fb64 | |||
| e1bdaa981a | |||
| 95e8668b77 | |||
| 90adf214d7 | |||
| ff92eb9112 | |||
| 5f08c77d8b | |||
| dc236d6830 | |||
| 8b066b2456 | |||
| 218c1599b1 | |||
| 9b2eecb3df | |||
| e0a242e9a1 | |||
| 48946fe46f | |||
| 0b0e661b62 | |||
| 10c3acf37f | |||
| e377f5a329 | |||
| cd4403be0c | |||
| f661d34049 | |||
| af1d4d6e72 | |||
| f6c7a0c9b8 | |||
| c2bfb221f5 | |||
| d59fced21b | |||
| 7c42d4ba75 | |||
| 14ce6d8ed8 | |||
| 2630e9ced1 | |||
| 482c687462 | |||
| de8c1165c2 | |||
| d51b98a8d2 | |||
| 6861ff47bc | |||
| 0ba3b5c0bd | |||
| fcdfcfce19 | |||
| d49ecd9009 | |||
| 72a5030f3d | |||
| 197a72adde | |||
| f043e92650 | |||
| e3f3e715e2 | |||
| e8f2fb58d9 | |||
| 8d2430e54d | |||
| dc1b3cc563 | |||
| d52695b077 | |||
| 6adbdb1e0e | |||
| 7d4f9545a7 | |||
| 4fc60601d3 | |||
| 4f4c3bb03b | |||
| 8434cb8951 | |||
| 99ebb5af3b | |||
| 45274868c3 | |||
| 3ef9566817 | |||
| 3299d6a204 | |||
| e27f5796d6 | |||
| ba4363d678 | |||
| eb2b3572bb | |||
| 1a9e75267b | |||
| dd0fb744fe | |||
| 70a66cd559 | |||
| 832a586b8d | |||
| 117ade0700 | |||
| 25f77e32ee | |||
| 704ee9f851 | |||
| 190932935c | |||
| a10d180d81 | |||
| c704de8442 | |||
| f63b9806d2 | |||
| 9409f53a9b | |||
| 9faf292218 | |||
| 92f5f86193 | |||
| 1e711fa5a5 | |||
| 51b346753a | |||
| b63aa0520a | |||
| 04e106e06a | |||
| 5d42a6de6e | |||
| bb3e0fa03f | |||
| e7e936f480 | |||
| 026bb8ed6f | |||
| cbc3800797 | |||
| 4236e870d7 | |||
| b725ae5ae0 | |||
| 3afa2736b7 | |||
| 8dd1b61aa2 | |||
| f5a1d8f2b5 | |||
| d3f2f94800 | |||
| f141e2868d | |||
| 400d6014d0 | |||
| ab6947b2c7 | |||
| 52bbdefec4 | |||
| 8ee67e41f4 | |||
| 0af2b38225 | |||
| 97f7a558fa | |||
| ab7c75860e | |||
| 680d723c77 | |||
| 8eda24f2d1 | |||
| 3dac1c34fa | |||
| 7fddac2c36 | |||
| a55975bd5a | |||
| 2c91c21021 | |||
| 3a1bee19a9 | |||
| 872e97dba6 | |||
| 705984f828 | |||
| aa620e8ea1 | |||
| 1addee32b5 | |||
| 92402603b9 | |||
| 4438b3e7af | |||
| 602a651613 | |||
| d0cee15317 | |||
| df5626cc1f | |||
| 2dcc986c4c | |||
| 550df8711f | |||
| 1e321cd48c | |||
| 020856d91a | |||
| 1bd0e09ae1 | |||
| dbaf8568d6 | |||
| 802a776330 | |||
| 9170c875e1 | |||
| 5002b71670 | |||
| f229084baa | |||
| f09b6a4c90 | |||
| 65a2125dba | |||
| 9e698b720f | |||
| 5157619eb7 | |||
| 90593fe6ae | |||
| 16b4178b8a | |||
| 8f2b848698 | |||
| 9655b61c11 | |||
| 9b9a4fcf22 | |||
| a736d0e83f | |||
| 7ba339e6bd | |||
| baf5b9edc3 | |||
| a615402d7c | |||
| c9bec10a8e | |||
| df80e8752b | |||
| 2df0532b17 | |||
| 62dc99dbef | |||
| b925ad5927 | |||
| 090579d6b5 | |||
| 29a3cb25d3 | |||
| 7ff690500a | |||
| d0b913dad1 | |||
| d659e679fd | |||
| ae97c1111a | |||
| f38d7b02f3 | |||
| 5e706bab56 | |||
| b362ce9a22 | |||
| 9961ad8e48 | |||
| b54b5aabac | |||
| d214c45fe5 | |||
| 3f638f92e2 | |||
| b0d668d254 | |||
| 488282409f | |||
| d3c2191cf7 | |||
| dd13cf637e | |||
| 0804be5d81 | |||
| 3c189d2cf6 | |||
| c83592629d | |||
| 2e3706e447 | |||
| 1524852ffc | |||
| 957e6f7f08 | |||
| 1b3ee7153c | |||
| bda5e8cc66 | |||
| 6d6f8f8da9 | |||
| dca6c451da | |||
| a6c8dcdd21 | |||
| b98a4c6d69 | |||
| 4be385d648 | |||
| 6bbecbe895 | |||
| b21993a1c4 | |||
| dd69fcba07 | |||
| 7909872877 | |||
| 0a920b5439 | |||
| 921ee82c97 | |||
| b7893082ce | |||
| 6bfe9b6656 | |||
| da703edbf4 | |||
| 796331fea6 | |||
| 820095ddac | |||
| d57fbf48f0 | |||
| 53c842e9ba | |||
| 1e375ba8de | |||
| 5cbb266ef5 | |||
| 0730e01b24 | |||
| dfc63dcb60 | |||
| 8093062e3b | |||
| 9524739dfc | |||
| 054e018e23 | |||
| 3d81ad46d2 | |||
| ed4d21045b | |||
| 93955a0fd8 | |||
| fc0002ab67 | |||
| 04fe23a3c8 | |||
| 1707e004ec | |||
| 5169dc07c7 | |||
| 18fb6a4be4 | |||
| 8dba0e332c | |||
| 3951b93d0a | |||
| 10bac2445b | |||
| 06e364b9bd | |||
| ce90509a07 | |||
| a0d0e93475 | |||
| 2ce1f4ba9f | |||
| a985449c31 | |||
| 6abc93ad84 | |||
| a9bc07dbff | |||
| da283d5a7f | |||
| 1181d7cf90 | |||
| 2a2d3273ea | |||
| 775e6caf31 | |||
| 4468ddf8f8 | |||
| bf0c6f5a30 | |||
| d1e670335f | |||
| c6c6c56ba9 | |||
| d4e95282c2 | |||
| 5a02ebe2c8 | |||
| dae817e5ab | |||
| 28502ba53b | |||
| 8dc74a004c | |||
| c584456a21 | |||
| c74d8405ec | |||
| e0a370f8f1 | |||
| 4cf70f360b | |||
| a83d9f59f6 | |||
| 5d14189a18 | |||
| fb686bdebd | |||
| 19b9cb7524 | |||
| f92334a769 | |||
| 4ee936ab8d | |||
| 0c21939600 | |||
| f6f2ab2f25 | |||
| 9d163fede8 | |||
| bb026c99a9 | |||
| 8d2ad0da0e | |||
| db7a3ffd2a | |||
| c213d72ec6 | |||
| 01b1385672 | |||
| e92fdb4a99 | |||
| 2817bab494 | |||
| 7d93dd6024 | |||
| e1da631d26 | |||
| 6151fdb324 | |||
| 2fe0eaf2ad | |||
| 85f2f4aa88 | |||
| 3f63e12198 | |||
| 10cde925ca | |||
| 56240240f6 | |||
| f83e1b8b0a | |||
| a27c68f526 | |||
| 8ec5987ae1 | |||
| 408fa027af | |||
| 5e903ed2ff | |||
| ce20604e3c | |||
| 92e1c71dd6 | |||
| 0190f90979 | |||
| 3d74c2f6c0 | |||
| 1596bca92d | |||
| 470508adbc | |||
| 8f42958ba3 | |||
| 4d7270cec9 | |||
| b13423d7f7 | |||
| 703404a54d | |||
| 15f5c85379 | |||
| e197af766d | |||
| 10fe5e97b3 | |||
| 5073fcd39e | |||
| a72ac6f841 | |||
| 8a67775149 | |||
| 85e331d5e2 | |||
| 81b00c7a3e | |||
| 49ecd73406 | |||
| 2180f4a475 | |||
| 1f0c1943da | |||
| 0b7711684b | |||
| 9821b2be25 | |||
| 49b42f585c | |||
| 9d5692ae68 | |||
| bfda101686 | |||
| 6d67567453 | |||
| 35711a400c | |||
| 66da96284a | |||
| 7d9600b740 | |||
| 0c09cb9c12 | |||
| 09e1cf0737 | |||
| 992858b687 | |||
| b555b0083a | |||
| 25feb507a4 | |||
| 0b299cb8b4 | |||
| fbe2366af3 | |||
| 391e2a5120 | |||
| 0103cedad7 | |||
| d56807095a | |||
| 5dc82c2720 | |||
| 5f1b397a05 | |||
| 6ed6a91a64 | |||
| 0f399a7294 | |||
| 4bcb667e97 | |||
| 7e6454b2f7 | |||
| 357e72654d | |||
| e9f3ebba13 | |||
| 3a30f9fd71 | |||
| 7a4ec48389 | |||
| a101e0d7ba | |||
| 64f5c9ab58 | |||
| 0ee5a07d01 | |||
| d658a8fca5 | |||
| abe8789890 | |||
| a4b60b78c8 | |||
| 66d1656367 | |||
| e3d1d1d85c | |||
| b6ea7b7418 | |||
| 7426f3b092 | |||
| 299c299dff | |||
| e398c074db | |||
| eadb66c9ef | |||
| 9d7e1c17cc | |||
| 775b544326 | |||
| da26e14959 | |||
| 222e5c8ae9 | |||
| 772dc47f55 | |||
| 0777351482 |
@@ -0,0 +1 @@
|
||||
*.odin linguist-language=Odin
|
||||
@@ -12,7 +12,7 @@ assignees: ''
|
||||
Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
|
||||
|
||||
* Operating System:
|
||||
* Odin version/Commit:
|
||||
* Please paste `odin version` output:
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# PLEASE POST THIS IN THE DISCUSSION TAB UNDER "PROPOSALS" OR "IDEAS/REQUESTS"
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
|
||||
+27
-25
@@ -2,47 +2,47 @@ name: CI
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_unix:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest]
|
||||
|
||||
build_linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: (macOS) Download LLVM and setup PATH
|
||||
if: startsWith(matrix.os, 'macOS')
|
||||
run: |
|
||||
brew install llvm
|
||||
echo ::add-path::/usr/local/opt/llvm/bin
|
||||
echo ::set-env name=CPATH::`xcrun --show-sdk-path`/usr/include
|
||||
- name: (Linux) Download LLVM
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
sudo apt-get install llvm
|
||||
- name: Download LLVM
|
||||
run: sudo apt-get install llvm-11 clang-11 llvm
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
build_macOS:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@11
|
||||
echo "/usr/local/opt/llvm@11/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install cURL
|
||||
run: choco install curl
|
||||
- name: Download and unpack LLVM bins
|
||||
shell: cmd
|
||||
run: |
|
||||
cd bin
|
||||
curl -sL https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip --output llvm-binaries.zip
|
||||
7z x llvm-binaries.zip > nul
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
./build_ci.bat
|
||||
./build.bat 1
|
||||
- name: Odin run
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -53,5 +53,7 @@ jobs:
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/demo/demo.odin -vet
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
name: Nightly
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 0 20 * * *
|
||||
|
||||
jobs:
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
./build.bat 1 1
|
||||
- name: Odin run
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo/demo.odin
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
rm bin/llvm/windows/LLVM-C.lib
|
||||
mkdir dist
|
||||
cp odin.exe dist
|
||||
cp LLVM-C.dll dist
|
||||
cp -r shared dist
|
||||
cp -r core dist
|
||||
cp -r bin dist
|
||||
cp -r examples dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: windows_artifacts
|
||||
path: dist
|
||||
build_ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: (Linux) Download LLVM
|
||||
run: sudo apt-get install llvm-11 clang-11 llvm
|
||||
- name: build odin
|
||||
run: make nightly
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
mkdir dist
|
||||
cp odin dist
|
||||
cp -r shared dist
|
||||
cp -r core dist
|
||||
cp -r examples dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: ubuntu_artifacts
|
||||
path: dist
|
||||
build_macos:
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@11
|
||||
echo "/usr/local/opt/llvm@11/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
- name: build odin
|
||||
run: make nightly
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
mkdir dist
|
||||
cp odin dist
|
||||
cp -r shared dist
|
||||
cp -r core dist
|
||||
cp -r examples dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: macos_artifacts
|
||||
path: dist
|
||||
upload_b2:
|
||||
runs-on: [ubuntu-latest]
|
||||
needs: [build_windows, build_macos, build_ubuntu]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install B2 CLI
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install --upgrade b2
|
||||
|
||||
- name: Display Python version
|
||||
run: python -c "import sys; print(sys.version)"
|
||||
|
||||
- name: Download Windows artifacts
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: windows_artifacts
|
||||
|
||||
- name: Download Ubuntu artifacts
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: ubuntu_artifacts
|
||||
|
||||
- name: Download macOS artifacts
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: macos_artifacts
|
||||
|
||||
- name: Create archives and upload
|
||||
shell: bash
|
||||
env:
|
||||
APPID: ${{ secrets.B2_APPID }}
|
||||
APPKEY: ${{ secrets.B2_APPKEY }}
|
||||
BUCKET: ${{ secrets.B2_BUCKET }}
|
||||
DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
|
||||
run: |
|
||||
b2 authorize-account "$APPID" "$APPKEY"
|
||||
|
||||
chmod +x ./ci/upload_create_nightly.sh
|
||||
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
|
||||
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/
|
||||
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/
|
||||
|
||||
python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
|
||||
|
||||
python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
|
||||
b2 upload-file "$BUCKET" nightly.json nightly.json
|
||||
|
||||
b2 clear-account
|
||||
@@ -21,6 +21,8 @@ bld/
|
||||
![Cc]ore/[Ll]og/
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Visual Studio Code options directory
|
||||
.vscode/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
demo
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016-2017 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2021 Ginger Bill. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
BIN
Binary file not shown.
@@ -1,12 +1,38 @@
|
||||
GIT_SHA=$(shell git rev-parse --short HEAD)
|
||||
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined
|
||||
LDFLAGS=-pthread -ldl -lm -lstdc++
|
||||
CFLAGS=-std=c++11
|
||||
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)
|
||||
LDFLAGS:=$(LDFLAGS) -liconv
|
||||
LLVM_CONFIG=llvm-config
|
||||
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
||||
LLVM_CONFIG=llvm-config
|
||||
else
|
||||
$(error "Requirement: llvm-config must be version 11")
|
||||
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 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
|
||||
@@ -20,5 +46,8 @@ debug:
|
||||
release:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
|
||||
|
||||
nightly:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ The proposal process is the process for reviewing a proposal and reaching a deci
|
||||
* Accept proposal
|
||||
* Decline proposal
|
||||
|
||||
After the proposal is accepted or declined, implementation of the proprosal proceeds in the same way as any other contribution to the project.
|
||||
After the proposal is accepted or declined, implementation of the proposal proceeds in the same way as any other contribution to the project.
|
||||
|
||||
## Design Documents
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
# The Odin Programming Language
|
||||
|
||||
The Odin programming language is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of replacing C with the following goals:
|
||||
The Odin programming language is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of creating an alternative to C with the following goals:
|
||||
* simplicity
|
||||
* high performance
|
||||
* built for modern systems
|
||||
@@ -29,7 +29,7 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
|
||||
|
||||
Website: [https://odin-lang.org/](https://odin-lang.org/)
|
||||
|
||||
```go
|
||||
```odin
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
@@ -60,7 +60,7 @@ main :: proc() {
|
||||
|
||||
#### [Getting Started](https://odin-lang.org/docs/install)
|
||||
|
||||
Instructions for downloading and install the Odin compiler and libraries.
|
||||
Instructions for downloading and installing the Odin compiler and libraries.
|
||||
|
||||
### Learning Odin
|
||||
|
||||
@@ -94,13 +94,12 @@ The official blog of the Odin programming language, featuring announcements, new
|
||||
|
||||
## Setup
|
||||
|
||||
Odin only supports x86-64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
Odin only supports x86-64/amd64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
|
||||
In addition, the following platform-specific steps are necessary:
|
||||
|
||||
- Windows
|
||||
* Have Visual Studio installed (MSVC 2010 or later, for the linker)
|
||||
* Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied
|
||||
* Open a valid command prompt:
|
||||
* **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or
|
||||
* **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session
|
||||
@@ -126,27 +125,33 @@ Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#g
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin).
|
||||
|
||||
- Windows
|
||||
* x86-64
|
||||
* x86-64/amd64
|
||||
* MSVC 2010 installed (C++11 support)
|
||||
* [LLVM binaries](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) for `opt.exe`, `llc.exe`, and `lld-link.exe`
|
||||
* Requires MSVC's link.exe as the linker
|
||||
* run `vcvarsall.bat` to setup the path
|
||||
|
||||
- MacOS
|
||||
* x86-64
|
||||
* x86-64/amd64
|
||||
* LLVM explicitly installed (`brew install llvm`)
|
||||
* XCode installed (for the linker)
|
||||
|
||||
- GNU/Linux
|
||||
* x86-64
|
||||
* x86-64/amd64
|
||||
* Build tools (ld)
|
||||
* LLVM installed
|
||||
* Clang installed (temporary - this is Calling the linker for now)
|
||||
|
||||
- FreeBSD
|
||||
* x86-64/amd64
|
||||
* Build tools (ld)
|
||||
* LLVM installed
|
||||
* Clang installed (temporary - this is Calling the linker for now)
|
||||
|
||||
Other platforms may be supported but are experimental for the time being.
|
||||
|
||||
## Warnings
|
||||
|
||||
* This is still highly in development and the language's design is quite volatile.
|
||||
* Syntax is not fixed.
|
||||
* The Odin compiler is still in development.
|
||||
|
||||
## Demonstrations:
|
||||
* First Talk & Demo
|
||||
|
||||
+3
-4
@@ -2,13 +2,12 @@
|
||||
|
||||
## Setup
|
||||
|
||||
Odin only supports x86-64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
Odin currently supports x86-64 and ARM64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
|
||||
In addition, the following platform-specific steps are necessary:
|
||||
|
||||
- Windows
|
||||
* Have Visual Studio installed (MSVC 2010 or later, for the linker)
|
||||
* Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied
|
||||
* Open a valid command prompt:
|
||||
* **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or
|
||||
* **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session
|
||||
@@ -19,12 +18,12 @@ In addition, the following platform-specific steps are necessary:
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
- GNU/Linux
|
||||
* Have LLVM installed (opt/llc)
|
||||
* Have Clang installed (version X.X or later, for linking)
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
Then build the compiler by calling `build.bat` (Windows) or `make` (Linux/MacOS). This will automatically run the demo program if successful.
|
||||
|
||||
**Notes for Linux:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or add `/path/to/odin` to `$PATH`.
|
||||
**Notes for \*Nix Systems:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable, by default. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to either simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or `set ODIN_ROOT=/path/to/odin_root`.
|
||||
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin) for more information.
|
||||
|
||||
Binary file not shown.
@@ -1,5 +1,14 @@
|
||||
@echo off
|
||||
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
||||
set CURR_DATE_TIME=%%j
|
||||
)
|
||||
|
||||
set curr_year=%CURR_DATE_TIME:~0,4%
|
||||
set curr_month=%CURR_DATE_TIME:~4,2%
|
||||
|
||||
:: Make sure this is a decent name and not generic
|
||||
set exe_name=odin.exe
|
||||
|
||||
@@ -12,13 +21,27 @@ if "%1" == "1" (
|
||||
set release_mode=0
|
||||
)
|
||||
|
||||
:: Normal = 0, CI Nightly = 1
|
||||
if "%2" == "1" (
|
||||
set nightly=1
|
||||
) else (
|
||||
set nightly=0
|
||||
)
|
||||
|
||||
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
||||
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
||||
|
||||
for /f %%i in ('git rev-parse --short HEAD') do set GIT_SHA=%%i
|
||||
if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
|
||||
if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
||||
rem -DDISPLAY_TIMING
|
||||
) else ( rem Release
|
||||
set compiler_flags=%compiler_flags% -O2 -MT -Z7 -DNO_ARRAY_BOUNDS_CHECK
|
||||
set compiler_flags=%compiler_flags% -O2 -MT -Z7
|
||||
set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
|
||||
)
|
||||
|
||||
set compiler_warnings= ^
|
||||
@@ -30,7 +53,8 @@ set compiler_warnings= ^
|
||||
|
||||
set compiler_includes=
|
||||
set libs= ^
|
||||
kernel32.lib
|
||||
kernel32.lib ^
|
||||
bin\llvm\windows\LLVM-C.lib
|
||||
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||
|
||||
@@ -40,15 +64,16 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
set linker_flags=%linker_flags% -debug
|
||||
)
|
||||
|
||||
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
|
||||
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines%
|
||||
set linker_settings=%libs% %linker_flags%
|
||||
|
||||
del *.pdb > NUL 2> NUL
|
||||
del *.ilk > NUL 2> NUL
|
||||
|
||||
cl %compiler_settings% "src\main.cpp" ^
|
||||
/link %linker_settings% -OUT:%exe_name% ^
|
||||
&& odin run examples/demo/demo.odin
|
||||
cl %compiler_settings% "src\main.cpp" /link %linker_settings% -OUT:%exe_name%
|
||||
|
||||
if %errorlevel% neq 0 goto end_of_build
|
||||
if %release_mode% EQU 0 odin run examples/demo/demo.odin
|
||||
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
release_mode=$1
|
||||
|
||||
warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
|
||||
libraries="-pthread -ldl -lm -lstdc++"
|
||||
other_args=""
|
||||
compiler="clang"
|
||||
|
||||
if [ -z "$release_mode" ]; then release_mode="0"; fi
|
||||
|
||||
if [ "$release_mode" -eq "0" ]; then
|
||||
other_args="${other_args} -g"
|
||||
fi
|
||||
if [ "$release_mode" -eq "1" ]; then
|
||||
other_args="${other_args} -O3 -march=native"
|
||||
fi
|
||||
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
|
||||
# Set compiler to clang on MacOS
|
||||
# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
|
||||
compiler="clang"
|
||||
|
||||
other_args="${other_args} -liconv"
|
||||
fi
|
||||
|
||||
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin && ./odin run examples/demo/demo.odin
|
||||
@@ -1,24 +0,0 @@
|
||||
@echo off
|
||||
|
||||
set exe_name=odin.exe
|
||||
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -GS- -EHsc- -GR- -O2 -MT -Z7 -DNO_ARRAY_BOUNDS_CHECK
|
||||
set compiler_warnings= ^
|
||||
-W4 -WX ^
|
||||
-wd4100 -wd4101 -wd4127 -wd4189 ^
|
||||
-wd4201 -wd4204 ^
|
||||
-wd4456 -wd4457 -wd4480 ^
|
||||
-wd4512
|
||||
|
||||
set compiler_includes=
|
||||
set libs= ^
|
||||
kernel32.lib
|
||||
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console -debug
|
||||
|
||||
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
|
||||
set linker_settings=%libs% %linker_flags%
|
||||
|
||||
cl %compiler_settings% "src\main.cpp" ^
|
||||
/link %linker_settings% -OUT:%exe_name% ^
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
import datetime
|
||||
import urllib.parse
|
||||
import sys
|
||||
|
||||
def main():
|
||||
files_by_date = {}
|
||||
bucket = sys.argv[1]
|
||||
|
||||
files_lines = execute_cli(f"b2 ls --long {bucket} nightly").split("\n")
|
||||
for x in files_lines:
|
||||
parts = x.split(" ", 1)
|
||||
if parts[0]:
|
||||
json_str = execute_cli(f"b2 get-file-info {parts[0]}")
|
||||
data = json.loads(json_str)
|
||||
name = remove_prefix(data['fileName'], "nightly/")
|
||||
url = f"https://f001.backblazeb2.com/file/{bucket}/nightly/{urllib.parse.quote_plus(name)}"
|
||||
sha1 = data['contentSha1']
|
||||
size = int(data['contentLength'])
|
||||
ts = int(data['fileInfo']['src_last_modified_millis'])
|
||||
date = datetime.datetime.fromtimestamp(ts/1000).strftime('%Y-%m-%d')
|
||||
|
||||
if date not in files_by_date.keys():
|
||||
files_by_date[date] = []
|
||||
|
||||
files_by_date[date].append({
|
||||
'name': name,
|
||||
'url': url,
|
||||
'sha1': sha1,
|
||||
'sizeInBytes': size,
|
||||
})
|
||||
|
||||
now = datetime.datetime.utcnow().isoformat()
|
||||
|
||||
print(json.dumps({
|
||||
'last_updated' : now,
|
||||
'files': files_by_date
|
||||
}, sort_keys=True, indent=4))
|
||||
|
||||
def remove_prefix(text, prefix):
|
||||
return text[text.startswith(prefix) and len(prefix):]
|
||||
|
||||
def execute_cli(command):
|
||||
sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
|
||||
return sb.stdout.read().decode("utf-8");
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
import datetime
|
||||
import urllib.parse
|
||||
import sys
|
||||
|
||||
def main():
|
||||
files_by_date = {}
|
||||
bucket = sys.argv[1]
|
||||
days_to_keep = int(sys.argv[2])
|
||||
print(f"Looking for binaries to delete older than {days_to_keep} days")
|
||||
|
||||
files_lines = execute_cli(f"b2 ls --long --versions {bucket} nightly").split("\n")
|
||||
for x in files_lines:
|
||||
parts = [y for y in x.split(' ') if y]
|
||||
|
||||
if parts and parts[0]:
|
||||
date = datetime.datetime.strptime(parts[2], '%Y-%m-%d').replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
now = datetime.datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
delta = now - date
|
||||
|
||||
if delta.days > days_to_keep:
|
||||
print(f'Deleting {parts[5]}')
|
||||
execute_cli(f'b2 delete-file-version {parts[0]}')
|
||||
|
||||
|
||||
def execute_cli(command):
|
||||
sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
|
||||
return sb.stdout.read().decode("utf-8");
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
bucket=$1
|
||||
platform=$2
|
||||
artifact=$3
|
||||
|
||||
now=$(date +'%Y-%m-%d')
|
||||
filename="odin-$platform-nightly+$now.zip"
|
||||
|
||||
echo "Creating archive $filename from $artifact and uploading to $bucket"
|
||||
|
||||
7z a -bd "output/$filename" -r "$artifact"
|
||||
b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"
|
||||
@@ -0,0 +1,83 @@
|
||||
package bufio
|
||||
|
||||
import "core:io"
|
||||
|
||||
// Loadahead_Reader provides io lookahead.
|
||||
// This is useful for tokenizers/parsers.
|
||||
// Loadahead_Reader is similar to bufio.Reader, but unlike bufio.Reader, Loadahead_Reader's buffer size
|
||||
// will EXACTLY match the specified size, whereas bufio.Reader's buffer size may differ from the specified size.
|
||||
// This makes sure that the buffer will not be accidentally read beyond the expected size.
|
||||
Loadahead_Reader :: struct {
|
||||
r: io.Reader,
|
||||
buf: []byte,
|
||||
n: int,
|
||||
}
|
||||
|
||||
lookahead_reader_init :: proc(lr: ^Loadahead_Reader, r: io.Reader, buf: []byte) -> ^Loadahead_Reader {
|
||||
lr.r = r;
|
||||
lr.buf = buf;
|
||||
lr.n = 0;
|
||||
return lr;
|
||||
}
|
||||
|
||||
lookahead_reader_buffer :: proc(lr: ^Loadahead_Reader) -> []byte {
|
||||
return lr.buf[:lr.n];
|
||||
}
|
||||
|
||||
|
||||
// lookahead_reader_peek returns a slice of the Lookahead_Reader which holds n bytes
|
||||
// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest.
|
||||
// NOTE: The returned buffer is not a copy of the underlying buffer
|
||||
lookahead_reader_peek :: proc(lr: ^Loadahead_Reader, n: int) -> ([]byte, io.Error) {
|
||||
switch {
|
||||
case n < 0:
|
||||
return nil, .Negative_Read;
|
||||
case n > len(lr.buf):
|
||||
return nil, .Buffer_Full;
|
||||
}
|
||||
|
||||
n := n;
|
||||
err: io.Error;
|
||||
read_count: int;
|
||||
|
||||
if lr.n < n {
|
||||
read_count, err = io.read_at_least(lr.r, lr.buf[lr.n:], n-lr.n);
|
||||
if err == .Unexpected_EOF {
|
||||
err = .EOF;
|
||||
}
|
||||
}
|
||||
|
||||
lr.n += read_count;
|
||||
|
||||
if n > lr.n {
|
||||
n = lr.n;
|
||||
}
|
||||
return lr.buf[:n], err;
|
||||
}
|
||||
|
||||
// lookahead_reader_peek_all returns a slice of the Lookahead_Reader populating the full buffer
|
||||
// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest.
|
||||
// NOTE: The returned buffer is not a copy of the underlying buffer
|
||||
lookahead_reader_peek_all :: proc(lr: ^Loadahead_Reader) -> ([]byte, io.Error) {
|
||||
return lookahead_reader_peek(lr, len(lr.buf));
|
||||
}
|
||||
|
||||
|
||||
// lookahead_reader_consume drops the first n populated bytes from the Lookahead_Reader.
|
||||
lookahead_reader_consume :: proc(lr: ^Loadahead_Reader, n: int) -> io.Error {
|
||||
switch {
|
||||
case n == 0:
|
||||
return nil;
|
||||
case n < 0:
|
||||
return .Negative_Read;
|
||||
case lr.n < n:
|
||||
return .Short_Buffer;
|
||||
}
|
||||
copy(lr.buf, lr.buf[n:lr.n]);
|
||||
lr.n -= n;
|
||||
return nil;
|
||||
}
|
||||
|
||||
lookahead_reader_consume_all :: proc(lr: ^Loadahead_Reader) -> io.Error {
|
||||
return lookahead_reader_consume(lr, lr.n);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package bufio
|
||||
|
||||
import "core:io"
|
||||
|
||||
// Read_Writer stores pointers to a Reader and a Writer
|
||||
Read_Writer :: struct {
|
||||
r: ^Reader,
|
||||
w: ^Writer,
|
||||
}
|
||||
|
||||
|
||||
read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
|
||||
rw.r, rw.w = r, w;
|
||||
}
|
||||
|
||||
read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
|
||||
s.stream_data = rw;
|
||||
s.stream_vtable = _read_writer_vtable;
|
||||
return;
|
||||
}
|
||||
|
||||
@(private)
|
||||
_read_writer_vtable := &io.Stream_VTable{
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_read(b, p);
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_read_byte(b);
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_unread_byte(b);
|
||||
},
|
||||
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_read_rune(b);
|
||||
},
|
||||
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_unread_rune(b);
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_write_to(b, w);
|
||||
},
|
||||
impl_flush = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Read_Writer)(s.stream_data).w;
|
||||
return writer_flush(b);
|
||||
},
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).w;
|
||||
return writer_write(b, p);
|
||||
},
|
||||
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
|
||||
b := (^Read_Writer)(s.stream_data).w;
|
||||
return writer_write_byte(b, c);
|
||||
},
|
||||
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).w;
|
||||
return writer_write_rune(b, r);
|
||||
},
|
||||
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).w;
|
||||
return writer_read_from(b, r);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,480 @@
|
||||
package bufio
|
||||
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
import "core:bytes"
|
||||
|
||||
// Reader is a buffered wrapper for an io.Reader
|
||||
Reader :: struct {
|
||||
buf: []byte,
|
||||
buf_allocator: mem.Allocator,
|
||||
|
||||
rd: io.Reader, // reader
|
||||
r, w: int, // read and write positions for buf
|
||||
|
||||
err: io.Error,
|
||||
|
||||
last_byte: int, // last byte read, invalid is -1
|
||||
last_rune_size: int, // size of last rune read, invalid is -1
|
||||
|
||||
max_consecutive_empty_reads: int,
|
||||
}
|
||||
|
||||
|
||||
DEFAULT_BUF_SIZE :: 4096;
|
||||
|
||||
@(private)
|
||||
MIN_READ_BUFFER_SIZE :: 16;
|
||||
@(private)
|
||||
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128;
|
||||
|
||||
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
||||
size := size;
|
||||
size = max(size, MIN_READ_BUFFER_SIZE);
|
||||
reader_reset(b, rd);
|
||||
b.buf_allocator = allocator;
|
||||
b.buf = make([]byte, size, allocator);
|
||||
}
|
||||
|
||||
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
|
||||
reader_reset(b, rd);
|
||||
b.buf_allocator = {};
|
||||
b.buf = buf;
|
||||
}
|
||||
|
||||
// reader_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
|
||||
reader_destroy :: proc(b: ^Reader) {
|
||||
delete(b.buf, b.buf_allocator);
|
||||
b^ = {};
|
||||
}
|
||||
|
||||
reader_size :: proc(b: ^Reader) -> int {
|
||||
return len(b.buf);
|
||||
}
|
||||
|
||||
reader_reset :: proc(b: ^Reader, r: io.Reader) {
|
||||
b.rd = r;
|
||||
b.r, b.w = 0, 0;
|
||||
b.err = nil;
|
||||
b.last_byte = -1;
|
||||
b.last_rune_size = -1;
|
||||
}
|
||||
|
||||
@(private)
|
||||
_reader_read_new_chunk :: proc(b: ^Reader) -> io.Error {
|
||||
if b.r > 0 {
|
||||
copy(b.buf, b.buf[b.r:b.w]);
|
||||
b.w -= b.r;
|
||||
b.r = 0;
|
||||
}
|
||||
|
||||
if b.w >= len(b.buf) {
|
||||
return .Buffer_Full;
|
||||
}
|
||||
|
||||
if b.max_consecutive_empty_reads <= 0 {
|
||||
b.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
|
||||
}
|
||||
|
||||
// read new data, and try a limited number of times
|
||||
for i := b.max_consecutive_empty_reads; i > 0; i -= 1 {
|
||||
n, err := io.read(b.rd, b.buf[b.w:]);
|
||||
if n < 0 {
|
||||
return .Negative_Read;
|
||||
}
|
||||
b.w += n;
|
||||
if err != nil {
|
||||
b.err = err;
|
||||
return nil;
|
||||
}
|
||||
if n > 0 {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
b.err = .No_Progress;
|
||||
return nil;
|
||||
}
|
||||
|
||||
@(private)
|
||||
_reader_consume_err :: proc(b: ^Reader) -> io.Error {
|
||||
err := b.err;
|
||||
b.err = nil;
|
||||
return err;
|
||||
}
|
||||
|
||||
// reader_peek returns the next n bytes without advancing the reader
|
||||
// The bytes stop being valid on the next read call
|
||||
// If reader_peek returns fewer than n bytes, it also return an error
|
||||
// explaining why the read is short
|
||||
// The error will be .Buffer_Full if n is larger than the internal buffer size
|
||||
reader_peek :: proc(b: ^Reader, n: int) -> (data: []byte, err: io.Error) {
|
||||
n := n;
|
||||
|
||||
if n < 0 {
|
||||
return nil, .Negative_Count;
|
||||
}
|
||||
b.last_byte = -1;
|
||||
b.last_rune_size = -1;
|
||||
|
||||
for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil {
|
||||
if fill_err := _reader_read_new_chunk(b); fill_err != nil {
|
||||
return nil, fill_err;
|
||||
}
|
||||
}
|
||||
|
||||
if n > len(b.buf) {
|
||||
return b.buf[b.r : b.w], .Buffer_Full;
|
||||
}
|
||||
|
||||
if available := b.w - b.r; available < n {
|
||||
n = available;
|
||||
err = _reader_consume_err(b);
|
||||
if err == nil {
|
||||
err = .Buffer_Full;
|
||||
}
|
||||
}
|
||||
|
||||
return b.buf[b.r : b.r+n], err;
|
||||
}
|
||||
|
||||
// reader_buffered returns the number of bytes that can be read from the current buffer
|
||||
reader_buffered :: proc(b: ^Reader) -> int {
|
||||
return b.w - b.r;
|
||||
}
|
||||
|
||||
// reader_discard skips the next n bytes, and returns the number of bytes that were discarded
|
||||
reader_discard :: proc(b: ^Reader, n: int) -> (discarded: int, err: io.Error) {
|
||||
if n < 0 {
|
||||
return 0, .Negative_Count;
|
||||
}
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
remaining := n;
|
||||
for {
|
||||
skip := reader_buffered(b);
|
||||
if skip == 0 {
|
||||
if fill_err := _reader_read_new_chunk(b); fill_err != nil {
|
||||
return 0, fill_err;
|
||||
}
|
||||
skip = reader_buffered(b);
|
||||
}
|
||||
skip = min(skip, remaining);
|
||||
b.r += skip;
|
||||
remaining -= skip;
|
||||
if remaining == 0 {
|
||||
return n, nil;
|
||||
}
|
||||
if b.err != nil {
|
||||
return n - remaining, _reader_consume_err(b);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// reader_read reads data into p
|
||||
// The bytes are taken from at most one read on the underlying Reader, which means n may be less than len(p)
|
||||
reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
||||
n = len(p);
|
||||
if n == 0 {
|
||||
if reader_buffered(b) > 0 {
|
||||
return 0, nil;
|
||||
}
|
||||
return 0, _reader_consume_err(b);
|
||||
}
|
||||
if b.r == b.w {
|
||||
if b.err != nil {
|
||||
return 0, _reader_consume_err(b);
|
||||
}
|
||||
|
||||
if len(p) >= len(b.buf) {
|
||||
n, b.err = io.read(b.rd, p);
|
||||
if n < 0 {
|
||||
return 0, .Negative_Read;
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
b.last_byte = int(p[n-1]);
|
||||
b.last_rune_size = -1;
|
||||
}
|
||||
return n, _reader_consume_err(b);
|
||||
}
|
||||
|
||||
b.r, b.w = 0, 0;
|
||||
n, b.err = io.read(b.rd, b.buf);
|
||||
if n < 0 {
|
||||
return 0, .Negative_Read;
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, _reader_consume_err(b);
|
||||
}
|
||||
b.w += n;
|
||||
}
|
||||
|
||||
n = copy(p, b.buf[b.r:b.w]);
|
||||
b.r += n;
|
||||
b.last_byte = int(b.buf[b.r-1]);
|
||||
b.last_rune_size = -1;
|
||||
return n, nil;
|
||||
}
|
||||
|
||||
// reader_read_byte reads and returns a single byte
|
||||
// If no byte is available, it return an error
|
||||
reader_read_byte :: proc(b: ^Reader) -> (byte, io.Error) {
|
||||
b.last_rune_size = -1;
|
||||
for b.r == b.w {
|
||||
if b.err != nil {
|
||||
return 0, _reader_consume_err(b);
|
||||
}
|
||||
if err := _reader_read_new_chunk(b); err != nil {
|
||||
return 0, err;
|
||||
}
|
||||
}
|
||||
c := b.buf[b.r];
|
||||
b.r += 1;
|
||||
b.last_byte = int(c);
|
||||
return c, nil;
|
||||
}
|
||||
|
||||
// reader_unread_byte unreads the last byte. Only the most recently read byte can be unread
|
||||
reader_unread_byte :: proc(b: ^Reader) -> io.Error {
|
||||
if b.last_byte < 0 || b.r == 0 && b.w > 0 {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
if b.r > 0 {
|
||||
b.r -= 1;
|
||||
} else {
|
||||
// b.r == 0 && b.w == 0
|
||||
b.w = 1;
|
||||
}
|
||||
b.buf[b.r] = byte(b.last_byte);
|
||||
b.last_byte = -1;
|
||||
b.last_rune_size = -1;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// reader_read_rune reads a single UTF-8 encoded unicode character
|
||||
// and returns the rune and its size in bytes
|
||||
// If the encoded rune is invalid, it consumes one byte and returns utf8.RUNE_ERROR (U+FFFD) with a size of 1
|
||||
reader_read_rune :: proc(b: ^Reader) -> (r: rune, size: int, err: io.Error) {
|
||||
for b.r+utf8.UTF_MAX > b.w &&
|
||||
!utf8.full_rune(b.buf[b.r:b.w]) &&
|
||||
b.err == nil &&
|
||||
b.w-b.w < len(b.buf) {
|
||||
if err = _reader_read_new_chunk(b); err != nil {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
b.last_rune_size = -1;
|
||||
if b.r == b.w {
|
||||
err = _reader_consume_err(b);
|
||||
return;
|
||||
}
|
||||
r, size = rune(b.buf[b.r]), 1;
|
||||
if r >= utf8.RUNE_SELF {
|
||||
r, size = utf8.decode_rune(b.buf[b.r : b.w]);
|
||||
}
|
||||
b.r += size;
|
||||
b.last_byte = int(b.buf[b.r-1]);
|
||||
b.last_rune_size = size;
|
||||
return;
|
||||
}
|
||||
|
||||
// reader_unread_rune unreads the last rune. Only the most recently read rune can be unread
|
||||
reader_unread_rune :: proc(b: ^Reader) -> io.Error {
|
||||
if b.last_rune_size < 0 || b.r < b.last_rune_size {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
b.r -= b.last_rune_size;
|
||||
b.last_byte = -1;
|
||||
b.last_rune_size = -1;
|
||||
return nil;
|
||||
}
|
||||
|
||||
reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) {
|
||||
n, err := io.write(w, b.buf[b.r:b.w]);
|
||||
if n < 0 {
|
||||
return 0, .Negative_Write;
|
||||
}
|
||||
b.r += n;
|
||||
return i64(n), err;
|
||||
}
|
||||
|
||||
n, err = write_buf(b, w);
|
||||
if err != nil {
|
||||
return;
|
||||
}
|
||||
|
||||
m: i64;
|
||||
if nr, ok := io.to_writer_to(b.rd); ok {
|
||||
m, err = io.write_to(nr, w);
|
||||
n += m;
|
||||
return n, err;
|
||||
}
|
||||
|
||||
if nw, ok := io.to_reader_from(w); ok {
|
||||
m, err = io.read_from(nw, b.rd);
|
||||
n += m;
|
||||
return n, err;
|
||||
}
|
||||
|
||||
if b.w-b.r < len(b.buf) {
|
||||
if err = _reader_read_new_chunk(b); err != nil {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for b.r < b.w {
|
||||
m, err = write_buf(b, w);
|
||||
n += m;
|
||||
if err != nil {
|
||||
return;
|
||||
}
|
||||
if err = _reader_read_new_chunk(b); err != nil {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if b.err == .EOF {
|
||||
b.err = nil;
|
||||
}
|
||||
|
||||
err = _reader_consume_err(b);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// reader_to_stream converts a Reader into an io.Stream
|
||||
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
|
||||
s.stream_data = b;
|
||||
s.stream_vtable = _reader_vtable;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
_reader_vtable := &io.Stream_VTable{
|
||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Reader)(s.stream_data);
|
||||
reader_destroy(b);
|
||||
return nil;
|
||||
},
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_read(b, p);
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_read_byte(b);
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_unread_byte(b);
|
||||
},
|
||||
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_read_rune(b);
|
||||
},
|
||||
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_unread_rune(b);
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_write_to(b, w);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Utility procedures
|
||||
//
|
||||
|
||||
|
||||
// reader_read_slice reads until the first occurrence of delim from the reader
|
||||
// It returns a slice pointing at the bytes in the buffer
|
||||
// The bytes stop being valid at the next read
|
||||
// If reader_read_slice encounters an error before finding a delimiter
|
||||
// reader_read_slice fails with error .Buffer_Full if the buffer fills without a delim
|
||||
// Because the data returned from reader_read_slice will be overwritten on the
|
||||
// next IO operation, reader_read_bytes or reader_read_string is usually preferred
|
||||
//
|
||||
// reader_read_slice returns err != nil if and only if line does not end in delim
|
||||
//
|
||||
reader_read_slice :: proc(b: ^Reader, delim: byte) -> (line: []byte, err: io.Error) {
|
||||
s := 0;
|
||||
for {
|
||||
if i := bytes.index_byte(b.buf[b.r+s : b.w], delim); i >= 0 {
|
||||
i += s;
|
||||
line = b.buf[b.r:][:i+1];
|
||||
b.r += i + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if b.err != nil {
|
||||
line = b.buf[b.r : b.w];
|
||||
b.r = b.w;
|
||||
err = _reader_consume_err(b);
|
||||
break;
|
||||
}
|
||||
|
||||
if reader_buffered(b) >= len(b.buf) {
|
||||
b.r = b.w;
|
||||
line = b.buf;
|
||||
err = .Buffer_Full;
|
||||
break;
|
||||
}
|
||||
|
||||
s = b.w - b.r;
|
||||
|
||||
if err = _reader_read_new_chunk(b); err != nil {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if i := len(line)-1; i >= 0 {
|
||||
b.last_byte = int(line[i]);
|
||||
b.last_rune_size = -1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// reader_read_bytes reads until the first occurrence of delim from the Reader
|
||||
// It returns an allocated slice containing the data up to and including the delimiter
|
||||
reader_read_bytes :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (buf: []byte, err: io.Error) {
|
||||
full: [dynamic]byte;
|
||||
full.allocator = allocator;
|
||||
|
||||
frag: []byte;
|
||||
for {
|
||||
e: io.Error;
|
||||
frag, e = reader_read_slice(b, delim);
|
||||
if e == nil {
|
||||
break;
|
||||
}
|
||||
if e != .Buffer_Full {
|
||||
err = e;
|
||||
break;
|
||||
}
|
||||
|
||||
append(&full, ..frag);
|
||||
}
|
||||
append(&full, ..frag);
|
||||
return full[:], err;
|
||||
}
|
||||
|
||||
// reader_read_string reads until the first occurrence of delim from the Reader
|
||||
// It returns an allocated string containing the data up to and including the delimiter
|
||||
reader_read_string :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (string, io.Error) {
|
||||
buf, err := reader_read_bytes(b, delim, allocator);
|
||||
return string(buf), err;
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
package bufio
|
||||
|
||||
import "core:bytes"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
import "intrinsics"
|
||||
|
||||
// Extra errors returns by scanning procedures
|
||||
Scanner_Extra_Error :: enum i32 {
|
||||
Negative_Advance,
|
||||
Advanced_Too_Far,
|
||||
Bad_Read_Count,
|
||||
Too_Long,
|
||||
Too_Short,
|
||||
}
|
||||
|
||||
Scanner_Error :: union {
|
||||
io.Error,
|
||||
Scanner_Extra_Error,
|
||||
}
|
||||
|
||||
// Split_Proc is the signature of the split procedure used to tokenize the input.
|
||||
Split_Proc :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool);
|
||||
|
||||
Scanner :: struct {
|
||||
r: io.Reader,
|
||||
split: Split_Proc,
|
||||
|
||||
buf: [dynamic]byte,
|
||||
max_token_size: int,
|
||||
start: int,
|
||||
end: int,
|
||||
token: []byte,
|
||||
|
||||
_err: Scanner_Error,
|
||||
max_consecutive_empty_reads: int,
|
||||
successive_empty_token_count: int,
|
||||
scan_called: bool,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
DEFAULT_MAX_SCAN_TOKEN_SIZE :: 1<<16;
|
||||
|
||||
@(private)
|
||||
_INIT_BUF_SIZE :: 4096;
|
||||
|
||||
scanner_init :: proc(s: ^Scanner, r: io.Reader, buf_allocator := context.allocator) -> ^Scanner {
|
||||
s.r = r;
|
||||
s.split = scan_lines;
|
||||
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE;
|
||||
s.buf.allocator = buf_allocator;
|
||||
return s;
|
||||
}
|
||||
scanner_init_with_buffer :: proc(s: ^Scanner, r: io.Reader, buf: []byte) -> ^Scanner {
|
||||
s.r = r;
|
||||
s.split = scan_lines;
|
||||
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE;
|
||||
s.buf = mem.buffer_from_slice(buf);
|
||||
resize(&s.buf, cap(s.buf));
|
||||
return s;
|
||||
}
|
||||
scanner_destroy :: proc(s: ^Scanner) {
|
||||
delete(s.buf);
|
||||
}
|
||||
|
||||
|
||||
// Returns the first non-EOF error that was encounted by the scanner
|
||||
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
||||
switch s._err {
|
||||
case .EOF, .None:
|
||||
return nil;
|
||||
}
|
||||
return s._err;
|
||||
}
|
||||
|
||||
// Returns the most recent token created by scanner_scan.
|
||||
// The underlying array may point to data that may be overwritten
|
||||
// by another call to scanner_scan.
|
||||
// Treat the returned value as if it is immutable.
|
||||
scanner_bytes :: proc(s: ^Scanner) -> []byte {
|
||||
return s.token;
|
||||
}
|
||||
|
||||
// Returns the most recent token created by scanner_scan.
|
||||
// The underlying array may point to data that may be overwritten
|
||||
// by another call to scanner_scan.
|
||||
// Treat the returned value as if it is immutable.
|
||||
scanner_text :: proc(s: ^Scanner) -> string {
|
||||
return string(s.token);
|
||||
}
|
||||
|
||||
// scanner_scan advances the scanner
|
||||
scanner_scan :: proc(s: ^Scanner) -> bool {
|
||||
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
|
||||
err := err;
|
||||
if err == .None {
|
||||
err = nil;
|
||||
}
|
||||
switch s._err {
|
||||
case nil, .EOF:
|
||||
s._err = err;
|
||||
}
|
||||
}
|
||||
|
||||
if s.done {
|
||||
return false;
|
||||
}
|
||||
s.scan_called = true;
|
||||
|
||||
for {
|
||||
// Check if a token is possible with what is available
|
||||
// Allow the split procedure to recover if it fails
|
||||
if s.start < s.end || s._err != nil {
|
||||
advance, token, err, final_token := s.split(s.buf[s.start:s.end], s._err != nil);
|
||||
if final_token {
|
||||
s.token = token;
|
||||
s.done = true;
|
||||
return true;
|
||||
}
|
||||
if err != nil {
|
||||
set_err(s, err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do advance
|
||||
if advance < 0 {
|
||||
set_err(s, .Negative_Advance);
|
||||
return false;
|
||||
}
|
||||
if advance > s.end-s.start {
|
||||
set_err(s, .Advanced_Too_Far);
|
||||
return false;
|
||||
}
|
||||
s.start += advance;
|
||||
|
||||
s.token = token;
|
||||
if s.token != nil {
|
||||
if s._err == nil || advance > 0 {
|
||||
s.successive_empty_token_count = 0;
|
||||
} else {
|
||||
s.successive_empty_token_count += 1;
|
||||
|
||||
if s.max_consecutive_empty_reads <= 0 {
|
||||
s.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
|
||||
}
|
||||
if s.successive_empty_token_count > s.max_consecutive_empty_reads {
|
||||
set_err(s, .No_Progress);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If an error is hit, no token can be created
|
||||
if s._err != nil {
|
||||
s.start = 0;
|
||||
s.end = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// More data must be required to be read
|
||||
if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
|
||||
copy(s.buf[:], s.buf[s.start:s.end]);
|
||||
s.end -= s.start;
|
||||
s.start = 0;
|
||||
}
|
||||
|
||||
could_be_too_short := false;
|
||||
|
||||
// Resize the buffer if full
|
||||
if s.end == len(s.buf) {
|
||||
if s.max_token_size <= 0 {
|
||||
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE;
|
||||
}
|
||||
if len(s.buf) >= s.max_token_size {
|
||||
set_err(s, .Too_Long);
|
||||
return false;
|
||||
}
|
||||
// overflow check
|
||||
new_size := _INIT_BUF_SIZE;
|
||||
if len(s.buf) > 0 {
|
||||
overflowed: bool;
|
||||
if new_size, overflowed = intrinsics.overflow_mul(len(s.buf), 2); overflowed {
|
||||
set_err(s, .Too_Long);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
old_size := len(s.buf);
|
||||
new_size = min(new_size, s.max_token_size);
|
||||
resize(&s.buf, new_size);
|
||||
s.end -= s.start;
|
||||
s.start = 0;
|
||||
|
||||
could_be_too_short = old_size >= len(s.buf);
|
||||
|
||||
}
|
||||
|
||||
// Read data into the buffer
|
||||
loop := 0;
|
||||
for {
|
||||
n, err := io.read(s.r, s.buf[s.end:len(s.buf)]);
|
||||
if n < 0 || len(s.buf)-s.end < n {
|
||||
set_err(s, .Bad_Read_Count);
|
||||
break;
|
||||
}
|
||||
s.end += n;
|
||||
if err != nil {
|
||||
set_err(s, err);
|
||||
break;
|
||||
}
|
||||
if n > 0 {
|
||||
s.successive_empty_token_count = 0;
|
||||
break;
|
||||
}
|
||||
loop += 1;
|
||||
|
||||
if s.max_consecutive_empty_reads <= 0 {
|
||||
s.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
|
||||
}
|
||||
if loop > s.max_consecutive_empty_reads {
|
||||
if could_be_too_short {
|
||||
set_err(s, .Too_Short);
|
||||
} else {
|
||||
set_err(s, .No_Progress);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
if at_eof && len(data) == 0 {
|
||||
return;
|
||||
}
|
||||
return 1, data[0:1], nil, false;
|
||||
}
|
||||
|
||||
scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
if at_eof && len(data) == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
if data[0] < utf8.RUNE_SELF {
|
||||
advance = 1;
|
||||
token = data[0:1];
|
||||
return;
|
||||
}
|
||||
|
||||
_, width := utf8.decode_rune(data);
|
||||
if width > 1 {
|
||||
advance = width;
|
||||
token = data[0:width];
|
||||
return;
|
||||
}
|
||||
|
||||
if !at_eof && !utf8.full_rune(data) {
|
||||
return;
|
||||
}
|
||||
|
||||
@thread_local ERROR_RUNE := []byte{0xef, 0xbf, 0xbd};
|
||||
|
||||
advance = 1;
|
||||
token = ERROR_RUNE;
|
||||
return;
|
||||
}
|
||||
|
||||
scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
is_space :: proc "contextless" (r: rune) -> bool {
|
||||
switch r {
|
||||
// lower ones
|
||||
case ' ', '\t', '\n', '\v', '\f', '\r':
|
||||
return true;
|
||||
case '\u0085', '\u00a0':
|
||||
return true;
|
||||
// higher ones
|
||||
case '\u2000' ..= '\u200a':
|
||||
return true;
|
||||
case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip spaces at the beginning
|
||||
start := 0;
|
||||
for width := 0; start < len(data); start += width {
|
||||
r: rune;
|
||||
r, width = utf8.decode_rune(data[start:]);
|
||||
if !is_space(r) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for width, i := 0, start; i < len(data); i += width {
|
||||
r: rune;
|
||||
r, width = utf8.decode_rune(data[i:]);
|
||||
if is_space(r) {
|
||||
advance = i+width;
|
||||
token = data[start:i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if at_eof && len(data) > start {
|
||||
advance = len(data);
|
||||
token = data[start:];
|
||||
return;
|
||||
}
|
||||
|
||||
advance = start;
|
||||
return;
|
||||
}
|
||||
|
||||
scan_lines :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
trim_carriage_return :: proc "contextless" (data: []byte) -> []byte {
|
||||
if len(data) > 0 && data[len(data)-1] == '\r' {
|
||||
return data[0:len(data)-1];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
if at_eof && len(data) == 0 {
|
||||
return;
|
||||
}
|
||||
if i := bytes.index_byte(data, '\n'); i >= 0 {
|
||||
advance = i+1;
|
||||
token = trim_carriage_return(data[0:i]);
|
||||
return;
|
||||
}
|
||||
|
||||
if at_eof {
|
||||
advance = len(data);
|
||||
token = trim_carriage_return(data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
package bufio
|
||||
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
// import "core:bytes"
|
||||
|
||||
// Writer is a buffered wrapper for an io.Writer
|
||||
Writer :: struct {
|
||||
buf: []byte,
|
||||
buf_allocator: mem.Allocator,
|
||||
|
||||
wr: io.Writer,
|
||||
n: int,
|
||||
|
||||
err: io.Error,
|
||||
|
||||
max_consecutive_empty_writes: int,
|
||||
|
||||
}
|
||||
|
||||
writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
||||
size := size;
|
||||
size = max(size, MIN_READ_BUFFER_SIZE);
|
||||
writer_reset(b, wr);
|
||||
b.buf_allocator = allocator;
|
||||
b.buf = make([]byte, size, allocator);
|
||||
}
|
||||
|
||||
writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
|
||||
writer_reset(b, wr);
|
||||
b.buf_allocator = {};
|
||||
b.buf = buf;
|
||||
}
|
||||
|
||||
// writer_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
|
||||
writer_destroy :: proc(b: ^Writer) {
|
||||
delete(b.buf, b.buf_allocator);
|
||||
b^ = {};
|
||||
}
|
||||
|
||||
// writer_size returns the size of underlying buffer in bytes
|
||||
writer_size :: proc(b: ^Writer) -> int {
|
||||
return len(b.buf);
|
||||
}
|
||||
|
||||
writer_reset :: proc(b: ^Writer, w: io.Writer) {
|
||||
b.wr = w;
|
||||
b.n = 0;
|
||||
b.err = nil;
|
||||
}
|
||||
|
||||
|
||||
// writer_flush writes any buffered data into the underlying io.Writer
|
||||
writer_flush :: proc(b: ^Writer) -> io.Error {
|
||||
if b.err != nil {
|
||||
return b.err;
|
||||
}
|
||||
if b.n == 0 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
n, err := io.write(b.wr, b.buf[0:b.n]);
|
||||
if n < b.n && err == nil {
|
||||
err = .Short_Write;
|
||||
}
|
||||
if err != nil {
|
||||
if n > 0 && n < b.n {
|
||||
copy(b.buf[:b.n-n], b.buf[n : b.n]);
|
||||
}
|
||||
b.n -= n;
|
||||
b.err = err;
|
||||
return err;
|
||||
}
|
||||
b.n = 0;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// writer_available returns how many bytes are unused in the buffer
|
||||
writer_available :: proc(b: ^Writer) -> int {
|
||||
return len(b.buf) - b.n;
|
||||
}
|
||||
|
||||
// writer_buffered returns the number of bytes that have been writted into the current buffer
|
||||
writer_buffered :: proc(b: ^Writer) -> int {
|
||||
return b.n;
|
||||
}
|
||||
|
||||
// writer_write writes the contents of p into the buffer
|
||||
// It returns the number of bytes written
|
||||
// If n < len(p), it will return an error explaining why the write is short
|
||||
writer_write :: proc(b: ^Writer, p: []byte) -> (n: int, err: io.Error) {
|
||||
p := p;
|
||||
for len(p) > writer_available(b) && b.err == nil {
|
||||
m: int;
|
||||
if writer_buffered(b) == 0 {
|
||||
m, b.err = io.write(b.wr, p);
|
||||
} else {
|
||||
m = copy(b.buf[b.n:], p);
|
||||
b.n += m;
|
||||
writer_flush(b);
|
||||
}
|
||||
n += m;
|
||||
p = p[m:];
|
||||
}
|
||||
if b.err != nil {
|
||||
return n, b.err;
|
||||
}
|
||||
m := copy(b.buf[b.n:], p);
|
||||
b.n += m;
|
||||
m += n;
|
||||
return m, nil;
|
||||
}
|
||||
|
||||
// writer_write_byte writes a single byte
|
||||
writer_write_byte :: proc(b: ^Writer, c: byte) -> io.Error {
|
||||
if b.err != nil {
|
||||
return b.err;
|
||||
}
|
||||
if writer_available(b) <= 0 && writer_flush(b) != nil {
|
||||
return b.err;
|
||||
}
|
||||
b.buf[b.n] = c;
|
||||
b.n += 1;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// writer_write_rune writes a single unicode code point, and returns the number of bytes written with any error
|
||||
writer_write_rune :: proc(b: ^Writer, r: rune) -> (size: int, err: io.Error) {
|
||||
if r < utf8.RUNE_SELF {
|
||||
err = writer_write_byte(b, byte(r));
|
||||
size = 0 if err != nil else 1;
|
||||
return;
|
||||
}
|
||||
if b.err != nil {
|
||||
return 0, b.err;
|
||||
}
|
||||
|
||||
buf: [4]u8;
|
||||
|
||||
n := writer_available(b);
|
||||
if n < utf8.UTF_MAX {
|
||||
writer_flush(b);
|
||||
if b.err != nil {
|
||||
return 0, b.err;
|
||||
}
|
||||
n = writer_available(b);
|
||||
if n < utf8.UTF_MAX {
|
||||
// this only happens if the buffer is very small
|
||||
w: int;
|
||||
buf, w = utf8.encode_rune(r);
|
||||
return writer_write(b, buf[:w]);
|
||||
}
|
||||
}
|
||||
|
||||
buf, size = utf8.encode_rune(r);
|
||||
copy(b.buf[b.n:], buf[:size]);
|
||||
b.n += size;
|
||||
return;
|
||||
}
|
||||
|
||||
// writer_write writes a string into the buffer
|
||||
// It returns the number of bytes written
|
||||
// If n < len(p), it will return an error explaining why the write is short
|
||||
writer_write_string :: proc(b: ^Writer, s: string) -> (int, io.Error) {
|
||||
return writer_write(b, transmute([]byte)s);
|
||||
}
|
||||
|
||||
// writer_read_from is to support io.Reader_From types
|
||||
// If the underlying writer supports the io,read_from, and b has no buffered data yet,
|
||||
// this procedure calls the underlying read_from implementation without buffering
|
||||
writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
if b.err != nil {
|
||||
return 0, b.err;
|
||||
}
|
||||
if writer_buffered(b) == 0 {
|
||||
if w, ok := io.to_reader_from(b.wr); !ok {
|
||||
n, err = io.read_from(w, r);
|
||||
b.err = err;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
if writer_available(b) == 0 {
|
||||
if ferr := writer_flush(b); ferr != nil {
|
||||
return n, ferr;
|
||||
}
|
||||
}
|
||||
if b.max_consecutive_empty_writes <= 0 {
|
||||
b.max_consecutive_empty_writes = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
|
||||
}
|
||||
|
||||
m: int;
|
||||
nr := 0;
|
||||
for nr < b.max_consecutive_empty_writes {
|
||||
m, err = io.read(r, b.buf[b.n:]);
|
||||
if m != 0 || err != nil {
|
||||
break;
|
||||
}
|
||||
nr += 1;
|
||||
}
|
||||
if nr == b.max_consecutive_empty_writes {
|
||||
return n, .No_Progress;
|
||||
}
|
||||
b.n += m;
|
||||
n += i64(m);
|
||||
if err != nil {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if err == .EOF {
|
||||
if writer_available(b) == 0 {
|
||||
err = writer_flush(b);
|
||||
} else {
|
||||
err = nil;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// writer_to_stream converts a Writer into an io.Stream
|
||||
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
|
||||
s.stream_data = b;
|
||||
s.stream_vtable = _writer_vtable;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
_writer_vtable := &io.Stream_VTable{
|
||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Writer)(s.stream_data);
|
||||
writer_destroy(b);
|
||||
return nil;
|
||||
},
|
||||
impl_flush = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Writer)(s.stream_data);
|
||||
return writer_flush(b);
|
||||
},
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Writer)(s.stream_data);
|
||||
return writer_write(b, p);
|
||||
},
|
||||
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
|
||||
b := (^Writer)(s.stream_data);
|
||||
return writer_write_byte(b, c);
|
||||
},
|
||||
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
|
||||
b := (^Writer)(s.stream_data);
|
||||
return writer_write_rune(b, r);
|
||||
},
|
||||
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
b := (^Writer)(s.stream_data);
|
||||
return writer_read_from(b, r);
|
||||
},
|
||||
};
|
||||
@@ -43,6 +43,7 @@ complex32 :: complex32;
|
||||
complex64 :: complex64;
|
||||
complex128 :: complex128;
|
||||
|
||||
quaternion64 :: quaternion64;
|
||||
quaternion128 :: quaternion128;
|
||||
quaternion256 :: quaternion256;
|
||||
|
||||
@@ -76,6 +77,17 @@ u64be :: u64be;
|
||||
i128be :: i128be;
|
||||
u128be :: u128be;
|
||||
|
||||
|
||||
f16le :: f16le;
|
||||
f32le :: f32le;
|
||||
f64le :: f64le;
|
||||
|
||||
f16be :: f16be;
|
||||
f32be :: f32be;
|
||||
f64be :: f64be;
|
||||
|
||||
|
||||
|
||||
// Procedures
|
||||
len :: proc(array: Array_Type) -> int ---
|
||||
cap :: proc(array: Array_Type) -> int ---
|
||||
@@ -103,3 +115,6 @@ min :: proc(values: ..T) -> T ---
|
||||
max :: proc(values: ..T) -> T ---
|
||||
abs :: proc(value: T) -> T ---
|
||||
clamp :: proc(value, minimum, maximum: T) -> T ---
|
||||
|
||||
soa_zip :: proc(slices: ...) -> #soa[]Struct ---
|
||||
soa_unzip :: proc(value: $S/#soa[]$E) -> (slices: ...) ---
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
package bytes
|
||||
|
||||
import "core:io"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
MIN_READ :: 512;
|
||||
|
||||
@(private)
|
||||
SMALL_BUFFER_SIZE :: 64;
|
||||
|
||||
// A Buffer is a variable-sized buffer of bytes with a io.Stream interface
|
||||
// The zero value for Buffer is an empty buffer ready to use.
|
||||
Buffer :: struct {
|
||||
buf: [dynamic]byte,
|
||||
off: int,
|
||||
last_read: Read_Op,
|
||||
}
|
||||
|
||||
@(private)
|
||||
Read_Op :: enum i8 {
|
||||
Read = -1,
|
||||
Invalid = 0,
|
||||
Read_Rune1 = 1,
|
||||
Read_Rune2 = 2,
|
||||
Read_Rune3 = 3,
|
||||
Read_Rune4 = 4,
|
||||
}
|
||||
|
||||
|
||||
buffer_init :: proc(b: ^Buffer, buf: []byte) {
|
||||
resize(&b.buf, len(buf));
|
||||
copy(b.buf[:], buf);
|
||||
}
|
||||
|
||||
buffer_init_string :: proc(b: ^Buffer, s: string) {
|
||||
resize(&b.buf, len(s));
|
||||
copy(b.buf[:], s);
|
||||
}
|
||||
|
||||
buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) {
|
||||
b.buf.allocator = allocator;
|
||||
reserve(&b.buf, cap);
|
||||
resize(&b.buf, len);
|
||||
}
|
||||
|
||||
buffer_destroy :: proc(b: ^Buffer) {
|
||||
delete(b.buf);
|
||||
buffer_reset(b);
|
||||
}
|
||||
|
||||
buffer_to_bytes :: proc(b: ^Buffer) -> []byte {
|
||||
return b.buf[b.off:];
|
||||
}
|
||||
|
||||
buffer_to_string :: proc(b: ^Buffer) -> string {
|
||||
if b == nil {
|
||||
return "<nil>";
|
||||
}
|
||||
return string(b.buf[b.off:]);
|
||||
}
|
||||
|
||||
buffer_is_empty :: proc(b: ^Buffer) -> bool {
|
||||
return len(b.buf) <= b.off;
|
||||
}
|
||||
|
||||
buffer_length :: proc(b: ^Buffer) -> int {
|
||||
return len(b.buf) - b.off;
|
||||
}
|
||||
|
||||
buffer_capacity :: proc(b: ^Buffer) -> int {
|
||||
return cap(b.buf);
|
||||
}
|
||||
|
||||
buffer_reset :: proc(b: ^Buffer) {
|
||||
clear(&b.buf);
|
||||
b.off = 0;
|
||||
b.last_read = .Invalid;
|
||||
}
|
||||
|
||||
|
||||
buffer_truncate :: proc(b: ^Buffer, n: int) {
|
||||
if n == 0 {
|
||||
buffer_reset(b);
|
||||
return;
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
if n < 0 || n > buffer_length(b) {
|
||||
panic("bytes.truncate: truncation out of range");
|
||||
}
|
||||
resize(&b.buf, b.off+n);
|
||||
}
|
||||
|
||||
@(private)
|
||||
_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) {
|
||||
if l := len(b.buf); n <= cap(b.buf)-l {
|
||||
resize(&b.buf, l+n);
|
||||
return l, true;
|
||||
}
|
||||
return 0, false;
|
||||
}
|
||||
|
||||
@(private)
|
||||
_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
|
||||
m := buffer_length(b);
|
||||
if m == 0 && b.off != 0 {
|
||||
buffer_reset(b);
|
||||
}
|
||||
if i, ok := _buffer_try_grow(b, n); ok {
|
||||
return i;
|
||||
}
|
||||
if b.buf == nil && n <= SMALL_BUFFER_SIZE {
|
||||
b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
c := cap(b.buf);
|
||||
if n <= c/2 - m {
|
||||
copy(b.buf[:], b.buf[b.off:]);
|
||||
} else if c > max(int) - c - n {
|
||||
panic("bytes.Buffer: too large");
|
||||
} else {
|
||||
resize(&b.buf, 2*c + n);
|
||||
copy(b.buf[:], b.buf[b.off:]);
|
||||
}
|
||||
b.off = 0;
|
||||
resize(&b.buf, m+n);
|
||||
return m;
|
||||
}
|
||||
|
||||
buffer_grow :: proc(b: ^Buffer, n: int) {
|
||||
if n < 0 {
|
||||
panic("bytes.buffer_grow: negative count");
|
||||
}
|
||||
m := _buffer_grow(b, n);
|
||||
resize(&b.buf, m);
|
||||
}
|
||||
|
||||
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
if offset < 0 {
|
||||
err = .Invalid_Offset;
|
||||
return;
|
||||
}
|
||||
_, ok := _buffer_try_grow(b, offset+len(p));
|
||||
if !ok {
|
||||
_ = _buffer_grow(b, offset+len(p));
|
||||
}
|
||||
if len(b.buf) <= offset {
|
||||
return 0, .Short_Write;
|
||||
}
|
||||
return copy(b.buf[offset:], p), nil;
|
||||
}
|
||||
|
||||
|
||||
buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, len(p));
|
||||
if !ok {
|
||||
m = _buffer_grow(b, len(p));
|
||||
}
|
||||
return copy(b.buf[m:], p), nil;
|
||||
}
|
||||
|
||||
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, len(s));
|
||||
if !ok {
|
||||
m = _buffer_grow(b, len(s));
|
||||
}
|
||||
return copy(b.buf[m:], s), nil;
|
||||
}
|
||||
|
||||
buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error {
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, 1);
|
||||
if !ok {
|
||||
m = _buffer_grow(b, 1);
|
||||
}
|
||||
b.buf[m] = c;
|
||||
return nil;
|
||||
}
|
||||
|
||||
buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) {
|
||||
if r < utf8.RUNE_SELF {
|
||||
buffer_write_byte(b, byte(r));
|
||||
return 1, nil;
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, utf8.UTF_MAX);
|
||||
if !ok {
|
||||
m = _buffer_grow(b, utf8.UTF_MAX);
|
||||
}
|
||||
res: [4]byte;
|
||||
res, n = utf8.encode_rune(r);
|
||||
copy(b.buf[m:][:utf8.UTF_MAX], res[:n]);
|
||||
resize(&b.buf, m+n);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_next :: proc(b: ^Buffer, n: int) -> []byte {
|
||||
n := n;
|
||||
b.last_read = .Invalid;
|
||||
m := buffer_length(b);
|
||||
if n > m {
|
||||
n = m;
|
||||
}
|
||||
data := b.buf[b.off : b.off + n];
|
||||
b.off += n;
|
||||
if n > 0 {
|
||||
b.last_read = .Read;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
if buffer_is_empty(b) {
|
||||
buffer_reset(b);
|
||||
if len(p) == 0 {
|
||||
return 0, nil;
|
||||
}
|
||||
return 0, .EOF;
|
||||
}
|
||||
n = copy(p, b.buf[b.off:]);
|
||||
b.off += n;
|
||||
if n > 0 {
|
||||
b.last_read = .Read;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
|
||||
if offset < 0 || offset >= len(b.buf) {
|
||||
err = .Invalid_Offset;
|
||||
return;
|
||||
}
|
||||
|
||||
if 0 <= offset && offset < len(b.buf) {
|
||||
n = copy(p, b.buf[offset:]);
|
||||
}
|
||||
if n > 0 {
|
||||
b.last_read = .Read;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
buffer_read_byte :: proc(b: ^Buffer) -> (byte, io.Error) {
|
||||
if buffer_is_empty(b) {
|
||||
buffer_reset(b);
|
||||
return 0, .EOF;
|
||||
}
|
||||
c := b.buf[b.off];
|
||||
b.off += 1;
|
||||
b.last_read = .Read;
|
||||
return c, nil;
|
||||
}
|
||||
|
||||
buffer_read_rune :: proc(b: ^Buffer) -> (r: rune, size: int, err: io.Error) {
|
||||
if buffer_is_empty(b) {
|
||||
buffer_reset(b);
|
||||
return 0, 0, .EOF;
|
||||
}
|
||||
c := b.buf[b.off];
|
||||
if c < utf8.RUNE_SELF {
|
||||
b.off += 1;
|
||||
b.last_read = .Read_Rune1;
|
||||
return rune(c), 1, nil;
|
||||
}
|
||||
r, size = utf8.decode_rune(b.buf[b.off:]);
|
||||
b.off += size;
|
||||
b.last_read = Read_Op(i8(size));
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_unread_byte :: proc(b: ^Buffer) -> io.Error {
|
||||
if b.last_read == .Invalid {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
if b.off > 0 {
|
||||
b.off -= 1;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
|
||||
if b.last_read <= .Invalid {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
if b.off >= int(b.last_read) {
|
||||
b.off -= int(i8(b.last_read));
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
|
||||
i := index_byte(b.buf[b.off:], delim);
|
||||
end := b.off + i + 1;
|
||||
if i < 0 {
|
||||
end = len(b.buf);
|
||||
err = .EOF;
|
||||
}
|
||||
line = b.buf[b.off:end];
|
||||
b.off = end;
|
||||
b.last_read = .Read;
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Error) {
|
||||
slice: []byte;
|
||||
slice, err = buffer_read_bytes(b, delim);
|
||||
return string(slice), err;
|
||||
}
|
||||
|
||||
buffer_write_to :: proc(b: ^Buffer, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
if byte_count := buffer_length(b); byte_count > 0 {
|
||||
m, e := io.write(w, b.buf[b.off:]);
|
||||
if m > byte_count {
|
||||
panic("bytes.buffer_write_to: invalid io.write count");
|
||||
}
|
||||
b.off += m;
|
||||
n = i64(m);
|
||||
if e != nil {
|
||||
err = e;
|
||||
return;
|
||||
}
|
||||
if m != byte_count {
|
||||
err = .Short_Write;
|
||||
return;
|
||||
}
|
||||
}
|
||||
buffer_reset(b);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #no_bounds_check {
|
||||
b.last_read = .Invalid;
|
||||
for {
|
||||
i := _buffer_grow(b, MIN_READ);
|
||||
resize(&b.buf, i);
|
||||
m, e := io.read(r, b.buf[i:cap(b.buf)]);
|
||||
if m < 0 {
|
||||
err = .Negative_Read;
|
||||
return;
|
||||
}
|
||||
|
||||
resize(&b.buf, i+m);
|
||||
n += i64(m);
|
||||
if e == .EOF {
|
||||
return;
|
||||
}
|
||||
if e != nil {
|
||||
err = e;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
|
||||
s.stream_data = b;
|
||||
s.stream_vtable = _buffer_vtable;
|
||||
return;
|
||||
}
|
||||
|
||||
@(private)
|
||||
_buffer_vtable := &io.Stream_VTable{
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return i64(buffer_capacity(b));
|
||||
},
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read(b, p);
|
||||
},
|
||||
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_at(b, p, int(offset));
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_byte(b);
|
||||
},
|
||||
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_rune(b);
|
||||
},
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write(b, p);
|
||||
},
|
||||
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_at(b, p, int(offset));
|
||||
},
|
||||
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_byte(b, c);
|
||||
},
|
||||
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_rune(b, r);
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_unread_byte(b);
|
||||
},
|
||||
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_unread_rune(b);
|
||||
},
|
||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
buffer_destroy(b);
|
||||
return nil;
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_to(b, w);
|
||||
},
|
||||
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_from(b, r);
|
||||
},
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,177 @@
|
||||
package bytes
|
||||
|
||||
import "core:io"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
Reader :: struct {
|
||||
s: []byte, // read-only buffer
|
||||
i: i64, // current reading index
|
||||
prev_rune: int, // previous reading index of rune or < 0
|
||||
}
|
||||
|
||||
reader_init :: proc(r: ^Reader, s: []byte) {
|
||||
r.s = s;
|
||||
r.i = 0;
|
||||
r.prev_rune = -1;
|
||||
}
|
||||
|
||||
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
||||
s.stream_data = r;
|
||||
s.stream_vtable = _reader_vtable;
|
||||
return;
|
||||
}
|
||||
|
||||
reader_length :: proc(r: ^Reader) -> int {
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0;
|
||||
}
|
||||
return int(i64(len(r.s)) - r.i);
|
||||
}
|
||||
|
||||
reader_size :: proc(r: ^Reader) -> i64 {
|
||||
return i64(len(r.s));
|
||||
}
|
||||
|
||||
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0, .EOF;
|
||||
}
|
||||
r.prev_rune = -1;
|
||||
n = copy(p, r.s[r.i:]);
|
||||
r.i += i64(n);
|
||||
return;
|
||||
}
|
||||
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
||||
if off < 0 {
|
||||
return 0, .Invalid_Offset;
|
||||
}
|
||||
if off >= i64(len(r.s)) {
|
||||
return 0, .EOF;
|
||||
}
|
||||
n = copy(p, r.s[off:]);
|
||||
if n < len(p) {
|
||||
err = .EOF;
|
||||
}
|
||||
return;
|
||||
}
|
||||
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
|
||||
r.prev_rune = -1;
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0, .EOF;
|
||||
}
|
||||
b := r.s[r.i];
|
||||
r.i += 1;
|
||||
return b, nil;
|
||||
}
|
||||
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
|
||||
if r.i <= 0 {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
r.prev_rune = -1;
|
||||
r.i -= 1;
|
||||
return nil;
|
||||
}
|
||||
reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
|
||||
if r.i >= i64(len(r.s)) {
|
||||
r.prev_rune = -1;
|
||||
return 0, 0, .EOF;
|
||||
}
|
||||
r.prev_rune = int(r.i);
|
||||
if c := r.s[r.i]; c < utf8.RUNE_SELF {
|
||||
r.i += 1;
|
||||
return rune(c), 1, nil;
|
||||
}
|
||||
ch, size = utf8.decode_rune(r.s[r.i:]);
|
||||
r.i += i64(size);
|
||||
return;
|
||||
}
|
||||
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
|
||||
if r.i <= 0 {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
if r.prev_rune < 0 {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
r.i = i64(r.prev_rune);
|
||||
r.prev_rune = -1;
|
||||
return nil;
|
||||
}
|
||||
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||
r.prev_rune = -1;
|
||||
abs: i64;
|
||||
switch whence {
|
||||
case .Start:
|
||||
abs = offset;
|
||||
case .Current:
|
||||
abs = r.i + offset;
|
||||
case .End:
|
||||
abs = i64(len(r.s)) + offset;
|
||||
case:
|
||||
return 0, .Invalid_Whence;
|
||||
}
|
||||
|
||||
if abs < 0 {
|
||||
return 0, .Invalid_Offset;
|
||||
}
|
||||
r.i = abs;
|
||||
return abs, nil;
|
||||
}
|
||||
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
r.prev_rune = -1;
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0, nil;
|
||||
}
|
||||
s := r.s[r.i:];
|
||||
m: int;
|
||||
m, err = io.write(w, s);
|
||||
if m > len(s) {
|
||||
panic("bytes.Reader.write_to: invalid io.write_string count");
|
||||
}
|
||||
r.i += i64(m);
|
||||
n = i64(m);
|
||||
if m != len(s) && err == nil {
|
||||
err = .Short_Write;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_reader_vtable := &io.Stream_VTable{
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_size(r);
|
||||
},
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read(r, p);
|
||||
},
|
||||
impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read_at(r, p, off);
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read_byte(r);
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_unread_byte(r);
|
||||
},
|
||||
impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read_rune(r);
|
||||
},
|
||||
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_unread_rune(r);
|
||||
},
|
||||
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_seek(r, offset, whence);
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_write_to(r, w);
|
||||
},
|
||||
};
|
||||
+3
-3
@@ -14,8 +14,8 @@ ushort :: b.u16;
|
||||
int :: b.i32;
|
||||
uint :: b.u32;
|
||||
|
||||
long :: (ODIN_OS == "windows" || size_of(b.rawptr) == 4) ? b.i32 : b.i64;
|
||||
ulong :: (ODIN_OS == "windows" || size_of(b.rawptr) == 4) ? b.u32 : b.u64;
|
||||
long :: b.i32 when (ODIN_OS == "windows" || size_of(b.rawptr) == 4) else b.i64;
|
||||
ulong :: b.u32 when (ODIN_OS == "windows" || size_of(b.rawptr) == 4) else b.u64;
|
||||
|
||||
longlong :: b.i64;
|
||||
ulonglong :: b.u64;
|
||||
@@ -32,4 +32,4 @@ ptrdiff_t :: b.int;
|
||||
uintptr_t :: b.uintptr;
|
||||
intptr_t :: b.int;
|
||||
|
||||
wchar_t :: (ODIN_OS == "windows") ? b.u16 : b.u32;
|
||||
wchar_t :: b.u16 when (ODIN_OS == "windows") else b.u32;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package c_frontend_preprocess
|
||||
|
||||
import "core:c/frontend/tokenizer"
|
||||
|
||||
const_expr :: proc(rest: ^^Token, tok: ^Token) -> i64 {
|
||||
// TODO(bill): Handle const_expr correctly
|
||||
// This is effectively a mini-parser
|
||||
|
||||
assert(rest != nil);
|
||||
assert(tok != nil);
|
||||
rest^ = tokenizer.new_eof(tok);
|
||||
switch v in tok.val {
|
||||
case i64:
|
||||
return v;
|
||||
case f64:
|
||||
return i64(v);
|
||||
case string:
|
||||
return 0;
|
||||
case []u16:
|
||||
// TODO
|
||||
case []u32:
|
||||
// TODO
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,154 @@
|
||||
package c_frontend_preprocess
|
||||
|
||||
import "core:unicode/utf8"
|
||||
|
||||
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
||||
hex_to_int :: proc(c: byte) -> int {
|
||||
switch c {
|
||||
case '0'..='9': return int(c-'0');
|
||||
case 'a'..='f': return int(c-'a')+10;
|
||||
case 'A'..='F': return int(c-'A')+10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
w: int;
|
||||
|
||||
if str[0] == quote && quote == '"' {
|
||||
return;
|
||||
} else if str[0] >= 0x80 {
|
||||
r, w = utf8.decode_rune_in_string(str);
|
||||
return r, true, str[w:], true;
|
||||
} else if str[0] != '\\' {
|
||||
return rune(str[0]), false, str[1:], true;
|
||||
}
|
||||
|
||||
if len(str) <= 1 {
|
||||
return;
|
||||
}
|
||||
s := str;
|
||||
c := s[1];
|
||||
s = s[2:];
|
||||
|
||||
switch c {
|
||||
case: r = rune(c);
|
||||
|
||||
case 'a': r = '\a';
|
||||
case 'b': r = '\b';
|
||||
case 'e': r = '\e';
|
||||
case 'f': r = '\f';
|
||||
case 'n': r = '\n';
|
||||
case 'r': r = '\r';
|
||||
case 't': r = '\t';
|
||||
case 'v': r = '\v';
|
||||
case '\\': r = '\\';
|
||||
|
||||
case '"': r = '"';
|
||||
case '\'': r = '\'';
|
||||
|
||||
case '0'..='7':
|
||||
v := int(c-'0');
|
||||
if len(s) < 2 {
|
||||
return;
|
||||
}
|
||||
for i in 0..<len(s) {
|
||||
d := int(s[i]-'0');
|
||||
if d < 0 || d > 7 {
|
||||
return;
|
||||
}
|
||||
v = (v<<3) | d;
|
||||
}
|
||||
s = s[2:];
|
||||
if v > 0xff {
|
||||
return;
|
||||
}
|
||||
r = rune(v);
|
||||
|
||||
case 'x', 'u', 'U':
|
||||
count: int;
|
||||
switch c {
|
||||
case 'x': count = 2;
|
||||
case 'u': count = 4;
|
||||
case 'U': count = 8;
|
||||
}
|
||||
|
||||
if len(s) < count {
|
||||
return;
|
||||
}
|
||||
|
||||
for i in 0..<count {
|
||||
d := hex_to_int(s[i]);
|
||||
if d < 0 {
|
||||
return;
|
||||
}
|
||||
r = (r<<4) | rune(d);
|
||||
}
|
||||
s = s[count:];
|
||||
if c == 'x' {
|
||||
break;
|
||||
}
|
||||
if r > utf8.MAX_RUNE {
|
||||
return;
|
||||
}
|
||||
multiple_bytes = true;
|
||||
}
|
||||
|
||||
success = true;
|
||||
tail_string = s;
|
||||
return;
|
||||
}
|
||||
|
||||
unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: string, allocated, success: bool) {
|
||||
contains_rune :: proc(s: string, r: rune) -> int {
|
||||
for c, offset in s {
|
||||
if c == r {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(len(lit) >= 2);
|
||||
|
||||
s := lit;
|
||||
quote := '"';
|
||||
|
||||
if s == `""` {
|
||||
return "", false, true;
|
||||
}
|
||||
|
||||
if contains_rune(s, '\n') >= 0 {
|
||||
return s, false, false;
|
||||
}
|
||||
|
||||
if contains_rune(s, '\\') < 0 && contains_rune(s, quote) < 0 {
|
||||
if quote == '"' {
|
||||
return s, false, true;
|
||||
}
|
||||
}
|
||||
s = s[1:len(s)-1];
|
||||
|
||||
|
||||
buf_len := 3*len(s) / 2;
|
||||
buf := make([]byte, buf_len, allocator);
|
||||
offset := 0;
|
||||
for len(s) > 0 {
|
||||
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote));
|
||||
if !ok {
|
||||
delete(buf);
|
||||
return s, false, false;
|
||||
}
|
||||
s = tail_string;
|
||||
if r < 0x80 || !multiple_bytes {
|
||||
buf[offset] = byte(r);
|
||||
offset += 1;
|
||||
} else {
|
||||
b, w := utf8.encode_rune(r);
|
||||
copy(buf[offset:], b[:w]);
|
||||
offset += w;
|
||||
}
|
||||
}
|
||||
|
||||
new_string := string(buf[:offset]);
|
||||
|
||||
return new_string, true, true;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
package demo
|
||||
|
||||
import tokenizer "core:c/frontend/tokenizer"
|
||||
import preprocessor "core:c/frontend/preprocessor"
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
t := &tokenizer.Tokenizer{};
|
||||
tokenizer.init_defaults(t);
|
||||
|
||||
cpp := &preprocessor.Preprocessor{};
|
||||
cpp.warn, cpp.err = t.warn, t.err;
|
||||
preprocessor.init_lookup_tables(cpp);
|
||||
preprocessor.init_default_macros(cpp);
|
||||
cpp.include_paths = {"my/path/to/include"};
|
||||
|
||||
tok := tokenizer.tokenize_file(t, "the/source/file.c", 1);
|
||||
|
||||
tok = preprocessor.preprocess(cpp, tok);
|
||||
if tok != nil {
|
||||
for t := tok; t.kind != .EOF; t = t.next {
|
||||
fmt.println(t.lit);
|
||||
}
|
||||
}
|
||||
|
||||
fmt.println("[Done]");
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
package c_frontend_tokenizer
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package c_frontend_tokenizer
|
||||
|
||||
// NOTE(bill): This is a really dumb approach for a hide set,
|
||||
// but it's really simple and probably fast enough in practice
|
||||
|
||||
|
||||
Hide_Set :: struct {
|
||||
next: ^Hide_Set,
|
||||
name: string,
|
||||
}
|
||||
|
||||
|
||||
new_hide_set :: proc(name: string) -> ^Hide_Set {
|
||||
hs := new(Hide_Set);
|
||||
hs.name = name;
|
||||
return hs;
|
||||
}
|
||||
|
||||
hide_set_contains :: proc(hs: ^Hide_Set, name: string) -> bool {
|
||||
for h := hs; h != nil; h = h.next {
|
||||
if h.name == name {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
hide_set_union :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
|
||||
head: Hide_Set;
|
||||
curr := &head;
|
||||
|
||||
for h := a; h != nil; h = h.next {
|
||||
curr.next = new_hide_set(h.name);
|
||||
curr = curr.next;
|
||||
}
|
||||
curr.next = b;
|
||||
return head.next;
|
||||
}
|
||||
|
||||
|
||||
hide_set_intersection :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
|
||||
head: Hide_Set;
|
||||
curr := &head;
|
||||
|
||||
for h := a; h != nil; h = h.next {
|
||||
if hide_set_contains(b, h.name) {
|
||||
curr.next = new_hide_set(h.name);
|
||||
curr = curr.next;
|
||||
}
|
||||
}
|
||||
return head.next;
|
||||
}
|
||||
|
||||
|
||||
add_hide_set :: proc(tok: ^Token, hs: ^Hide_Set) -> ^Token {
|
||||
head: Token;
|
||||
curr := &head;
|
||||
|
||||
tok := tok;
|
||||
for ; tok != nil; tok = tok.next {
|
||||
t := copy_token(tok);
|
||||
t.hide_set = hide_set_union(t.hide_set, hs);
|
||||
curr.next = t;
|
||||
curr = curr.next;
|
||||
}
|
||||
return head.next;
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package c_frontend_tokenizer
|
||||
|
||||
|
||||
Pos :: struct {
|
||||
file: string,
|
||||
line: int,
|
||||
column: int,
|
||||
offset: int,
|
||||
}
|
||||
|
||||
Token_Kind :: enum {
|
||||
Invalid,
|
||||
Ident,
|
||||
Punct,
|
||||
Keyword,
|
||||
Char,
|
||||
String,
|
||||
Number,
|
||||
PP_Number,
|
||||
Comment,
|
||||
EOF,
|
||||
}
|
||||
|
||||
File :: struct {
|
||||
name: string,
|
||||
id: int,
|
||||
src: []byte,
|
||||
|
||||
display_name: string,
|
||||
line_delta: int,
|
||||
}
|
||||
|
||||
|
||||
Token_Type_Hint :: enum u8 {
|
||||
None,
|
||||
|
||||
Int,
|
||||
Long,
|
||||
Long_Long,
|
||||
|
||||
Unsigned_Int,
|
||||
Unsigned_Long,
|
||||
Unsigned_Long_Long,
|
||||
|
||||
Float,
|
||||
Double,
|
||||
Long_Double,
|
||||
|
||||
UTF_8,
|
||||
UTF_16,
|
||||
UTF_32,
|
||||
UTF_Wide,
|
||||
}
|
||||
|
||||
Token_Value :: union {
|
||||
i64,
|
||||
f64,
|
||||
string,
|
||||
[]u16,
|
||||
[]u32,
|
||||
}
|
||||
|
||||
Token :: struct {
|
||||
kind: Token_Kind,
|
||||
next: ^Token,
|
||||
lit: string,
|
||||
|
||||
pos: Pos,
|
||||
file: ^File,
|
||||
line_delta: int,
|
||||
at_bol: bool,
|
||||
has_space: bool,
|
||||
|
||||
type_hint: Token_Type_Hint,
|
||||
val: Token_Value,
|
||||
prefix: string,
|
||||
|
||||
// Preprocessor values
|
||||
hide_set: ^Hide_Set,
|
||||
origin: ^Token,
|
||||
}
|
||||
|
||||
Is_Keyword_Proc :: #type proc(tok: ^Token) -> bool;
|
||||
|
||||
copy_token :: proc(tok: ^Token) -> ^Token {
|
||||
t := new_clone(tok^);
|
||||
t.next = nil;
|
||||
return t;
|
||||
}
|
||||
|
||||
new_eof :: proc(tok: ^Token) -> ^Token {
|
||||
t := new_clone(tok^);
|
||||
t.kind = .EOF;
|
||||
t.lit = "";
|
||||
return t;
|
||||
}
|
||||
|
||||
default_is_keyword :: proc(tok: ^Token) -> bool {
|
||||
if tok.kind == .Keyword {
|
||||
return true;
|
||||
}
|
||||
if len(tok.lit) > 0 {
|
||||
return default_keyword_set[tok.lit];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
token_name := [Token_Kind]string {
|
||||
.Invalid = "invalid",
|
||||
.Ident = "ident",
|
||||
.Punct = "punct",
|
||||
.Keyword = "keyword",
|
||||
.Char = "char",
|
||||
.String = "string",
|
||||
.Number = "number",
|
||||
.PP_Number = "preprocessor number",
|
||||
.Comment = "comment",
|
||||
.EOF = "eof",
|
||||
};
|
||||
|
||||
default_keyword_set := map[string]bool{
|
||||
"auto" = true,
|
||||
"break" = true,
|
||||
"case" = true,
|
||||
"char" = true,
|
||||
"const" = true,
|
||||
"continue" = true,
|
||||
"default" = true,
|
||||
"do" = true,
|
||||
"double" = true,
|
||||
"else" = true,
|
||||
"enum" = true,
|
||||
"extern" = true,
|
||||
"float" = true,
|
||||
"for" = true,
|
||||
"goto" = true,
|
||||
"if" = true,
|
||||
"int" = true,
|
||||
"long" = true,
|
||||
"register" = true,
|
||||
"restrict" = true,
|
||||
"return" = true,
|
||||
"short" = true,
|
||||
"signed" = true,
|
||||
"sizeof" = true,
|
||||
"static" = true,
|
||||
"struct" = true,
|
||||
"switch" = true,
|
||||
"typedef" = true,
|
||||
"union" = true,
|
||||
"unsigned" = true,
|
||||
"void" = true,
|
||||
"volatile" = true,
|
||||
"while" = true,
|
||||
"_Alignas" = true,
|
||||
"_Alignof" = true,
|
||||
"_Atomic" = true,
|
||||
"_Bool" = true,
|
||||
"_Generic" = true,
|
||||
"_Noreturn" = true,
|
||||
"_Thread_local" = true,
|
||||
"__restrict" = true,
|
||||
"typeof" = true,
|
||||
"asm" = true,
|
||||
"__restrict__" = true,
|
||||
"__thread" = true,
|
||||
"__attribute__" = true,
|
||||
};
|
||||
@@ -0,0 +1,667 @@
|
||||
package c_frontend_tokenizer
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
|
||||
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
|
||||
|
||||
|
||||
Tokenizer :: struct {
|
||||
// Immutable data
|
||||
path: string,
|
||||
src: []byte,
|
||||
|
||||
|
||||
// Tokenizing state
|
||||
ch: rune,
|
||||
offset: int,
|
||||
read_offset: int,
|
||||
line_offset: int,
|
||||
line_count: int,
|
||||
|
||||
// Extra information for tokens
|
||||
at_bol: bool,
|
||||
has_space: bool,
|
||||
|
||||
// Mutable data
|
||||
err: Error_Handler,
|
||||
warn: Error_Handler,
|
||||
error_count: int,
|
||||
warning_count: int,
|
||||
}
|
||||
|
||||
init_defaults :: proc(t: ^Tokenizer, err: Error_Handler = default_error_handler, warn: Error_Handler = default_warn_handler) {
|
||||
t.err = err;
|
||||
t.warn = warn;
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> (pos: Pos) {
|
||||
pos.file = t.path;
|
||||
pos.offset = offset;
|
||||
pos.line = t.line_count;
|
||||
pos.column = offset - t.line_offset + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
default_warn_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
||||
fmt.eprintf("%s(%d:%d) warning: ", pos.file, pos.line, pos.column);
|
||||
fmt.eprintf(msg, ..args);
|
||||
fmt.eprintf("\n");
|
||||
}
|
||||
|
||||
error_offset :: 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;
|
||||
}
|
||||
|
||||
warn_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
||||
pos := offset_to_pos(t, offset);
|
||||
if t.warn != nil {
|
||||
t.warn(pos, msg, ..args);
|
||||
}
|
||||
t.warning_count += 1;
|
||||
}
|
||||
|
||||
error :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
|
||||
pos := tok.pos;
|
||||
if t.err != nil {
|
||||
t.err(pos, msg, ..args);
|
||||
}
|
||||
t.error_count += 1;
|
||||
}
|
||||
|
||||
warn :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
|
||||
pos := tok.pos;
|
||||
if t.warn != nil {
|
||||
t.warn(pos, msg, ..args);
|
||||
}
|
||||
t.warning_count += 1;
|
||||
}
|
||||
|
||||
|
||||
advance_rune :: proc(t: ^Tokenizer) {
|
||||
if t.read_offset < len(t.src) {
|
||||
t.offset = t.read_offset;
|
||||
if t.ch == '\n' {
|
||||
t.at_bol = true;
|
||||
t.line_offset = t.offset;
|
||||
t.line_count += 1;
|
||||
}
|
||||
r, w := rune(t.src[t.read_offset]), 1;
|
||||
switch {
|
||||
case r == 0:
|
||||
error_offset(t, t.offset, "illegal character NUL");
|
||||
case r >= utf8.RUNE_SELF:
|
||||
r, w = utf8.decode_rune(t.src[t.read_offset:]);
|
||||
if r == utf8.RUNE_ERROR && w == 1 {
|
||||
error_offset(t, t.offset, "illegal UTF-8 encoding");
|
||||
} else if r == utf8.RUNE_BOM && t.offset > 0 {
|
||||
error_offset(t, t.offset, "illegal byte order mark");
|
||||
}
|
||||
}
|
||||
t.read_offset += w;
|
||||
t.ch = r;
|
||||
} else {
|
||||
t.offset = len(t.src);
|
||||
if t.ch == '\n' {
|
||||
t.at_bol = true;
|
||||
t.line_offset = t.offset;
|
||||
t.line_count += 1;
|
||||
}
|
||||
t.ch = -1;
|
||||
}
|
||||
}
|
||||
|
||||
advance_rune_n :: proc(t: ^Tokenizer, n: int) {
|
||||
for in 0..<n {
|
||||
advance_rune(t);
|
||||
}
|
||||
}
|
||||
|
||||
is_digit :: proc(r: rune) -> bool {
|
||||
return '0' <= r && r <= '9';
|
||||
}
|
||||
|
||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||
for {
|
||||
switch t.ch {
|
||||
case ' ', '\t', '\r', '\v', '\f', '\n':
|
||||
t.has_space = true;
|
||||
advance_rune(t);
|
||||
case:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan_comment :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1;
|
||||
next := -1;
|
||||
general: {
|
||||
if t.ch == '/'{ // line comments
|
||||
advance_rune(t);
|
||||
for t.ch != '\n' && t.ch >= 0 {
|
||||
advance_rune(t);
|
||||
}
|
||||
|
||||
next = t.offset;
|
||||
if t.ch == '\n' {
|
||||
next += 1;
|
||||
}
|
||||
break general;
|
||||
}
|
||||
|
||||
/* style comment */
|
||||
advance_rune(t);
|
||||
for t.ch >= 0 {
|
||||
ch := t.ch;
|
||||
advance_rune(t);
|
||||
if ch == '*' && t.ch == '/' {
|
||||
advance_rune(t);
|
||||
next = t.offset;
|
||||
break general;
|
||||
}
|
||||
}
|
||||
|
||||
error_offset(t, offset, "comment not terminated");
|
||||
}
|
||||
|
||||
lit := t.src[offset : t.offset];
|
||||
|
||||
// NOTE(bill): Strip CR for line comments
|
||||
for len(lit) > 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
|
||||
lit = lit[:len(lit)-1];
|
||||
}
|
||||
|
||||
|
||||
return string(lit);
|
||||
}
|
||||
|
||||
scan_identifier :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset;
|
||||
|
||||
for is_ident1(t.ch) {
|
||||
advance_rune(t);
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
scan_string :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1;
|
||||
|
||||
for {
|
||||
ch := t.ch;
|
||||
if ch == '\n' || ch < 0 {
|
||||
error_offset(t, offset, "string literal was not terminated");
|
||||
break;
|
||||
}
|
||||
advance_rune(t);
|
||||
if ch == '"' {
|
||||
break;
|
||||
}
|
||||
if ch == '\\' {
|
||||
scan_escape(t);
|
||||
}
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
digit_val :: proc(r: rune) -> int {
|
||||
switch r {
|
||||
case '0'..='9':
|
||||
return int(r-'0');
|
||||
case 'A'..='F':
|
||||
return int(r-'A' + 10);
|
||||
case 'a'..='f':
|
||||
return int(r-'a' + 10);
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
scan_escape :: proc(t: ^Tokenizer) -> bool {
|
||||
offset := t.offset;
|
||||
|
||||
esc := t.ch;
|
||||
n: int;
|
||||
base, max: u32;
|
||||
switch esc {
|
||||
case 'a', 'b', 'e', 'f', 'n', 't', 'v', 'r', '\\', '\'', '"':
|
||||
advance_rune(t);
|
||||
return true;
|
||||
|
||||
case '0'..='7':
|
||||
for digit_val(t.ch) < 8 {
|
||||
advance_rune(t);
|
||||
}
|
||||
return true;
|
||||
case 'x':
|
||||
advance_rune(t);
|
||||
for digit_val(t.ch) < 16 {
|
||||
advance_rune(t);
|
||||
}
|
||||
return true;
|
||||
case 'u':
|
||||
advance_rune(t);
|
||||
n, base, max = 4, 16, utf8.MAX_RUNE;
|
||||
case 'U':
|
||||
advance_rune(t);
|
||||
n, base, max = 8, 16, utf8.MAX_RUNE;
|
||||
case:
|
||||
if t.ch < 0 {
|
||||
error_offset(t, offset, "escape sequence was not terminated");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
x: u32;
|
||||
main_loop: for n > 0 {
|
||||
d := u32(digit_val(t.ch));
|
||||
if d >= base {
|
||||
if t.ch == '"' || t.ch == '\'' {
|
||||
break main_loop;
|
||||
}
|
||||
if t.ch < 0 {
|
||||
error_offset(t, t.offset, "escape sequence was not terminated");
|
||||
} else {
|
||||
error_offset(t, t.offset, "illegal character '%r' : %d in escape sequence", t.ch, t.ch);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
x = x*base + d;
|
||||
advance_rune(t);
|
||||
n -= 1;
|
||||
}
|
||||
|
||||
if x > max || 0xd800 <= x && x <= 0xe000 {
|
||||
error_offset(t, offset, "escape sequence is an invalid Unicode code point");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
scan_rune :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1;
|
||||
valid := true;
|
||||
n := 0;
|
||||
for {
|
||||
ch := t.ch;
|
||||
if ch == '\n' || ch < 0 {
|
||||
if valid {
|
||||
error_offset(t, offset, "rune literal not terminated");
|
||||
valid = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
advance_rune(t);
|
||||
if ch == '\'' {
|
||||
break;
|
||||
}
|
||||
n += 1;
|
||||
if ch == '\\' {
|
||||
if !scan_escape(t) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if valid && n != 1 {
|
||||
error_offset(t, offset, "illegal rune literal");
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) {
|
||||
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
|
||||
for digit_val(t.ch) < base {
|
||||
advance_rune(t);
|
||||
}
|
||||
}
|
||||
scan_exponent :: proc(t: ^Tokenizer) {
|
||||
if t.ch == 'e' || t.ch == 'E' || t.ch == 'p' || t.ch == 'P' {
|
||||
advance_rune(t);
|
||||
if t.ch == '-' || t.ch == '+' {
|
||||
advance_rune(t);
|
||||
}
|
||||
if digit_val(t.ch) < 10 {
|
||||
scan_mantissa(t, 10);
|
||||
} else {
|
||||
error_offset(t, t.offset, "illegal floating-point exponent");
|
||||
}
|
||||
}
|
||||
}
|
||||
scan_fraction :: proc(t: ^Tokenizer) -> (early_exit: bool) {
|
||||
if t.ch == '.' && peek(t) == '.' {
|
||||
return true;
|
||||
}
|
||||
if t.ch == '.' {
|
||||
advance_rune(t);
|
||||
scan_mantissa(t, 10);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
check_end := true;
|
||||
|
||||
|
||||
offset := t.offset;
|
||||
seen_point := seen_decimal_point;
|
||||
|
||||
if seen_point {
|
||||
offset -= 1;
|
||||
scan_mantissa(t, 10);
|
||||
scan_exponent(t);
|
||||
} else {
|
||||
if t.ch == '0' {
|
||||
int_base :: proc(t: ^Tokenizer, base: int, msg: string) {
|
||||
prev := t.offset;
|
||||
advance_rune(t);
|
||||
scan_mantissa(t, base);
|
||||
if t.offset - prev <= 1 {
|
||||
error_offset(t, t.offset, msg);
|
||||
}
|
||||
}
|
||||
|
||||
advance_rune(t);
|
||||
switch t.ch {
|
||||
case 'b', 'B':
|
||||
int_base(t, 2, "illegal binary integer");
|
||||
case 'x', 'X':
|
||||
int_base(t, 16, "illegal hexadecimal integer");
|
||||
case:
|
||||
seen_point = false;
|
||||
scan_mantissa(t, 10);
|
||||
if t.ch == '.' {
|
||||
seen_point = true;
|
||||
if scan_fraction(t) {
|
||||
check_end = false;
|
||||
}
|
||||
}
|
||||
if check_end {
|
||||
scan_exponent(t);
|
||||
check_end = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if check_end {
|
||||
scan_mantissa(t, 10);
|
||||
|
||||
if !scan_fraction(t) {
|
||||
scan_exponent(t);
|
||||
}
|
||||
}
|
||||
|
||||
return .Number, string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
scan_punct :: proc(t: ^Tokenizer, ch: rune) -> (kind: Token_Kind) {
|
||||
kind = .Punct;
|
||||
switch ch {
|
||||
case:
|
||||
kind = .Invalid;
|
||||
|
||||
case '<', '>':
|
||||
if t.ch == ch {
|
||||
advance_rune(t);
|
||||
}
|
||||
if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
}
|
||||
case '!', '+', '-', '*', '/', '%', '^', '=':
|
||||
if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
}
|
||||
case '#':
|
||||
if t.ch == '#' {
|
||||
advance_rune(t);
|
||||
}
|
||||
case '&':
|
||||
if t.ch == '=' || t.ch == '&' {
|
||||
advance_rune(t);
|
||||
}
|
||||
case '|':
|
||||
if t.ch == '=' || t.ch == '|' {
|
||||
advance_rune(t);
|
||||
}
|
||||
case '(', ')', '[', ']', '{', '}':
|
||||
// okay
|
||||
case '~', ',', ':', ';', '?':
|
||||
// okay
|
||||
case '`':
|
||||
// okay
|
||||
case '.':
|
||||
if t.ch == '.' && peek(t) == '.' {
|
||||
advance_rune(t);
|
||||
advance_rune(t); // consume last '.'
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
peek :: proc(t: ^Tokenizer) -> byte {
|
||||
if t.read_offset < len(t.src) {
|
||||
return t.src[t.read_offset];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
peek_str :: proc(t: ^Tokenizer, str: string) -> bool {
|
||||
if t.read_offset < len(t.src) {
|
||||
return strings.has_prefix(string(t.src[t.offset:]), str);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
scan_literal_prefix :: proc(t: ^Tokenizer, str: string, prefix: ^string) -> bool {
|
||||
if peek_str(t, str) {
|
||||
offset := t.offset;
|
||||
for _ in str {
|
||||
advance_rune(t);
|
||||
}
|
||||
prefix^ = string(t.src[offset:][:len(str)-1]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
allow_next_to_be_newline :: proc(t: ^Tokenizer) -> bool {
|
||||
if t.ch == '\n' {
|
||||
advance_rune(t);
|
||||
return true;
|
||||
} else if t.ch == '\r' && peek(t) == '\n' { // allow for MS-DOS style line endings
|
||||
advance_rune(t); // \r
|
||||
advance_rune(t); // \n
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
scan :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
|
||||
skip_whitespace(t);
|
||||
|
||||
offset := t.offset;
|
||||
|
||||
kind: Token_Kind;
|
||||
lit: string;
|
||||
prefix: string;
|
||||
|
||||
switch ch := t.ch; {
|
||||
case scan_literal_prefix(t, `u8"`, &prefix):
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
case scan_literal_prefix(t, `u"`, &prefix):
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
case scan_literal_prefix(t, `L"`, &prefix):
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
case scan_literal_prefix(t, `U"`, &prefix):
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
case scan_literal_prefix(t, `u'`, &prefix):
|
||||
kind = .Char;
|
||||
lit = scan_rune(t);
|
||||
case scan_literal_prefix(t, `L'`, &prefix):
|
||||
kind = .Char;
|
||||
lit = scan_rune(t);
|
||||
case scan_literal_prefix(t, `U'`, &prefix):
|
||||
kind = .Char;
|
||||
lit = scan_rune(t);
|
||||
|
||||
case is_ident0(ch):
|
||||
lit = scan_identifier(t);
|
||||
kind = .Ident;
|
||||
case '0' <= ch && ch <= '9':
|
||||
kind, lit = scan_number(t, false);
|
||||
case:
|
||||
advance_rune(t);
|
||||
switch ch {
|
||||
case -1:
|
||||
kind = .EOF;
|
||||
case '\\':
|
||||
kind = .Punct;
|
||||
if allow_next_to_be_newline(t) {
|
||||
t.at_bol = true;
|
||||
t.has_space = false;
|
||||
return scan(t, f);
|
||||
}
|
||||
|
||||
case '.':
|
||||
if is_digit(t.ch) {
|
||||
kind, lit = scan_number(t, true);
|
||||
} else {
|
||||
kind = scan_punct(t, ch);
|
||||
}
|
||||
case '"':
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
case '\'':
|
||||
kind = .Char;
|
||||
lit = scan_rune(t);
|
||||
case '/':
|
||||
if t.ch == '/' || t.ch == '*' {
|
||||
kind = .Comment;
|
||||
lit = scan_comment(t);
|
||||
t.has_space = true;
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
case:
|
||||
kind = scan_punct(t, ch);
|
||||
if kind == .Invalid && ch != utf8.RUNE_BOM {
|
||||
error_offset(t, t.offset, "illegal character '%r': %d", ch, ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if lit == "" {
|
||||
lit = string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
if kind == .Comment {
|
||||
return scan(t, f);
|
||||
}
|
||||
|
||||
tok := new(Token);
|
||||
tok.kind = kind;
|
||||
tok.lit = lit;
|
||||
tok.pos = offset_to_pos(t, offset);
|
||||
tok.file = f;
|
||||
tok.prefix = prefix;
|
||||
tok.at_bol = t.at_bol;
|
||||
tok.has_space = t.has_space;
|
||||
|
||||
t.at_bol, t.has_space = false, false;
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
tokenize :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
|
||||
setup_tokenizer: {
|
||||
t.src = f.src;
|
||||
t.ch = ' ';
|
||||
t.offset = 0;
|
||||
t.read_offset = 0;
|
||||
t.line_offset = 0;
|
||||
t.line_count = len(t.src) > 0 ? 1 : 0;
|
||||
t.error_count = 0;
|
||||
t.path = f.name;
|
||||
|
||||
|
||||
advance_rune(t);
|
||||
if t.ch == utf8.RUNE_BOM {
|
||||
advance_rune(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
t.at_bol = true;
|
||||
t.has_space = false;
|
||||
|
||||
head: Token;
|
||||
curr := &head;
|
||||
for {
|
||||
tok := scan(t, f);
|
||||
if tok == nil {
|
||||
break;
|
||||
}
|
||||
curr.next = tok;
|
||||
curr = curr.next;
|
||||
if tok.kind == .EOF {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return head.next;
|
||||
}
|
||||
|
||||
add_new_file :: proc(t: ^Tokenizer, name: string, src: []byte, id: int) -> ^File {
|
||||
file := new(File);
|
||||
file.id = id;
|
||||
file.src = src;
|
||||
file.name = name;
|
||||
file.display_name = name;
|
||||
return file;
|
||||
}
|
||||
|
||||
tokenize_file :: proc(t: ^Tokenizer, path: string, id: int, loc := #caller_location) -> ^Token {
|
||||
src, ok := os.read_entire_file(path);
|
||||
if !ok {
|
||||
return nil;
|
||||
}
|
||||
return tokenize(t, add_new_file(t, path, src, id));
|
||||
}
|
||||
|
||||
|
||||
inline_tokenize :: proc(t: ^Tokenizer, tok: ^Token, src: []byte) -> ^Token {
|
||||
file := new(File);
|
||||
file.src = src;
|
||||
if tok.file != nil {
|
||||
file.id = tok.file.id;
|
||||
file.name = tok.file.name;
|
||||
file.display_name = tok.file.name;
|
||||
}
|
||||
|
||||
return tokenize(t, file);
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package c_frontend_tokenizer
|
||||
|
||||
|
||||
in_range :: proc(range: []rune, c: rune) -> bool #no_bounds_check {
|
||||
for i := 0; range[i] != -1; i += 2 {
|
||||
if range[i] <= c && c <= range[i+1] {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// [https://www.sigbus.info/n1570#D] C11 allows ASCII and some multibyte characters in certan Unicode ranges to be used in an identifier.
|
||||
//
|
||||
// is_ident0 returns true if a given character is acceptable as the first character of an identifier.
|
||||
is_ident0 :: proc(c: rune) -> bool {
|
||||
return in_range(_range_ident0, c);
|
||||
}
|
||||
// is_ident0 returns true if a given character is acceptable as a non-first character of an identifier.
|
||||
is_ident1 :: proc(c: rune) -> bool {
|
||||
return is_ident0(c) || in_range(_range_ident1, c);
|
||||
}
|
||||
|
||||
// Returns the number of columns needed to display a given character in a fixed-width font.
|
||||
// Based on https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
||||
char_width :: proc(c: rune) -> int {
|
||||
switch {
|
||||
case in_range(_range_width0, c):
|
||||
return 0;
|
||||
case in_range(_range_width2, c):
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
display_width :: proc(str: string) -> (w: int) {
|
||||
for c in str {
|
||||
w += char_width(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
_range_ident0 := []rune{
|
||||
'_', '_', 'a', 'z', 'A', 'Z', '$', '$',
|
||||
0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF,
|
||||
0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6,
|
||||
0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F,
|
||||
0x1681, 0x180D, 0x180F, 0x1DBF, 0x1E00, 0x1FFF, 0x200B, 0x200D,
|
||||
0x202A, 0x202E, 0x203F, 0x2040, 0x2054, 0x2054, 0x2060, 0x206F,
|
||||
0x2070, 0x20CF, 0x2100, 0x218F, 0x2460, 0x24FF, 0x2776, 0x2793,
|
||||
0x2C00, 0x2DFF, 0x2E80, 0x2FFF, 0x3004, 0x3007, 0x3021, 0x302F,
|
||||
0x3031, 0x303F, 0x3040, 0xD7FF, 0xF900, 0xFD3D, 0xFD40, 0xFDCF,
|
||||
0xFDF0, 0xFE1F, 0xFE30, 0xFE44, 0xFE47, 0xFFFD,
|
||||
0x10000, 0x1FFFD, 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD,
|
||||
0x50000, 0x5FFFD, 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD,
|
||||
0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD,
|
||||
0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD,
|
||||
-1,
|
||||
};
|
||||
|
||||
_range_ident1 := []rune{
|
||||
'0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F,
|
||||
-1,
|
||||
};
|
||||
|
||||
|
||||
_range_width0 := []rune{
|
||||
0x0000, 0x001F, 0x007f, 0x00a0, 0x0300, 0x036F, 0x0483, 0x0486,
|
||||
0x0488, 0x0489, 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2,
|
||||
0x05C4, 0x05C5, 0x05C7, 0x05C7, 0x0600, 0x0603, 0x0610, 0x0615,
|
||||
0x064B, 0x065E, 0x0670, 0x0670, 0x06D6, 0x06E4, 0x06E7, 0x06E8,
|
||||
0x06EA, 0x06ED, 0x070F, 0x070F, 0x0711, 0x0711, 0x0730, 0x074A,
|
||||
0x07A6, 0x07B0, 0x07EB, 0x07F3, 0x0901, 0x0902, 0x093C, 0x093C,
|
||||
0x0941, 0x0948, 0x094D, 0x094D, 0x0951, 0x0954, 0x0962, 0x0963,
|
||||
0x0981, 0x0981, 0x09BC, 0x09BC, 0x09C1, 0x09C4, 0x09CD, 0x09CD,
|
||||
0x09E2, 0x09E3, 0x0A01, 0x0A02, 0x0A3C, 0x0A3C, 0x0A41, 0x0A42,
|
||||
0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A82,
|
||||
0x0ABC, 0x0ABC, 0x0AC1, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0ACD,
|
||||
0x0AE2, 0x0AE3, 0x0B01, 0x0B01, 0x0B3C, 0x0B3C, 0x0B3F, 0x0B3F,
|
||||
0x0B41, 0x0B43, 0x0B4D, 0x0B4D, 0x0B56, 0x0B56, 0x0B82, 0x0B82,
|
||||
0x0BC0, 0x0BC0, 0x0BCD, 0x0BCD, 0x0C3E, 0x0C40, 0x0C46, 0x0C48,
|
||||
0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0CBC, 0x0CBC, 0x0CBF, 0x0CBF,
|
||||
0x0CC6, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D41, 0x0D43,
|
||||
0x0D4D, 0x0D4D, 0x0DCA, 0x0DCA, 0x0DD2, 0x0DD4, 0x0DD6, 0x0DD6,
|
||||
0x0E31, 0x0E31, 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1,
|
||||
0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19,
|
||||
0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F71, 0x0F7E,
|
||||
0x0F80, 0x0F84, 0x0F86, 0x0F87, 0x0F90, 0x0F97, 0x0F99, 0x0FBC,
|
||||
0x0FC6, 0x0FC6, 0x102D, 0x1030, 0x1032, 0x1032, 0x1036, 0x1037,
|
||||
0x1039, 0x1039, 0x1058, 0x1059, 0x1160, 0x11FF, 0x135F, 0x135F,
|
||||
0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 0x1772, 0x1773,
|
||||
0x17B4, 0x17B5, 0x17B7, 0x17BD, 0x17C6, 0x17C6, 0x17C9, 0x17D3,
|
||||
0x17DD, 0x17DD, 0x180B, 0x180D, 0x18A9, 0x18A9, 0x1920, 0x1922,
|
||||
0x1927, 0x1928, 0x1932, 0x1932, 0x1939, 0x193B, 0x1A17, 0x1A18,
|
||||
0x1B00, 0x1B03, 0x1B34, 0x1B34, 0x1B36, 0x1B3A, 0x1B3C, 0x1B3C,
|
||||
0x1B42, 0x1B42, 0x1B6B, 0x1B73, 0x1DC0, 0x1DCA, 0x1DFE, 0x1DFF,
|
||||
0x200B, 0x200F, 0x202A, 0x202E, 0x2060, 0x2063, 0x206A, 0x206F,
|
||||
0x20D0, 0x20EF, 0x302A, 0x302F, 0x3099, 0x309A, 0xA806, 0xA806,
|
||||
0xA80B, 0xA80B, 0xA825, 0xA826, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F,
|
||||
0xFE20, 0xFE23, 0xFEFF, 0xFEFF, 0xFFF9, 0xFFFB, 0x10A01, 0x10A03,
|
||||
0x10A05, 0x10A06, 0x10A0C, 0x10A0F, 0x10A38, 0x10A3A, 0x10A3F, 0x10A3F,
|
||||
0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD,
|
||||
0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF,
|
||||
-1,
|
||||
};
|
||||
|
||||
_range_width2 := []rune{
|
||||
0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E,
|
||||
0x3040, 0xA4CF, 0xAC00, 0xD7A3, 0xF900, 0xFAFF, 0xFE10, 0xFE19,
|
||||
0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644,
|
||||
0x20000, 0x2FFFD, 0x30000, 0x3FFFD,
|
||||
-1,
|
||||
};
|
||||
@@ -0,0 +1,478 @@
|
||||
package compress
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
*/
|
||||
|
||||
import "core:io"
|
||||
import "core:image"
|
||||
import "core:bytes"
|
||||
|
||||
/*
|
||||
These settings bound how much compression algorithms will allocate for their output buffer.
|
||||
If streaming their output, these are unnecessary and will be ignored.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
When a decompression routine doesn't stream its output, but writes to a buffer,
|
||||
we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
|
||||
*/
|
||||
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20));
|
||||
|
||||
/*
|
||||
This bounds the maximum a buffer will resize to as needed, or the maximum we'll
|
||||
pre-allocate if you inform the decompression routine you know the payload size.
|
||||
|
||||
For reference, the largest payload size of a GZIP file is 4 GiB.
|
||||
|
||||
*/
|
||||
when size_of(uintptr) == 8 {
|
||||
/*
|
||||
For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
||||
which is GZIP and PKZIP's max payload size.
|
||||
*/
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32));
|
||||
} else {
|
||||
/*
|
||||
For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
||||
*/
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29));
|
||||
}
|
||||
|
||||
|
||||
Error :: union {
|
||||
General_Error,
|
||||
Deflate_Error,
|
||||
ZLIB_Error,
|
||||
GZIP_Error,
|
||||
ZIP_Error,
|
||||
/*
|
||||
This is here because png.load will return a this type of error union,
|
||||
as it may involve an I/O error, a Deflate error, etc.
|
||||
*/
|
||||
image.Error,
|
||||
}
|
||||
|
||||
General_Error :: enum {
|
||||
File_Not_Found,
|
||||
Cannot_Open_File,
|
||||
File_Too_Short,
|
||||
Stream_Too_Short,
|
||||
Output_Too_Short,
|
||||
Unknown_Compression_Method,
|
||||
Checksum_Failed,
|
||||
Incompatible_Options,
|
||||
Unimplemented,
|
||||
|
||||
|
||||
/*
|
||||
Memory errors
|
||||
*/
|
||||
Allocation_Failed,
|
||||
Resize_Failed,
|
||||
}
|
||||
|
||||
GZIP_Error :: enum {
|
||||
Invalid_GZIP_Signature,
|
||||
Reserved_Flag_Set,
|
||||
Invalid_Extra_Data,
|
||||
Original_Name_Too_Long,
|
||||
Comment_Too_Long,
|
||||
Payload_Length_Invalid,
|
||||
Payload_CRC_Invalid,
|
||||
|
||||
/*
|
||||
GZIP's payload can be a maximum of max(u32le), or 4 GiB.
|
||||
If you tell it you expect it to contain more, that's obviously an error.
|
||||
*/
|
||||
Payload_Size_Exceeds_Max_Payload,
|
||||
/*
|
||||
For buffered instead of streamed output, the payload size can't exceed
|
||||
the max set by the `COMPRESS_OUTPUT_ALLOCATE_MAX` switch in compress/common.odin.
|
||||
|
||||
You can tweak this setting using `-define:COMPRESS_OUTPUT_ALLOCATE_MAX=size_in_bytes`
|
||||
*/
|
||||
Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX,
|
||||
|
||||
}
|
||||
|
||||
ZIP_Error :: enum {
|
||||
Invalid_ZIP_File_Signature,
|
||||
Unexpected_Signature,
|
||||
Insert_Next_Disk,
|
||||
Expected_End_of_Central_Directory_Record,
|
||||
}
|
||||
|
||||
ZLIB_Error :: enum {
|
||||
Unsupported_Window_Size,
|
||||
FDICT_Unsupported,
|
||||
Unsupported_Compression_Level,
|
||||
Code_Buffer_Malformed,
|
||||
}
|
||||
|
||||
Deflate_Error :: enum {
|
||||
Huffman_Bad_Sizes,
|
||||
Huffman_Bad_Code_Lengths,
|
||||
Inflate_Error,
|
||||
Bad_Distance,
|
||||
Bad_Huffman_Code,
|
||||
Len_Nlen_Mismatch,
|
||||
BType_3,
|
||||
}
|
||||
|
||||
|
||||
// General I/O context for ZLIB, LZW, etc.
|
||||
Context_Memory_Input :: struct #packed {
|
||||
input_data: []u8,
|
||||
output: ^bytes.Buffer,
|
||||
bytes_written: i64,
|
||||
|
||||
code_buffer: u64,
|
||||
num_bits: u64,
|
||||
|
||||
/*
|
||||
If we know the data size, we can optimize the reads and writes.
|
||||
*/
|
||||
size_packed: i64,
|
||||
size_unpacked: i64,
|
||||
}
|
||||
#assert(size_of(Context_Memory_Input) == 64);
|
||||
|
||||
Context_Stream_Input :: struct #packed {
|
||||
input_data: []u8,
|
||||
input: io.Stream,
|
||||
output: ^bytes.Buffer,
|
||||
bytes_written: i64,
|
||||
|
||||
code_buffer: u64,
|
||||
num_bits: u64,
|
||||
|
||||
/*
|
||||
If we know the data size, we can optimize the reads and writes.
|
||||
*/
|
||||
size_packed: i64,
|
||||
size_unpacked: i64,
|
||||
|
||||
/*
|
||||
Flags:
|
||||
`input_fully_in_memory`
|
||||
true = This tells us we read input from `input_data` exclusively. [] = EOF.
|
||||
false = Try to refill `input_data` from the `input` stream.
|
||||
*/
|
||||
input_fully_in_memory: b8,
|
||||
|
||||
padding: [1]u8,
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: The stream versions should really only check if a certain method is available once, perhaps even during setup.
|
||||
|
||||
Bit and byte readers may be merged so that reading bytes will grab them from the bit buffer first.
|
||||
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) {
|
||||
return i64(len(z.input_data)), nil;
|
||||
}
|
||||
|
||||
input_size_from_stream :: proc(z: ^Context_Stream_Input) -> (res: i64, err: Error) {
|
||||
return io.size(z.input), nil;
|
||||
}
|
||||
|
||||
input_size :: proc{input_size_from_memory, input_size_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= size {
|
||||
res = z.input_data[:size];
|
||||
z.input_data = z.input_data[size:];
|
||||
return res, .None;
|
||||
}
|
||||
}
|
||||
|
||||
if len(z.input_data) == 0 {
|
||||
return []u8{}, .EOF;
|
||||
} else {
|
||||
return []u8{}, .Short_Buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||
b := make([]u8, size, context.temp_allocator);
|
||||
_, e := z.input->impl_read(b[:]);
|
||||
if e == .None {
|
||||
return b, .None;
|
||||
}
|
||||
|
||||
return []u8{}, e;
|
||||
}
|
||||
|
||||
read_slice :: proc{read_slice_from_memory, read_slice_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
|
||||
b, e := read_slice(z, size_of(T));
|
||||
if e == .None {
|
||||
return (^T)(&b[0])^, .None;
|
||||
}
|
||||
|
||||
return T{}, e;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) {
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= 1 {
|
||||
res = z.input_data[0];
|
||||
z.input_data = z.input_data[1:];
|
||||
return res, .None;
|
||||
}
|
||||
}
|
||||
return 0, .EOF;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) {
|
||||
b, e := read_slice_from_stream(z, 1);
|
||||
if e == .None {
|
||||
return b[0], .None;
|
||||
}
|
||||
|
||||
return 0, e;
|
||||
}
|
||||
|
||||
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream};
|
||||
|
||||
/*
|
||||
You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
||||
preferentially.
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
||||
if z.num_bits >= 8 {
|
||||
res = u8(read_bits_no_refill_lsb(z, 8));
|
||||
} else {
|
||||
size, _ := input_size(z);
|
||||
if size > 0 {
|
||||
res, err = read_u8(z);
|
||||
} else {
|
||||
err = .EOF;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T);
|
||||
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= size {
|
||||
buf := z.input_data[:size];
|
||||
return (^T)(&buf[0])^, .None;
|
||||
}
|
||||
}
|
||||
|
||||
if len(z.input_data) == 0 {
|
||||
return T{}, .EOF;
|
||||
} else {
|
||||
return T{}, .Short_Buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T);
|
||||
|
||||
// Get current position to read from.
|
||||
curr, e1 := z.input->impl_seek(0, .Current);
|
||||
if e1 != .None {
|
||||
return T{}, e1;
|
||||
}
|
||||
r, e2 := io.to_reader_at(z.input);
|
||||
if !e2 {
|
||||
return T{}, .Empty;
|
||||
}
|
||||
when size <= 128 {
|
||||
b: [size]u8;
|
||||
} else {
|
||||
b := make([]u8, size, context.temp_allocator);
|
||||
}
|
||||
_, e3 := io.read_at(r, b[:], curr);
|
||||
if e3 != .None {
|
||||
return T{}, .Empty;
|
||||
}
|
||||
|
||||
res = (^T)(&b[0])^;
|
||||
return res, .None;
|
||||
}
|
||||
|
||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream};
|
||||
|
||||
|
||||
|
||||
// Sliding window read back
|
||||
@(optimization_mode="speed")
|
||||
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
|
||||
// Look back into the sliding window.
|
||||
return z.output.buf[z.bytes_written - offset], .None;
|
||||
}
|
||||
|
||||
// Generalized bit reader LSB
|
||||
@(optimization_mode="speed")
|
||||
refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) {
|
||||
refill := u64(width);
|
||||
b := u64(0);
|
||||
|
||||
if z.num_bits > refill {
|
||||
return;
|
||||
}
|
||||
|
||||
for {
|
||||
if len(z.input_data) != 0 {
|
||||
b = u64(z.input_data[0]);
|
||||
z.input_data = z.input_data[1:];
|
||||
} else {
|
||||
b = 0;
|
||||
}
|
||||
|
||||
z.code_buffer |= b << u8(z.num_bits);
|
||||
z.num_bits += 8;
|
||||
if z.num_bits > refill {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generalized bit reader LSB
|
||||
@(optimization_mode="speed")
|
||||
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
||||
refill := u64(width);
|
||||
|
||||
for {
|
||||
if z.num_bits > refill {
|
||||
break;
|
||||
}
|
||||
if z.code_buffer == 0 && z.num_bits > 63 {
|
||||
z.num_bits = 0;
|
||||
}
|
||||
if z.code_buffer >= 1 << uint(z.num_bits) {
|
||||
// Code buffer is malformed.
|
||||
z.num_bits = max(u64);
|
||||
return;
|
||||
}
|
||||
b, err := read_u8(z);
|
||||
if err != .None {
|
||||
// This is fine at the end of the file.
|
||||
return;
|
||||
}
|
||||
z.code_buffer |= (u64(b) << u8(z.num_bits));
|
||||
z.num_bits += 8;
|
||||
}
|
||||
}
|
||||
|
||||
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream};
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
|
||||
z.code_buffer >>= width;
|
||||
z.num_bits -= u64(width);
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) {
|
||||
z.code_buffer >>= width;
|
||||
z.num_bits -= u64(width);
|
||||
}
|
||||
|
||||
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
if z.num_bits < u64(width) {
|
||||
refill_lsb(z);
|
||||
}
|
||||
return u32(z.code_buffer & ~(~u64(0) << width));
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
if z.num_bits < u64(width) {
|
||||
refill_lsb(z);
|
||||
}
|
||||
return u32(z.code_buffer & ~(~u64(0) << width));
|
||||
}
|
||||
|
||||
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
assert(z.num_bits >= u64(width));
|
||||
return u32(z.code_buffer & ~(~u64(0) << width));
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
assert(z.num_bits >= u64(width));
|
||||
return u32(z.code_buffer & ~(~u64(0) << width));
|
||||
}
|
||||
|
||||
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
k := #force_inline peek_bits_lsb(z, width);
|
||||
#force_inline consume_bits_lsb(z, width);
|
||||
return k;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
k := peek_bits_lsb(z, width);
|
||||
consume_bits_lsb(z, width);
|
||||
return k;
|
||||
}
|
||||
|
||||
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
k := #force_inline peek_bits_no_refill_lsb(z, width);
|
||||
#force_inline consume_bits_lsb(z, width);
|
||||
return k;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
k := peek_bits_no_refill_lsb(z, width);
|
||||
consume_bits_lsb(z, width);
|
||||
return k;
|
||||
}
|
||||
|
||||
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream};
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
|
||||
discard := u8(z.num_bits & 7);
|
||||
#force_inline consume_bits_lsb(z, discard);
|
||||
}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
||||
discard := u8(z.num_bits & 7);
|
||||
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};
|
||||
@@ -0,0 +1,89 @@
|
||||
//+ignore
|
||||
package gzip
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
|
||||
A small GZIP implementation as an example.
|
||||
*/
|
||||
|
||||
import "core:bytes"
|
||||
import "core:os"
|
||||
import "core:compress"
|
||||
import "core:fmt"
|
||||
|
||||
// Small GZIP file with fextra, fname and fcomment present.
|
||||
@private
|
||||
TEST: []u8 = {
|
||||
0x1f, 0x8b, 0x08, 0x1c, 0xcb, 0x3b, 0x3a, 0x5a,
|
||||
0x02, 0x03, 0x07, 0x00, 0x61, 0x62, 0x03, 0x00,
|
||||
0x63, 0x64, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x00, 0x54, 0x68, 0x69, 0x73,
|
||||
0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f,
|
||||
0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2b, 0x48,
|
||||
0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x01, 0x00, 0x15,
|
||||
0x6a, 0x2c, 0x42, 0x07, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
main :: proc() {
|
||||
// Set up output buffer.
|
||||
buf := bytes.Buffer{};
|
||||
|
||||
stdout :: proc(s: string) {
|
||||
os.write_string(os.stdout, s);
|
||||
}
|
||||
stderr :: proc(s: string) {
|
||||
os.write_string(os.stderr, s);
|
||||
}
|
||||
|
||||
args := os.args;
|
||||
|
||||
if len(args) < 2 {
|
||||
stderr("No input file specified.\n");
|
||||
err := load(slice=TEST, buf=&buf, known_gzip_size=len(TEST));
|
||||
if err == nil {
|
||||
stdout("Displaying test vector: ");
|
||||
stdout(bytes.buffer_to_string(&buf));
|
||||
stdout("\n");
|
||||
} else {
|
||||
fmt.printf("gzip.load returned %v\n", err);
|
||||
}
|
||||
bytes.buffer_destroy(&buf);
|
||||
os.exit(0);
|
||||
}
|
||||
|
||||
// The rest are all files.
|
||||
args = args[1:];
|
||||
err: Error;
|
||||
|
||||
for file in args {
|
||||
if file == "-" {
|
||||
// Read from stdin
|
||||
s := os.stream_from_handle(os.stdin);
|
||||
ctx := &compress.Context_Stream_Input{
|
||||
input = s,
|
||||
};
|
||||
err = load(ctx, &buf);
|
||||
} else {
|
||||
err = load(file, &buf);
|
||||
}
|
||||
if err != nil {
|
||||
if err != E_General.File_Not_Found {
|
||||
stderr("File not found: ");
|
||||
stderr(file);
|
||||
stderr("\n");
|
||||
os.exit(1);
|
||||
}
|
||||
stderr("GZIP returned an error.\n");
|
||||
bytes.buffer_destroy(&buf);
|
||||
os.exit(2);
|
||||
}
|
||||
stdout(bytes.buffer_to_string(&buf));
|
||||
}
|
||||
bytes.buffer_destroy(&buf);
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
package gzip
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
|
||||
This package implements support for the GZIP file format v4.3,
|
||||
as specified in RFC 1952.
|
||||
|
||||
It is implemented in such a way that it lends itself naturally
|
||||
to be the input to a complementary TAR implementation.
|
||||
*/
|
||||
|
||||
import "core:compress/zlib"
|
||||
import "core:compress"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
import "core:bytes"
|
||||
import "core:hash"
|
||||
|
||||
Magic :: enum u16le {
|
||||
GZIP = 0x8b << 8 | 0x1f,
|
||||
}
|
||||
|
||||
Header :: struct #packed {
|
||||
magic: Magic,
|
||||
compression_method: Compression,
|
||||
flags: Header_Flags,
|
||||
modification_time: u32le,
|
||||
xfl: Compression_Flags,
|
||||
os: OS,
|
||||
}
|
||||
#assert(size_of(Header) == 10);
|
||||
|
||||
Header_Flag :: enum u8 {
|
||||
// Order is important
|
||||
text = 0,
|
||||
header_crc = 1,
|
||||
extra = 2,
|
||||
name = 3,
|
||||
comment = 4,
|
||||
reserved_1 = 5,
|
||||
reserved_2 = 6,
|
||||
reserved_3 = 7,
|
||||
}
|
||||
Header_Flags :: distinct bit_set[Header_Flag; u8];
|
||||
|
||||
OS :: enum u8 {
|
||||
FAT = 0,
|
||||
Amiga = 1,
|
||||
VMS = 2,
|
||||
Unix = 3,
|
||||
VM_CMS = 4,
|
||||
Atari_TOS = 5,
|
||||
HPFS = 6,
|
||||
Macintosh = 7,
|
||||
Z_System = 8,
|
||||
CP_M = 9,
|
||||
TOPS_20 = 10,
|
||||
NTFS = 11,
|
||||
QDOS = 12,
|
||||
Acorn_RISCOS = 13,
|
||||
_Unknown = 14,
|
||||
Unknown = 255,
|
||||
}
|
||||
OS_Name :: #partial [OS]string{
|
||||
.FAT = "FAT",
|
||||
.Amiga = "Amiga",
|
||||
.VMS = "VMS/OpenVMS",
|
||||
.Unix = "Unix",
|
||||
.VM_CMS = "VM/CMS",
|
||||
.Atari_TOS = "Atari TOS",
|
||||
.HPFS = "HPFS",
|
||||
.Macintosh = "Macintosh",
|
||||
.Z_System = "Z-System",
|
||||
.CP_M = "CP/M",
|
||||
.TOPS_20 = "TOPS-20",
|
||||
.NTFS = "NTFS",
|
||||
.QDOS = "QDOS",
|
||||
.Acorn_RISCOS = "Acorn RISCOS",
|
||||
.Unknown = "Unknown",
|
||||
};
|
||||
|
||||
Compression :: enum u8 {
|
||||
DEFLATE = 8,
|
||||
}
|
||||
|
||||
Compression_Flags :: enum u8 {
|
||||
Maximum_Compression = 2,
|
||||
Fastest_Compression = 4,
|
||||
}
|
||||
|
||||
Error :: compress.Error;
|
||||
E_General :: compress.General_Error;
|
||||
E_GZIP :: compress.GZIP_Error;
|
||||
E_ZLIB :: compress.ZLIB_Error;
|
||||
E_Deflate :: compress.Deflate_Error;
|
||||
|
||||
GZIP_MAX_PAYLOAD_SIZE :: int(max(u32le));
|
||||
|
||||
load :: proc{load_from_slice, load_from_file, load_from_context};
|
||||
|
||||
load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
data, ok := os.read_entire_file(filename, allocator);
|
||||
defer delete(data);
|
||||
|
||||
err = E_General.File_Not_Found;
|
||||
if ok {
|
||||
err = load_from_slice(data, buf, len(data), expected_output_size, allocator);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
load_from_slice :: proc(slice: []u8, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
buf := buf;
|
||||
|
||||
z := &compress.Context_Memory_Input{
|
||||
input_data = slice,
|
||||
output = buf,
|
||||
};
|
||||
return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator);
|
||||
}
|
||||
|
||||
load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
buf := buf;
|
||||
expected_output_size := expected_output_size;
|
||||
|
||||
input_data_consumed := 0;
|
||||
|
||||
z.output = buf;
|
||||
|
||||
if expected_output_size > GZIP_MAX_PAYLOAD_SIZE {
|
||||
return E_GZIP.Payload_Size_Exceeds_Max_Payload;
|
||||
}
|
||||
|
||||
if expected_output_size > compress.COMPRESS_OUTPUT_ALLOCATE_MAX {
|
||||
return E_GZIP.Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX;
|
||||
}
|
||||
|
||||
b: []u8;
|
||||
|
||||
header, e := compress.read_data(z, Header);
|
||||
if e != .None {
|
||||
return E_General.File_Too_Short;
|
||||
}
|
||||
input_data_consumed += size_of(Header);
|
||||
|
||||
if header.magic != .GZIP {
|
||||
return E_GZIP.Invalid_GZIP_Signature;
|
||||
}
|
||||
if header.compression_method != .DEFLATE {
|
||||
return E_General.Unknown_Compression_Method;
|
||||
}
|
||||
|
||||
if header.os >= ._Unknown {
|
||||
header.os = .Unknown;
|
||||
}
|
||||
|
||||
if .reserved_1 in header.flags || .reserved_2 in header.flags || .reserved_3 in header.flags {
|
||||
return E_GZIP.Reserved_Flag_Set;
|
||||
}
|
||||
|
||||
// printf("signature: %v\n", header.magic);
|
||||
// printf("compression: %v\n", header.compression_method);
|
||||
// printf("flags: %v\n", header.flags);
|
||||
// printf("modification time: %v\n", time.unix(i64(header.modification_time), 0));
|
||||
// printf("xfl: %v (%v)\n", header.xfl, int(header.xfl));
|
||||
// printf("os: %v\n", OS_Name[header.os]);
|
||||
|
||||
if .extra in header.flags {
|
||||
xlen, e_extra := compress.read_data(z, u16le);
|
||||
input_data_consumed += 2;
|
||||
|
||||
if e_extra != .None {
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
// printf("Extra data present (%v bytes)\n", xlen);
|
||||
if xlen < 4 {
|
||||
// Minimum length is 2 for ID + 2 for a field length, if set to zero.
|
||||
return E_GZIP.Invalid_Extra_Data;
|
||||
}
|
||||
|
||||
field_id: [2]u8;
|
||||
field_length: u16le;
|
||||
field_error: io.Error;
|
||||
|
||||
for xlen >= 4 {
|
||||
// println("Parsing Extra field(s).");
|
||||
field_id, field_error = compress.read_data(z, [2]u8);
|
||||
if field_error != .None {
|
||||
// printf("Parsing Extra returned: %v\n", field_error);
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
xlen -= 2;
|
||||
input_data_consumed += 2;
|
||||
|
||||
field_length, field_error = compress.read_data(z, u16le);
|
||||
if field_error != .None {
|
||||
// printf("Parsing Extra returned: %v\n", field_error);
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
xlen -= 2;
|
||||
input_data_consumed += 2;
|
||||
|
||||
if xlen <= 0 {
|
||||
// We're not going to try and recover by scanning for a ZLIB header.
|
||||
// Who knows what else is wrong with this file.
|
||||
return E_GZIP.Invalid_Extra_Data;
|
||||
}
|
||||
|
||||
// printf(" Field \"%v\" of length %v found: ", string(field_id[:]), field_length);
|
||||
if field_length > 0 {
|
||||
b, field_error = compress.read_slice(z, int(field_length));
|
||||
if field_error != .None {
|
||||
// printf("Parsing Extra returned: %v\n", field_error);
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
xlen -= field_length;
|
||||
input_data_consumed += int(field_length);
|
||||
|
||||
// printf("%v\n", string(field_data));
|
||||
}
|
||||
|
||||
if xlen != 0 {
|
||||
return E_GZIP.Invalid_Extra_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if .name in header.flags {
|
||||
// Should be enough.
|
||||
name: [1024]u8;
|
||||
i := 0;
|
||||
name_error: io.Error;
|
||||
|
||||
for i < len(name) {
|
||||
b, name_error = compress.read_slice(z, 1);
|
||||
if name_error != .None {
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
input_data_consumed += 1;
|
||||
if b[0] == 0 {
|
||||
break;
|
||||
}
|
||||
name[i] = b[0];
|
||||
i += 1;
|
||||
if i >= len(name) {
|
||||
return E_GZIP.Original_Name_Too_Long;
|
||||
}
|
||||
}
|
||||
// printf("Original filename: %v\n", string(name[:i]));
|
||||
}
|
||||
|
||||
if .comment in header.flags {
|
||||
// Should be enough.
|
||||
comment: [1024]u8;
|
||||
i := 0;
|
||||
comment_error: io.Error;
|
||||
|
||||
for i < len(comment) {
|
||||
b, comment_error = compress.read_slice(z, 1);
|
||||
if comment_error != .None {
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
input_data_consumed += 1;
|
||||
if b[0] == 0 {
|
||||
break;
|
||||
}
|
||||
comment[i] = b[0];
|
||||
i += 1;
|
||||
if i >= len(comment) {
|
||||
return E_GZIP.Comment_Too_Long;
|
||||
}
|
||||
}
|
||||
// printf("Comment: %v\n", string(comment[:i]));
|
||||
}
|
||||
|
||||
if .header_crc in header.flags {
|
||||
crc_error: io.Error;
|
||||
_, crc_error = compress.read_slice(z, 2);
|
||||
input_data_consumed += 2;
|
||||
if crc_error != .None {
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
/*
|
||||
We don't actually check the CRC16 (lower 2 bytes of CRC32 of header data until the CRC field).
|
||||
If we find a gzip file in the wild that sets this field, we can add proper support for it.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
We should have arrived at the ZLIB payload.
|
||||
*/
|
||||
payload_u32le: u32le;
|
||||
|
||||
// fmt.printf("known_gzip_size: %v | expected_output_size: %v\n", known_gzip_size, expected_output_size);
|
||||
|
||||
if expected_output_size > -1 {
|
||||
/*
|
||||
We already checked that it's not larger than the output buffer max,
|
||||
or GZIP length field's max.
|
||||
|
||||
We'll just pass it on to `zlib.inflate_raw`;
|
||||
*/
|
||||
} else {
|
||||
/*
|
||||
If we know the size of the GZIP file *and* it is fully in memory,
|
||||
then we can peek at the unpacked size at the end.
|
||||
|
||||
We'll still want to ensure there's capacity left in the output buffer when we write, of course.
|
||||
|
||||
*/
|
||||
if known_gzip_size > -1 {
|
||||
offset := i64(known_gzip_size - input_data_consumed - 4);
|
||||
size, _ := compress.input_size(z);
|
||||
if size >= offset + 4 {
|
||||
length_bytes := z.input_data[offset:][:4];
|
||||
payload_u32le = (^u32le)(&length_bytes[0])^;
|
||||
expected_output_size = int(payload_u32le);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
TODO(Jeroen): When reading a GZIP from a stream, check if impl_seek is present.
|
||||
If so, we can seek to the end, grab the size from the footer, and seek back to payload start.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// fmt.printf("GZIP: Expected Payload Size: %v\n", expected_output_size);
|
||||
|
||||
zlib_error := zlib.inflate_raw(z=z, expected_output_size=expected_output_size);
|
||||
if zlib_error != nil {
|
||||
return zlib_error;
|
||||
}
|
||||
/*
|
||||
Read CRC32 using the ctx bit reader because zlib may leave bytes in there.
|
||||
*/
|
||||
compress.discard_to_next_byte_lsb(z);
|
||||
|
||||
footer_error: io.Error;
|
||||
|
||||
payload_crc_b: [4]u8;
|
||||
for _, i in payload_crc_b {
|
||||
payload_crc_b[i], footer_error = compress.read_u8_prefer_code_buffer_lsb(z);
|
||||
}
|
||||
payload_crc := transmute(u32le)payload_crc_b;
|
||||
|
||||
payload := bytes.buffer_to_bytes(buf);
|
||||
crc32 := u32le(hash.crc32(payload));
|
||||
if crc32 != payload_crc {
|
||||
return E_GZIP.Payload_CRC_Invalid;
|
||||
}
|
||||
|
||||
payload_len_b: [4]u8;
|
||||
for _, i in payload_len_b {
|
||||
payload_len_b[i], footer_error = compress.read_u8_prefer_code_buffer_lsb(z);
|
||||
}
|
||||
payload_len := transmute(u32le)payload_len_b;
|
||||
|
||||
if len(payload) != int(payload_len) {
|
||||
return E_GZIP.Payload_Length_Invalid;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//+ignore
|
||||
package zlib
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
|
||||
An example of how to use `zlib.inflate`.
|
||||
*/
|
||||
|
||||
import "core:bytes"
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
|
||||
ODIN_DEMO := []u8{
|
||||
120, 156, 101, 144, 77, 110, 131, 48, 16, 133, 215, 204, 41, 158, 44,
|
||||
69, 73, 32, 148, 182, 75, 35, 14, 208, 125, 47, 96, 185, 195, 143,
|
||||
130, 13, 50, 38, 81, 84, 101, 213, 75, 116, 215, 43, 246, 8, 53,
|
||||
82, 126, 8, 181, 188, 152, 153, 111, 222, 147, 159, 123, 165, 247, 170,
|
||||
98, 24, 213, 88, 162, 198, 244, 157, 243, 16, 186, 115, 44, 75, 227,
|
||||
5, 77, 115, 72, 137, 222, 117, 122, 179, 197, 39, 69, 161, 170, 156,
|
||||
50, 144, 5, 68, 130, 4, 49, 126, 127, 190, 191, 144, 34, 19, 57,
|
||||
69, 74, 235, 209, 140, 173, 242, 157, 155, 54, 158, 115, 162, 168, 12,
|
||||
181, 239, 246, 108, 17, 188, 174, 242, 224, 20, 13, 199, 198, 235, 250,
|
||||
194, 166, 129, 86, 3, 99, 157, 172, 37, 230, 62, 73, 129, 151, 252,
|
||||
70, 211, 5, 77, 31, 104, 188, 160, 113, 129, 215, 59, 205, 22, 52,
|
||||
123, 160, 83, 142, 255, 242, 89, 123, 93, 149, 200, 50, 188, 85, 54,
|
||||
252, 18, 248, 192, 238, 228, 235, 198, 86, 224, 118, 224, 176, 113, 166,
|
||||
112, 67, 106, 227, 159, 122, 215, 88, 95, 110, 196, 123, 205, 183, 224,
|
||||
98, 53, 8, 104, 213, 234, 201, 147, 7, 248, 192, 14, 170, 29, 25,
|
||||
171, 15, 18, 59, 138, 112, 63, 23, 205, 110, 254, 136, 109, 78, 231,
|
||||
63, 234, 138, 133, 204,
|
||||
};
|
||||
OUTPUT_SIZE :: 438;
|
||||
|
||||
buf: bytes.Buffer;
|
||||
|
||||
// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
|
||||
err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE);
|
||||
defer bytes.buffer_destroy(&buf);
|
||||
|
||||
if err != nil {
|
||||
fmt.printf("\nError: %v\n", err);
|
||||
}
|
||||
s := bytes.buffer_to_string(&buf);
|
||||
fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s);
|
||||
assert(len(s) == OUTPUT_SIZE);
|
||||
}
|
||||
@@ -0,0 +1,715 @@
|
||||
package zlib
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
*/
|
||||
|
||||
import "core:compress"
|
||||
|
||||
import "core:mem"
|
||||
import "core:io"
|
||||
import "core:hash"
|
||||
import "core:bytes"
|
||||
|
||||
/*
|
||||
zlib.inflate decompresses a ZLIB stream passed in as a []u8 or io.Stream.
|
||||
Returns: Error.
|
||||
*/
|
||||
|
||||
/*
|
||||
Do we do Adler32 as we write bytes to output?
|
||||
It used to be faster to do it inline, now it's faster to do it at the end of `inflate`.
|
||||
|
||||
We'll see what's faster after more optimization, and might end up removing
|
||||
`Context.rolling_hash` if not inlining it is still faster.
|
||||
|
||||
*/
|
||||
|
||||
Compression_Method :: enum u8 {
|
||||
DEFLATE = 8,
|
||||
Reserved = 15,
|
||||
}
|
||||
|
||||
Compression_Level :: enum u8 {
|
||||
Fastest = 0,
|
||||
Fast = 1,
|
||||
Default = 2,
|
||||
Maximum = 3,
|
||||
}
|
||||
|
||||
Options :: struct {
|
||||
window_size: u16,
|
||||
level: u8,
|
||||
}
|
||||
|
||||
Error :: compress.Error;
|
||||
E_General :: compress.General_Error;
|
||||
E_ZLIB :: compress.ZLIB_Error;
|
||||
E_Deflate :: compress.Deflate_Error;
|
||||
|
||||
DEFLATE_MAX_CHUNK_SIZE :: 65535;
|
||||
DEFLATE_MAX_LITERAL_SIZE :: 65535;
|
||||
DEFLATE_MAX_DISTANCE :: 32768;
|
||||
DEFLATE_MAX_LENGTH :: 258;
|
||||
|
||||
HUFFMAN_MAX_BITS :: 16;
|
||||
HUFFMAN_FAST_BITS :: 9;
|
||||
HUFFMAN_FAST_MASK :: ((1 << HUFFMAN_FAST_BITS) - 1);
|
||||
|
||||
Z_LENGTH_BASE := [31]u16{
|
||||
3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,
|
||||
67,83,99,115,131,163,195,227,258,0,0,
|
||||
};
|
||||
|
||||
Z_LENGTH_EXTRA := [31]u8{
|
||||
0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,
|
||||
};
|
||||
|
||||
Z_DIST_BASE := [32]u16{
|
||||
1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
|
||||
257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0,
|
||||
};
|
||||
|
||||
Z_DIST_EXTRA := [32]u8{
|
||||
0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0,
|
||||
};
|
||||
|
||||
Z_LENGTH_DEZIGZAG := []u8{
|
||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
|
||||
};
|
||||
|
||||
Z_FIXED_LENGTH := [288]u8{
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,
|
||||
};
|
||||
|
||||
Z_FIXED_DIST := [32]u8{
|
||||
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
};
|
||||
|
||||
/*
|
||||
Accelerate all cases in default tables.
|
||||
*/
|
||||
ZFAST_BITS :: 9;
|
||||
ZFAST_MASK :: ((1 << ZFAST_BITS) - 1);
|
||||
|
||||
/*
|
||||
ZLIB-style Huffman encoding.
|
||||
JPEG packs from left, ZLIB from right. We can't share code.
|
||||
*/
|
||||
Huffman_Table :: struct {
|
||||
fast: [1 << ZFAST_BITS]u16,
|
||||
firstcode: [16]u16,
|
||||
maxcode: [17]int,
|
||||
firstsymbol: [16]u16,
|
||||
size: [288]u8,
|
||||
value: [288]u16,
|
||||
};
|
||||
|
||||
// Implementation starts here
|
||||
@(optimization_mode="speed")
|
||||
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
||||
assert(bits <= 16);
|
||||
// NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling
|
||||
// by reversing all of the bits and masking out the unneeded ones.
|
||||
r = n;
|
||||
r = ((r & 0xAAAA) >> 1) | ((r & 0x5555) << 1);
|
||||
r = ((r & 0xCCCC) >> 2) | ((r & 0x3333) << 2);
|
||||
r = ((r & 0xF0F0) >> 4) | ((r & 0x0F0F) << 4);
|
||||
r = ((r & 0xFF00) >> 8) | ((r & 0x00FF) << 8);
|
||||
|
||||
r >>= (16 - bits);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
|
||||
/*
|
||||
That we get here at all means that we didn't pass an expected output size,
|
||||
or that it was too little.
|
||||
*/
|
||||
|
||||
/*
|
||||
Double until we reach the maximum allowed.
|
||||
*/
|
||||
new_size := min(len(buf) << 1, compress.COMPRESS_OUTPUT_ALLOCATE_MAX);
|
||||
resize(buf, new_size);
|
||||
if len(buf) != new_size {
|
||||
/*
|
||||
Resize failed.
|
||||
*/
|
||||
return .Resize_Failed;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Make these return compress.Error.
|
||||
*/
|
||||
|
||||
@(optimization_mode="speed")
|
||||
write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_check {
|
||||
/*
|
||||
Resize if needed.
|
||||
*/
|
||||
if int(z.bytes_written) + 1 >= len(z.output.buf) {
|
||||
e := grow_buffer(&z.output.buf);
|
||||
if e != nil {
|
||||
return .Short_Write;
|
||||
}
|
||||
}
|
||||
|
||||
#no_bounds_check {
|
||||
z.output.buf[z.bytes_written] = c;
|
||||
}
|
||||
z.bytes_written += 1;
|
||||
return .None;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check {
|
||||
/*
|
||||
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
||||
without having to worry about wrapping, so no need for a temp allocation to give to
|
||||
the output stream, just give it _that_ slice.
|
||||
*/
|
||||
|
||||
/*
|
||||
Resize if needed.
|
||||
*/
|
||||
if int(z.bytes_written) + int(count) >= len(z.output.buf) {
|
||||
e := grow_buffer(&z.output.buf);
|
||||
if e != nil {
|
||||
return .Short_Write;
|
||||
}
|
||||
}
|
||||
|
||||
#no_bounds_check {
|
||||
for _ in 0..<count {
|
||||
z.output.buf[z.bytes_written] = c;
|
||||
z.bytes_written += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return .None;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
repl_bytes :: proc(z: ^$C, count: u16, distance: u16) -> (err: io.Error) {
|
||||
/*
|
||||
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
||||
without having to worry about wrapping, so no need for a temp allocation to give to
|
||||
the output stream, just give it _that_ slice.
|
||||
*/
|
||||
|
||||
offset := i64(distance);
|
||||
|
||||
if int(z.bytes_written) + int(count) >= len(z.output.buf) {
|
||||
e := grow_buffer(&z.output.buf);
|
||||
if e != nil {
|
||||
return .Short_Write;
|
||||
}
|
||||
}
|
||||
|
||||
#no_bounds_check {
|
||||
for _ in 0..<count {
|
||||
c := z.output.buf[z.bytes_written - offset];
|
||||
z.output.buf[z.bytes_written] = c;
|
||||
z.bytes_written += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return .None;
|
||||
}
|
||||
|
||||
|
||||
allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_Table, err: Error) {
|
||||
return new(Huffman_Table, allocator), nil;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
||||
sizes: [HUFFMAN_MAX_BITS+1]int;
|
||||
next_code: [HUFFMAN_MAX_BITS]int;
|
||||
|
||||
k := int(0);
|
||||
|
||||
mem.zero_slice(sizes[:]);
|
||||
mem.zero_slice(z.fast[:]);
|
||||
|
||||
for v in code_lengths {
|
||||
sizes[v] += 1;
|
||||
}
|
||||
sizes[0] = 0;
|
||||
|
||||
for i in 1..<(HUFFMAN_MAX_BITS+1) {
|
||||
if sizes[i] > (1 << uint(i)) {
|
||||
return E_Deflate.Huffman_Bad_Sizes;
|
||||
}
|
||||
}
|
||||
code := int(0);
|
||||
|
||||
for i in 1..<HUFFMAN_MAX_BITS {
|
||||
next_code[i] = code;
|
||||
z.firstcode[i] = u16(code);
|
||||
z.firstsymbol[i] = u16(k);
|
||||
code = code + sizes[i];
|
||||
if sizes[i] != 0 {
|
||||
if code - 1 >= (1 << u16(i)) {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
}
|
||||
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i));
|
||||
code <<= 1;
|
||||
k += int(sizes[i]);
|
||||
}
|
||||
|
||||
z.maxcode[HUFFMAN_MAX_BITS] = 0x10000; // Sentinel
|
||||
c: int;
|
||||
|
||||
for v, ci in code_lengths {
|
||||
if v != 0 {
|
||||
c = next_code[v] - int(z.firstcode[v]) + int(z.firstsymbol[v]);
|
||||
fastv := u16((u16(v) << 9) | u16(ci));
|
||||
z.size[c] = u8(v);
|
||||
z.value[c] = u16(ci);
|
||||
if v <= ZFAST_BITS {
|
||||
j := z_bit_reverse(u16(next_code[v]), v);
|
||||
for j < (1 << ZFAST_BITS) {
|
||||
z.fast[j] = fastv;
|
||||
j += (1 << v);
|
||||
}
|
||||
}
|
||||
next_code[v] += 1;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||
code := u16(compress.peek_bits_lsb(z,16));
|
||||
|
||||
k := int(z_bit_reverse(code, 16));
|
||||
s: u8;
|
||||
|
||||
#no_bounds_check for s = HUFFMAN_FAST_BITS+1; ; {
|
||||
if k < t.maxcode[s] {
|
||||
break;
|
||||
}
|
||||
s += 1;
|
||||
}
|
||||
if s >= 16 {
|
||||
return 0, E_Deflate.Bad_Huffman_Code;
|
||||
}
|
||||
// code size is s, so:
|
||||
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s]);
|
||||
if b >= size_of(t.size) {
|
||||
return 0, E_Deflate.Bad_Huffman_Code;
|
||||
}
|
||||
if t.size[b] != s {
|
||||
return 0, E_Deflate.Bad_Huffman_Code;
|
||||
}
|
||||
|
||||
compress.consume_bits_lsb(z, s);
|
||||
|
||||
r = t.value[b];
|
||||
return r, nil;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||
if z.num_bits < 16 {
|
||||
if z.num_bits > 63 {
|
||||
return 0, E_ZLIB.Code_Buffer_Malformed;
|
||||
}
|
||||
compress.refill_lsb(z);
|
||||
if z.num_bits > 63 {
|
||||
return 0, E_General.Stream_Too_Short;
|
||||
}
|
||||
}
|
||||
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK];
|
||||
if b != 0 {
|
||||
s := u8(b >> ZFAST_BITS);
|
||||
compress.consume_bits_lsb(z, s);
|
||||
return b & 511, nil;
|
||||
}
|
||||
return decode_huffman_slowpath(z, t);
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
|
||||
#no_bounds_check for {
|
||||
value, e := decode_huffman(z, z_repeat);
|
||||
if e != nil {
|
||||
return err;
|
||||
}
|
||||
if value < 256 {
|
||||
e := write_byte(z, u8(value));
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short;
|
||||
}
|
||||
} else {
|
||||
if value == 256 {
|
||||
// End of block
|
||||
return nil;
|
||||
}
|
||||
|
||||
value -= 257;
|
||||
length := Z_LENGTH_BASE[value];
|
||||
if Z_LENGTH_EXTRA[value] > 0 {
|
||||
length += u16(compress.read_bits_lsb(z, Z_LENGTH_EXTRA[value]));
|
||||
}
|
||||
|
||||
value, e = decode_huffman(z, z_offset);
|
||||
if e != nil {
|
||||
return E_Deflate.Bad_Huffman_Code;
|
||||
}
|
||||
|
||||
distance := Z_DIST_BASE[value];
|
||||
if Z_DIST_EXTRA[value] > 0 {
|
||||
distance += u16(compress.read_bits_lsb(z, Z_DIST_EXTRA[value]));
|
||||
}
|
||||
|
||||
if z.bytes_written < i64(distance) {
|
||||
// Distance is longer than we've decoded so far.
|
||||
return E_Deflate.Bad_Distance;
|
||||
}
|
||||
|
||||
/*
|
||||
These might be sped up with a repl_byte call that copies
|
||||
from the already written output more directly, and that
|
||||
update the Adler checksum once after.
|
||||
|
||||
That way we'd suffer less Stream vtable overhead.
|
||||
*/
|
||||
if distance == 1 {
|
||||
/*
|
||||
Replicate the last outputted byte, length times.
|
||||
*/
|
||||
if length > 0 {
|
||||
c := z.output.buf[z.bytes_written - i64(distance)];
|
||||
e := repl_byte(z, length, c);
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if length > 0 {
|
||||
e := repl_bytes(z, length, distance);
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := false, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
||||
/*
|
||||
ctx.output must be a bytes.Buffer for now. We'll add a separate implementation that writes to a stream.
|
||||
|
||||
raw determines whether the ZLIB header is processed, or we're inflating a raw
|
||||
DEFLATE stream.
|
||||
*/
|
||||
|
||||
if !raw {
|
||||
size, size_err := compress.input_size(ctx);
|
||||
if size < 6 || size_err != nil {
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
|
||||
cmf, _ := compress.read_u8(ctx);
|
||||
|
||||
method := Compression_Method(cmf & 0xf);
|
||||
if method != .DEFLATE {
|
||||
return E_General.Unknown_Compression_Method;
|
||||
}
|
||||
|
||||
cinfo := (cmf >> 4) & 0xf;
|
||||
if cinfo > 7 {
|
||||
return E_ZLIB.Unsupported_Window_Size;
|
||||
}
|
||||
flg, _ := compress.read_u8(ctx);
|
||||
|
||||
fcheck := flg & 0x1f;
|
||||
fcheck_computed := (cmf << 8 | flg) & 0x1f;
|
||||
if fcheck != fcheck_computed {
|
||||
return E_General.Checksum_Failed;
|
||||
}
|
||||
|
||||
fdict := (flg >> 5) & 1;
|
||||
/*
|
||||
We don't handle built-in dictionaries for now.
|
||||
They're application specific and PNG doesn't use them.
|
||||
*/
|
||||
if fdict != 0 {
|
||||
return E_ZLIB.FDICT_Unsupported;
|
||||
}
|
||||
|
||||
// flevel := Compression_Level((flg >> 6) & 3);
|
||||
/*
|
||||
Inflate can consume bits belonging to the Adler checksum.
|
||||
We pass the entire stream to Inflate and will unget bytes if we need to
|
||||
at the end to compare checksums.
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
// Parse ZLIB stream without header.
|
||||
err = inflate_raw(z=ctx, expected_output_size=expected_output_size);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
if !raw {
|
||||
compress.discard_to_next_byte_lsb(ctx);
|
||||
|
||||
adler_b: [4]u8;
|
||||
for _, i in adler_b {
|
||||
adler_b[i], _ = compress.read_u8_prefer_code_buffer_lsb(ctx);
|
||||
}
|
||||
adler := transmute(u32be)adler_b;
|
||||
|
||||
output_hash := hash.adler32(ctx.output.buf[:]);
|
||||
|
||||
if output_hash != u32(adler) {
|
||||
return E_General.Checksum_Failed;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// TODO: Check alignment of reserve/resize.
|
||||
|
||||
@(optimization_mode="speed")
|
||||
inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
||||
expected_output_size := expected_output_size;
|
||||
|
||||
/*
|
||||
Always set up a minimum allocation size.
|
||||
*/
|
||||
expected_output_size = max(max(expected_output_size, compress.COMPRESS_OUTPUT_ALLOCATE_MIN), 512);
|
||||
|
||||
// fmt.printf("\nZLIB: Expected Payload Size: %v\n\n", expected_output_size);
|
||||
|
||||
if expected_output_size > 0 && expected_output_size <= compress.COMPRESS_OUTPUT_ALLOCATE_MAX {
|
||||
/*
|
||||
Try to pre-allocate the output buffer.
|
||||
*/
|
||||
reserve(&z.output.buf, expected_output_size);
|
||||
resize (&z.output.buf, expected_output_size);
|
||||
};
|
||||
|
||||
if len(z.output.buf) != expected_output_size {
|
||||
return .Resize_Failed;
|
||||
}
|
||||
|
||||
z.num_bits = 0;
|
||||
z.code_buffer = 0;
|
||||
|
||||
z_repeat: ^Huffman_Table;
|
||||
z_offset: ^Huffman_Table;
|
||||
codelength_ht: ^Huffman_Table;
|
||||
|
||||
z_repeat, err = allocate_huffman_table(allocator=context.allocator);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
z_offset, err = allocate_huffman_table(allocator=context.allocator);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
codelength_ht, err = allocate_huffman_table(allocator=context.allocator);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
defer free(z_repeat);
|
||||
defer free(z_offset);
|
||||
defer free(codelength_ht);
|
||||
|
||||
final := u32(0);
|
||||
type := u32(0);
|
||||
|
||||
for {
|
||||
final = compress.read_bits_lsb(z, 1);
|
||||
type = compress.read_bits_lsb(z, 2);
|
||||
|
||||
// fmt.printf("Final: %v | Type: %v\n", final, type);
|
||||
|
||||
switch type {
|
||||
case 0:
|
||||
// Uncompressed block
|
||||
|
||||
// Discard bits until next byte boundary
|
||||
compress.discard_to_next_byte_lsb(z);
|
||||
|
||||
uncompressed_len := i16(compress.read_bits_lsb(z, 16));
|
||||
length_check := i16(compress.read_bits_lsb(z, 16));
|
||||
|
||||
// fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check);
|
||||
|
||||
|
||||
if ~uncompressed_len != length_check {
|
||||
return E_Deflate.Len_Nlen_Mismatch;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Maybe speed this up with a stream-to-stream copy (read_from)
|
||||
and a single Adler32 update after.
|
||||
*/
|
||||
#no_bounds_check for uncompressed_len > 0 {
|
||||
compress.refill_lsb(z);
|
||||
lit := compress.read_bits_lsb(z, 8);
|
||||
write_byte(z, u8(lit));
|
||||
uncompressed_len -= 1;
|
||||
}
|
||||
case 3:
|
||||
return E_Deflate.BType_3;
|
||||
case:
|
||||
// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
|
||||
if type == 1 {
|
||||
// Use fixed code lengths.
|
||||
err = build_huffman(z_repeat, Z_FIXED_LENGTH[:]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
err = build_huffman(z_offset, Z_FIXED_DIST[:]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
lencodes: [286+32+137]u8;
|
||||
codelength_sizes: [19]u8;
|
||||
|
||||
//i: u32;
|
||||
n: u32;
|
||||
|
||||
compress.refill_lsb(z, 14);
|
||||
hlit := compress.read_bits_no_refill_lsb(z, 5) + 257;
|
||||
hdist := compress.read_bits_no_refill_lsb(z, 5) + 1;
|
||||
hclen := compress.read_bits_no_refill_lsb(z, 4) + 4;
|
||||
ntot := hlit + hdist;
|
||||
|
||||
#no_bounds_check for i in 0..<hclen {
|
||||
s := compress.read_bits_lsb(z, 3);
|
||||
codelength_sizes[Z_LENGTH_DEZIGZAG[i]] = u8(s);
|
||||
}
|
||||
err = build_huffman(codelength_ht, codelength_sizes[:]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
n = 0;
|
||||
c: u16;
|
||||
|
||||
for n < ntot {
|
||||
c, err = decode_huffman(z, codelength_ht);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
if c < 0 || c >= 19 {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
if c < 16 {
|
||||
lencodes[n] = u8(c);
|
||||
n += 1;
|
||||
} else {
|
||||
fill := u8(0);
|
||||
compress.refill_lsb(z, 7);
|
||||
switch c {
|
||||
case 16:
|
||||
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3);
|
||||
if n == 0 {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
fill = lencodes[n - 1];
|
||||
case 17:
|
||||
c = u16(compress.read_bits_no_refill_lsb(z, 3) + 3);
|
||||
case 18:
|
||||
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11);
|
||||
case:
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
|
||||
if ntot - n < u32(c) {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
|
||||
nc := n + u32(c);
|
||||
#no_bounds_check for ; n < nc; n += 1 {
|
||||
lencodes[n] = fill;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if n != ntot {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
|
||||
err = build_huffman(z_repeat, lencodes[:hlit]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = build_huffman(z_offset, lencodes[hlit:ntot]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
err = parse_huffman_block(z, z_repeat, z_offset);
|
||||
// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if final == 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if int(z.bytes_written) != len(z.output.buf) {
|
||||
resize(&z.output.buf, int(z.bytes_written));
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) {
|
||||
ctx := compress.Context_Memory_Input{};
|
||||
|
||||
ctx.input_data = input;
|
||||
ctx.output = buf;
|
||||
|
||||
err = inflate_from_context(ctx=&ctx, raw=raw, expected_output_size=expected_output_size);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) {
|
||||
ctx := compress.Context_Memory_Input{};
|
||||
|
||||
ctx.input_data = input;
|
||||
ctx.output = buf;
|
||||
|
||||
return inflate_raw(z=&ctx, expected_output_size=expected_output_size);
|
||||
}
|
||||
|
||||
inflate :: proc{inflate_from_context, inflate_from_byte_array};
|
||||
@@ -0,0 +1,216 @@
|
||||
package container
|
||||
|
||||
import "core:mem"
|
||||
import "core:runtime"
|
||||
|
||||
Array :: struct($T: typeid) {
|
||||
data: ^T,
|
||||
len: int,
|
||||
cap: int,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
ARRAY_DEFAULT_CAPACITY :: 16;
|
||||
|
||||
/*
|
||||
array_init :: proc {
|
||||
array_init_none,
|
||||
array_init_len,
|
||||
array_init_len_cap,
|
||||
}
|
||||
array_init
|
||||
array_delete
|
||||
array_len
|
||||
array_cap
|
||||
array_space
|
||||
array_slice
|
||||
array_get
|
||||
array_get_ptr
|
||||
array_set
|
||||
array_reserve
|
||||
array_resize
|
||||
array_push = array_append :: proc{
|
||||
array_push_back,
|
||||
array_push_back_elems,
|
||||
}
|
||||
array_push_front
|
||||
array_pop_back
|
||||
array_pop_front
|
||||
array_consume
|
||||
array_trim
|
||||
array_clear
|
||||
array_clone
|
||||
array_set_capacity
|
||||
array_grow
|
||||
*/
|
||||
|
||||
|
||||
array_init_none :: proc(a: ^$A/Array, allocator := context.allocator) {
|
||||
array_init_len_cap(a, 0, ARRAY_DEFAULT_CAPACITY, allocator);
|
||||
}
|
||||
array_init_len :: proc(a: ^$A/Array, len: int, allocator := context.allocator) {
|
||||
array_init_len_cap(a, len, len, allocator);
|
||||
}
|
||||
array_init_len_cap :: proc(a: ^$A/Array($T), len: int, cap: int, allocator := context.allocator) {
|
||||
a.allocator = allocator;
|
||||
a.data = (^T)(mem.alloc(size_of(T)*cap, align_of(T), a.allocator));
|
||||
a.len = len;
|
||||
a.cap = cap;
|
||||
}
|
||||
|
||||
array_init :: proc{array_init_none, array_init_len, array_init_len_cap};
|
||||
|
||||
array_delete :: proc(a: $A/Array) {
|
||||
mem.free(a.data, a.allocator);
|
||||
}
|
||||
|
||||
array_len :: proc(a: $A/Array) -> int {
|
||||
return a.len;
|
||||
}
|
||||
|
||||
array_cap :: proc(a: $A/Array) -> int {
|
||||
return a.cap;
|
||||
}
|
||||
|
||||
array_space :: proc(a: $A/Array) -> int {
|
||||
return a.cap - a.len;
|
||||
}
|
||||
|
||||
array_slice :: proc(a: $A/Array($T)) -> []T {
|
||||
s := mem.Raw_Slice{a.data, a.len};
|
||||
return transmute([]T)s;
|
||||
}
|
||||
|
||||
array_cap_slice :: proc(a: $A/Array($T)) -> []T {
|
||||
s := mem.Raw_Slice{a.data, a.cap};
|
||||
return transmute([]T)s;
|
||||
}
|
||||
|
||||
array_get :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> T {
|
||||
runtime.bounds_check_error_loc(loc, index, array_len(a));
|
||||
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^;
|
||||
}
|
||||
array_get_ptr :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, index, array_len(a));
|
||||
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index));
|
||||
}
|
||||
|
||||
array_set :: proc(a: ^$A/Array($T), index: int, item: T, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, index, array_len(a^));
|
||||
(^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item;
|
||||
}
|
||||
|
||||
|
||||
array_reserve :: proc(a: ^$A/Array, capacity: int) {
|
||||
if capacity > a.len {
|
||||
array_set_capacity(a, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
array_resize :: proc(a: ^$A/Array, length: int) {
|
||||
if length > a.len {
|
||||
array_set_capacity(a, length);
|
||||
}
|
||||
a.len = length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
array_push_back :: proc(a: ^$A/Array($T), item: T) {
|
||||
if array_space(a^) == 0 {
|
||||
array_grow(a);
|
||||
}
|
||||
|
||||
a.len += 1;
|
||||
array_set(a, a.len-1, item);
|
||||
}
|
||||
|
||||
array_push_front :: proc(a: ^$A/Array($T), item: T) {
|
||||
if array_space(a^) == 0 {
|
||||
array_grow(a);
|
||||
}
|
||||
|
||||
a.len += 1;
|
||||
data := array_slice(a^);
|
||||
copy(data[1:], data[:]);
|
||||
data[0] = item;
|
||||
}
|
||||
|
||||
array_pop_back :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc);
|
||||
item := array_get(a^, a.len-1);
|
||||
a.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
array_pop_front :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc);
|
||||
item := array_get(a^, 0);
|
||||
s := array_slice(a^);
|
||||
copy(s[:], s[1:]);
|
||||
a.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
array_consume :: proc(a: ^$A/Array($T), count: int, loc := #caller_location) {
|
||||
assert(condition=a.len >= count, loc=loc);
|
||||
a.len -= count;
|
||||
}
|
||||
|
||||
|
||||
array_trim :: proc(a: ^$A/Array($T)) {
|
||||
array_set_capacity(a, a.len);
|
||||
}
|
||||
|
||||
array_clear :: proc(a: ^$A/Array($T)) {
|
||||
array_resize(a, 0);
|
||||
}
|
||||
|
||||
array_clone :: proc(a: $A/Array($T), allocator := context.allocator) -> A {
|
||||
res: A;
|
||||
array_init(&res, array_len(a), array_len(a), allocator);
|
||||
copy(array_slice(res), array_slice(a));
|
||||
return res;
|
||||
}
|
||||
|
||||
array_push_back_elems :: proc(a: ^$A/Array($T), items: ..T) {
|
||||
if array_space(a^) < len(items) {
|
||||
array_grow(a, a.len + len(items));
|
||||
}
|
||||
offset := a.len;
|
||||
data := array_cap_slice(a^);
|
||||
n := copy(data[a.len:], items);
|
||||
a.len += n;
|
||||
}
|
||||
|
||||
array_push :: proc{array_push_back, array_push_back_elems};
|
||||
array_append :: proc{array_push_back, array_push_back_elems};
|
||||
|
||||
array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) {
|
||||
if new_capacity == a.cap {
|
||||
return;
|
||||
}
|
||||
|
||||
if new_capacity < a.len {
|
||||
array_resize(a, new_capacity);
|
||||
}
|
||||
|
||||
new_data: ^T;
|
||||
if new_capacity > 0 {
|
||||
if a.allocator.procedure == nil {
|
||||
a.allocator = context.allocator;
|
||||
}
|
||||
new_data = (^T)(mem.alloc(size_of(T)*new_capacity, align_of(T), a.allocator));
|
||||
if new_data != nil {
|
||||
mem.copy(new_data, a.data, size_of(T)*a.len);
|
||||
}
|
||||
}
|
||||
mem.free(a.data, a.allocator);
|
||||
a.data = new_data;
|
||||
a.cap = new_capacity;
|
||||
}
|
||||
array_grow :: proc(a: ^$A/Array, min_capacity: int = 0) {
|
||||
new_capacity := max(array_len(a^)*2 + 8, min_capacity);
|
||||
array_set_capacity(a, new_capacity);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package container
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Bloom_Hash_Proc :: #type proc(data: []byte) -> u32;
|
||||
|
||||
Bloom_Hash :: struct {
|
||||
hash_proc: Bloom_Hash_Proc,
|
||||
next: ^Bloom_Hash,
|
||||
}
|
||||
|
||||
Bloom_Filter :: struct {
|
||||
allocator: mem.Allocator,
|
||||
hash: ^Bloom_Hash,
|
||||
bits: []byte,
|
||||
}
|
||||
|
||||
bloom_filter_init :: proc(b: ^Bloom_Filter, size: int, allocator := context.allocator) {
|
||||
b.allocator = allocator;
|
||||
b.bits = make([]byte, size, allocator);
|
||||
}
|
||||
|
||||
bloom_filter_destroy :: proc(b: ^Bloom_Filter) {
|
||||
context.allocator = b.allocator;
|
||||
delete(b.bits);
|
||||
for b.hash != nil {
|
||||
hash := b.hash;
|
||||
b.hash = b.hash.next;
|
||||
free(hash);
|
||||
}
|
||||
}
|
||||
|
||||
bloom_filter_add_hash_proc :: proc(b: ^Bloom_Filter, hash_proc: Bloom_Hash_Proc) {
|
||||
context.allocator = b.allocator;
|
||||
h := new(Bloom_Hash);
|
||||
h.hash_proc = hash_proc;
|
||||
|
||||
head := &b.hash;
|
||||
for head^ != nil {
|
||||
head = &(head^.next);
|
||||
}
|
||||
head^ = h;
|
||||
}
|
||||
|
||||
bloom_filter_add :: proc(b: ^Bloom_Filter, item: []byte) {
|
||||
#no_bounds_check for h := b.hash; h != nil; h = h.next {
|
||||
hash := h.hash_proc(item);
|
||||
hash %= u32(len(b.bits) * 8);
|
||||
b.bits[hash >> 3] |= 1 << (hash & 3);
|
||||
}
|
||||
}
|
||||
|
||||
bloom_filter_add_string :: proc(b: ^Bloom_Filter, item: string) {
|
||||
bloom_filter_add(b, transmute([]byte)item);
|
||||
}
|
||||
|
||||
bloom_filter_add_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) {
|
||||
item := mem.slice_ptr((^byte)(data), size);
|
||||
bloom_filter_add(b, item);
|
||||
}
|
||||
|
||||
bloom_filter_test :: proc(b: ^Bloom_Filter, item: []byte) -> bool {
|
||||
#no_bounds_check for h := b.hash; h != nil; h = h.next {
|
||||
hash := h.hash_proc(item);
|
||||
hash %= u32(len(b.bits) * 8);
|
||||
if (b.bits[hash >> 3] & (1 << (hash & 3)) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bloom_filter_test_string :: proc(b: ^Bloom_Filter, item: string) -> bool {
|
||||
return bloom_filter_test(b, transmute([]byte)item);
|
||||
}
|
||||
|
||||
bloom_filter_test_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) -> bool {
|
||||
item := mem.slice_ptr((^byte)(data), size);
|
||||
return bloom_filter_test(b, item);
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
package container
|
||||
|
||||
import "intrinsics"
|
||||
_ :: intrinsics;
|
||||
|
||||
|
||||
Map :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
hash: Array(int),
|
||||
entries: Array(Map_Entry(Key, Value)),
|
||||
}
|
||||
|
||||
Map_Entry :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
hash: uintptr,
|
||||
next: int,
|
||||
key: Key,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
map_init :: proc{
|
||||
map_init_none,
|
||||
map_init_cap,
|
||||
}
|
||||
map_delete
|
||||
|
||||
map_has
|
||||
map_get
|
||||
map_get_default
|
||||
map_get_ptr
|
||||
map_set
|
||||
map_remove
|
||||
map_reserve
|
||||
map_clear
|
||||
|
||||
// Multi Map
|
||||
|
||||
multi_map_find_first
|
||||
multi_map_find_next
|
||||
multi_map_count
|
||||
multi_map_get :: proc{
|
||||
multi_map_get_array,
|
||||
multi_map_get_slice,
|
||||
};
|
||||
multi_map_get_as_slice
|
||||
multi_map_insert
|
||||
multi_map_remove
|
||||
multi_map_remove_all
|
||||
|
||||
*/
|
||||
|
||||
map_init :: proc{map_init_none, map_init_cap};
|
||||
|
||||
map_init_none :: proc(m: ^$M/Map($Key, $Value), allocator := context.allocator) {
|
||||
m.hash.allocator = allocator;
|
||||
m.entries.allocator = allocator;
|
||||
}
|
||||
|
||||
map_init_cap :: proc(m: ^$M/Map($Key, $Value), cap: int, allocator := context.allocator) {
|
||||
m.hash.allocator = allocator;
|
||||
m.entries.allocator = allocator;
|
||||
map_reserve(m, cap);
|
||||
}
|
||||
|
||||
map_delete :: proc(m: $M/Map($Key, $Value)) {
|
||||
array_delete(m.hash);
|
||||
array_delete(m.entries);
|
||||
}
|
||||
|
||||
|
||||
map_has :: proc(m: $M/Map($Key, $Value), key: Key) -> bool {
|
||||
return _map_find_or_fail(m, key) >= 0;
|
||||
}
|
||||
|
||||
map_get :: proc(m: $M/Map($Key, $Value), key: Key) -> (res: Value, ok: bool) #optional_ok {
|
||||
i := _map_find_or_fail(m, key);
|
||||
if i < 0 {
|
||||
return {}, false;
|
||||
}
|
||||
return array_get(m.entries, i).value, true;
|
||||
}
|
||||
|
||||
map_get_default :: proc(m: $M/Map($Key, $Value), key: Key, default: Value) -> (res: Value, ok: bool) #optional_ok {
|
||||
i := _map_find_or_fail(m, key);
|
||||
if i < 0 {
|
||||
return default, false;
|
||||
}
|
||||
return array_get(m.entries, i).value, true;
|
||||
}
|
||||
|
||||
map_get_ptr :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Value {
|
||||
i := _map_find_or_fail(m, key);
|
||||
if i < 0 {
|
||||
return nil;
|
||||
}
|
||||
return array_get_ptr(m.entries, i).value;
|
||||
}
|
||||
|
||||
map_set :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
|
||||
if array_len(m.hash) == 0 {
|
||||
_map_grow(m);
|
||||
}
|
||||
|
||||
i := _map_find_or_make(m, key);
|
||||
array_get_ptr(m.entries, i).value = value;
|
||||
if _map_full(m^) {
|
||||
_map_grow(m);
|
||||
}
|
||||
}
|
||||
|
||||
map_remove :: proc(m: ^$M/Map($Key, $Value), key: Key) {
|
||||
fr := _map_find_key(m^, key);
|
||||
if fr.entry_index >= 0 {
|
||||
_map_erase(m, fr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
map_reserve :: proc(m: ^$M/Map($Key, $Value), new_size: int) {
|
||||
nm: M;
|
||||
map_init(&nm, m.hash.allocator);
|
||||
array_resize(&nm.hash, new_size);
|
||||
array_reserve(&nm.entries, array_len(m.entries));
|
||||
|
||||
for i in 0..<new_size {
|
||||
array_set(&nm.hash, i, -1);
|
||||
}
|
||||
for i in 0..<array_len(m.entries) {
|
||||
e := array_get(m.entries, i);
|
||||
multi_map_insert(&nm, e.key, e.value);
|
||||
}
|
||||
|
||||
map_delete(m^);
|
||||
m^ = nm;
|
||||
}
|
||||
|
||||
map_clear :: proc(m: ^$M/Map($Key, $Value)) {
|
||||
array_clear(&m.hash);
|
||||
array_clear(&m.entries);
|
||||
}
|
||||
|
||||
|
||||
|
||||
multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Map_Entry(Key, Value) {
|
||||
i := _map_find_or_fail(m, key);
|
||||
if i < 0 {
|
||||
return nil;
|
||||
}
|
||||
return array_get_ptr(m.entries, i);
|
||||
}
|
||||
|
||||
multi_map_find_next :: proc(m: $M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> ^Map_Entry(Key, Value) {
|
||||
i := e.next;
|
||||
for i >= 0 {
|
||||
it := array_get_ptr(m.entries, i);
|
||||
if it.hash == e.hash && it.key == e.key {
|
||||
return it;
|
||||
}
|
||||
i = it.next;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
multi_map_count :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
|
||||
n := 0;
|
||||
e := multi_map_find_first(m, key);
|
||||
for e != nil {
|
||||
n += 1;
|
||||
e = multi_map_find_next(m, e);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
multi_map_get :: proc{multi_map_get_array, multi_map_get_slice};
|
||||
|
||||
multi_map_get_array :: proc(m: $M/Map($Key, $Value), key: Key, items: ^Array(Value)) {
|
||||
if items == nil {
|
||||
return;
|
||||
}
|
||||
e := multi_map_find_first(m, key);
|
||||
for e != nil {
|
||||
array_append(items, e.value);
|
||||
e = multi_map_find_next(m, e);
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_get_slice :: proc(m: $M/Map($Key, $Value), key: Key, items: []Value) {
|
||||
e := multi_map_find_first(m, key);
|
||||
i := 0;
|
||||
for e != nil && i < len(items) {
|
||||
items[i] = e.value;
|
||||
i += 1;
|
||||
e = multi_map_find_next(m, e);
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_get_as_slice :: proc(m: $M/Map($Key, $Value), key: Key) -> []Value {
|
||||
items: Array(Value);
|
||||
array_init(&items, 0);
|
||||
|
||||
e := multi_map_find_first(m, key);
|
||||
for e != nil {
|
||||
array_append(&items, e.value);
|
||||
e = multi_map_find_next(m, e);
|
||||
}
|
||||
|
||||
return array_slice(items);
|
||||
}
|
||||
|
||||
|
||||
multi_map_insert :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
|
||||
if array_len(m.hash) == 0 {
|
||||
_map_grow(m);
|
||||
}
|
||||
|
||||
i := _map_make(m, key);
|
||||
array_get_ptr(m.entries, i).value = value;
|
||||
if _map_full(m^) {
|
||||
_map_grow(m);
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_remove :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) {
|
||||
fr := _map_find_entry(m, e);
|
||||
if fr.entry_index >= 0 {
|
||||
_map_erase(m, fr);
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_remove_all :: proc(m: ^$M/Map($Key, $Value), key: Key) {
|
||||
for map_exist(m^, key) {
|
||||
map_remove(m, key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Internal
|
||||
|
||||
|
||||
Map_Find_Result :: struct {
|
||||
hash_index: int,
|
||||
entry_prev: int,
|
||||
entry_index: int,
|
||||
}
|
||||
|
||||
_map_add_entry :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int where intrinsics.type_is_valid_map_key(Key) {
|
||||
hasher := intrinsics.type_hasher_proc(Key);
|
||||
|
||||
e: Map_Entry(Key, Value);
|
||||
e.key = key;
|
||||
e.hash = hasher(&e.key, 0);
|
||||
e.next = -1;
|
||||
idx := array_len(m.entries);
|
||||
array_push(&m.entries, e);
|
||||
return idx;
|
||||
}
|
||||
|
||||
_map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) {
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next;
|
||||
}
|
||||
|
||||
if fr.entry_index == array_len(m.entries)-1 {
|
||||
array_pop_back(&m.entries);
|
||||
return;
|
||||
}
|
||||
|
||||
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1));
|
||||
last := _map_find_key(m^, array_get(m.entries, fr.entry_index).key);
|
||||
|
||||
if last.entry_prev < 0 {
|
||||
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index;
|
||||
} else {
|
||||
array_set(&m.hash, last.hash_index, fr.entry_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_map_find_key :: proc(m: $M/Map($Key, $Value), key: Key) -> Map_Find_Result where intrinsics.type_is_valid_map_key(Key) {
|
||||
fr: Map_Find_Result;
|
||||
fr.hash_index = -1;
|
||||
fr.entry_prev = -1;
|
||||
fr.entry_index = -1;
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr;
|
||||
}
|
||||
|
||||
hasher := intrinsics.type_hasher_proc(Key);
|
||||
|
||||
key := key;
|
||||
hash := hasher(&key, 0);
|
||||
|
||||
fr.hash_index = int(hash % uintptr(array_len(m.hash)));
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index);
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index);
|
||||
if it.hash == hash && it.key == key {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = it.next;
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
_map_find_entry :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> Map_Find_Result {
|
||||
fr: Map_Find_Result;
|
||||
fr.hash_index = -1;
|
||||
fr.entry_prev = -1;
|
||||
fr.entry_index = -1;
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr;
|
||||
}
|
||||
|
||||
fr.hash_index = int(e.hash % uintptr(array_len(m.hash)));
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index);
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index);
|
||||
if it == e {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = it.next;
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
_map_find_or_fail :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
|
||||
return _map_find_key(m, key).entry_index;
|
||||
}
|
||||
_map_find_or_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
|
||||
fr := _map_find_key(m^, key);
|
||||
if fr.entry_index >= 0 {
|
||||
return fr.entry_index;
|
||||
}
|
||||
|
||||
i := _map_add_entry(m, key);
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
_map_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
|
||||
fr := _map_find_key(m^, key);
|
||||
i := _map_add_entry(m, key);
|
||||
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i;
|
||||
}
|
||||
|
||||
array_get_ptr(m.entries, i).next = fr.entry_index;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
_map_full :: proc(m: $M/Map($Key, $Value)) -> bool {
|
||||
// TODO(bill): Determine good max load factor
|
||||
return array_len(m.entries) >= (array_len(m.hash) / 4)*3;
|
||||
}
|
||||
|
||||
_map_grow :: proc(m: ^$M/Map($Key, $Value)) {
|
||||
new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate
|
||||
map_reserve(m, new_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package container
|
||||
|
||||
Priority_Queue :: struct($T: typeid) {
|
||||
data: Array(T),
|
||||
len: int,
|
||||
priority: proc(item: T) -> int,
|
||||
}
|
||||
|
||||
priority_queue_init_none :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, allocator := context.allocator) {
|
||||
queue_init_len(q, f, 0, allocator);
|
||||
}
|
||||
priority_queue_init_len :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, allocator := context.allocator) {
|
||||
queue_init_len_cap(q, f, 0, 16, allocator);
|
||||
}
|
||||
priority_queue_init_len_cap :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, cap: int, allocator := context.allocator) {
|
||||
array_init(&q.data, len, cap, allocator);
|
||||
q.len = len;
|
||||
q.priority = f;
|
||||
}
|
||||
|
||||
priority_queue_init :: proc{priority_queue_init_none, priority_queue_init_len, priority_queue_init_len_cap};
|
||||
|
||||
|
||||
priority_queue_delete :: proc(q: $Q/Priority_Queue($T)) {
|
||||
array_delete(q.data);
|
||||
}
|
||||
|
||||
priority_queue_clear :: proc(q: ^$Q/Priority_Queue($T)) {
|
||||
q.len = 0;
|
||||
}
|
||||
|
||||
priority_queue_len :: proc(q: $Q/Priority_Queue($T)) -> int {
|
||||
return q.len;
|
||||
}
|
||||
|
||||
priority_queue_cap :: proc(q: $Q/Priority_Queue($T)) -> int {
|
||||
return array_cap(q.data);
|
||||
}
|
||||
|
||||
priority_queue_space :: proc(q: $Q/Priority_Queue($T)) -> int {
|
||||
return array_len(q.data) - q.len;
|
||||
}
|
||||
|
||||
priority_queue_reserve :: proc(q: ^$Q/Priority_Queue($T), capacity: int) {
|
||||
if capacity > q.len {
|
||||
array_resize(&q.data, new_capacity);
|
||||
}
|
||||
}
|
||||
|
||||
priority_queue_resize :: proc(q: ^$Q/Priority_Queue($T), length: int) {
|
||||
if length > q.len {
|
||||
array_resize(&q.data, new_capacity);
|
||||
}
|
||||
q.len = length;
|
||||
}
|
||||
|
||||
_priority_queue_grow :: proc(q: ^$Q/Priority_Queue($T), min_capacity: int = 0) {
|
||||
new_capacity := max(array_len(q.data)*2 + 8, min_capacity);
|
||||
array_resize(&q.data, new_capacity);
|
||||
}
|
||||
|
||||
|
||||
priority_queue_push :: proc(q: ^$Q/Priority_Queue($T), item: T) {
|
||||
if array_len(q.data) - q.len == 0 {
|
||||
_priority_queue_grow(q);
|
||||
}
|
||||
|
||||
s := array_slice(q.data);
|
||||
s[q.len] = item;
|
||||
|
||||
i := q.len;
|
||||
for i > 0 {
|
||||
p := (i - 1) / 2;
|
||||
if q.priority(s[p]) <= q.priority(item) do break;
|
||||
s[i] = s[p];
|
||||
i = p;
|
||||
}
|
||||
|
||||
q.len += 1;
|
||||
if q.len > 0 do s[i] = item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
priority_queue_pop :: proc(q: ^$Q/Priority_Queue($T)) -> T {
|
||||
assert(q.len > 0);
|
||||
|
||||
s := array_slice(q.data);
|
||||
min := s[0];
|
||||
root := s[q.len-1];
|
||||
q.len -= 1;
|
||||
|
||||
i := 0;
|
||||
for i * 2 + 1 < q.len {
|
||||
a := i * 2 + 1;
|
||||
b := i * 2 + 2;
|
||||
c := b < q.len && q.priority(s[b]) < q.priority(s[a]) ? b : a;
|
||||
|
||||
if q.priority(s[c]) >= q.priority(root) do break;
|
||||
s[i] = s[c];
|
||||
i = c;
|
||||
}
|
||||
|
||||
if q.len > 0 do s[i] = root;
|
||||
return min;
|
||||
}
|
||||
|
||||
priority_queue_peek :: proc(q: ^$Q/Priority_Queue($T)) -> T {
|
||||
assert(q.len > 0);
|
||||
|
||||
s := array_slice(q.data);
|
||||
return s[0];
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package container
|
||||
|
||||
Queue :: struct($T: typeid) {
|
||||
data: Array(T),
|
||||
len: int,
|
||||
offset: int,
|
||||
}
|
||||
|
||||
/*
|
||||
queue_init :: proc{
|
||||
queue_init_none,
|
||||
queue_init_len,
|
||||
queue_init_len_cap,
|
||||
}
|
||||
queue_delete
|
||||
queue_clear
|
||||
queue_len
|
||||
queue_cap
|
||||
queue_space
|
||||
queue_get
|
||||
queue_set
|
||||
queue_reserve
|
||||
queue_resize
|
||||
queue_push :: proc{
|
||||
queue_push_back,
|
||||
queue_push_elems,
|
||||
};
|
||||
queue_push_front
|
||||
queue_pop_front
|
||||
queue_pop_back
|
||||
queue_consume
|
||||
*/
|
||||
|
||||
queue_init_none :: proc(q: ^$Q/Queue($T), allocator := context.allocator) {
|
||||
queue_init_len(q, 0, allocator);
|
||||
}
|
||||
queue_init_len :: proc(q: ^$Q/Queue($T), len: int, allocator := context.allocator) {
|
||||
queue_init_len_cap(q, 0, 16, allocator);
|
||||
}
|
||||
queue_init_len_cap :: proc(q: ^$Q/Queue($T), len: int, cap: int, allocator := context.allocator) {
|
||||
array_init(&q.data, len, cap, allocator);
|
||||
q.len = len;
|
||||
q.offset = 0;
|
||||
}
|
||||
|
||||
queue_init :: proc{queue_init_none, queue_init_len, queue_init_len_cap};
|
||||
|
||||
queue_delete :: proc(q: $Q/Queue($T)) {
|
||||
array_delete(q.data);
|
||||
}
|
||||
|
||||
queue_clear :: proc(q: ^$Q/Queue($T)) {
|
||||
q.len = 0;
|
||||
}
|
||||
|
||||
queue_len :: proc(q: $Q/Queue($T)) -> int {
|
||||
return q.len;
|
||||
}
|
||||
|
||||
queue_cap :: proc(q: $Q/Queue($T)) -> int {
|
||||
return array_cap(q.data);
|
||||
}
|
||||
|
||||
queue_space :: proc(q: $Q/Queue($T)) -> int {
|
||||
return array_len(q.data) - q.len;
|
||||
}
|
||||
|
||||
queue_get :: proc(q: $Q/Queue($T), index: int) -> T {
|
||||
i := (index + q.offset) % array_len(q.data);
|
||||
data := array_slice(q.data);
|
||||
return data[i];
|
||||
}
|
||||
|
||||
queue_set :: proc(q: ^$Q/Queue($T), index: int, item: T) {
|
||||
i := (index + q.offset) % array_len(q.data);
|
||||
data := array_slice(q.data);
|
||||
data[i] = item;
|
||||
}
|
||||
|
||||
|
||||
queue_reserve :: proc(q: ^$Q/Queue($T), capacity: int) {
|
||||
if capacity > q.len {
|
||||
_queue_increase_capacity(q, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
queue_resize :: proc(q: ^$Q/Queue($T), length: int) {
|
||||
if length > q.len {
|
||||
_queue_increase_capacity(q, length);
|
||||
}
|
||||
q.len = length;
|
||||
}
|
||||
|
||||
queue_push_back :: proc(q: ^$Q/Queue($T), item: T) {
|
||||
if queue_space(q^) == 0 {
|
||||
_queue_grow(q);
|
||||
}
|
||||
|
||||
queue_set(q, q.len, item);
|
||||
q.len += 1;
|
||||
}
|
||||
|
||||
queue_push_front :: proc(q: ^$Q/Queue($T), item: T) {
|
||||
if queue_space(q^) == 0 {
|
||||
_queue_grow(q);
|
||||
}
|
||||
|
||||
q.offset = (q.offset - 1 + array_len(q.data)) % array_len(q.data);
|
||||
q.len += 1;
|
||||
queue_set(q, 0, item);
|
||||
}
|
||||
|
||||
queue_pop_front :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
assert(q.len > 0);
|
||||
item := queue_get(q^, 0);
|
||||
q.offset = (q.offset + 1) % array_len(q.data);
|
||||
q.len -= 1;
|
||||
if q.len == 0 {
|
||||
q.offset = 0;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
queue_pop_back :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
assert(q.len > 0);
|
||||
item := queue_get(q^, q.len-1);
|
||||
q.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
queue_consume :: proc(q: ^$Q/Queue($T), count: int) {
|
||||
q.offset = (q.offset + count) & array_len(q.data);
|
||||
q.len -= count;
|
||||
}
|
||||
|
||||
|
||||
queue_push_elems :: proc(q: ^$Q/Queue($T), items: ..T) {
|
||||
if queue_space(q^) < len(items) {
|
||||
_queue_grow(q, q.len + len(items));
|
||||
}
|
||||
size := array_len(q.data);
|
||||
insert := (q.offset + q.len) % size;
|
||||
|
||||
to_insert := len(items);
|
||||
if insert + to_insert > size {
|
||||
to_insert = size - insert;
|
||||
}
|
||||
|
||||
the_items := items[:];
|
||||
|
||||
data := array_slice(q.data);
|
||||
|
||||
q.len += copy(data[insert:][:to_insert], the_items);
|
||||
the_items = the_items[to_insert:];
|
||||
q.len += copy(data[:], the_items);
|
||||
}
|
||||
|
||||
queue_push :: proc{queue_push_back, queue_push_elems};
|
||||
|
||||
|
||||
|
||||
_queue_increase_capacity :: proc(q: ^$Q/Queue($T), new_capacity: int) {
|
||||
end := array_len(q.data);
|
||||
array_resize(&q.data, new_capacity);
|
||||
if q.offset + q.len > end {
|
||||
end_items := q.len + end;
|
||||
data := array_slice(q.data);
|
||||
copy(data[new_capacity-end_items:][:end_items], data[q.offset:][:end_items]);
|
||||
q.offset += new_capacity - end;
|
||||
}
|
||||
}
|
||||
_queue_grow :: proc(q: ^$Q/Queue($T), min_capacity: int = 0) {
|
||||
new_capacity := max(array_len(q.data)*2 + 8, min_capacity);
|
||||
_queue_increase_capacity(q, new_capacity);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package container
|
||||
|
||||
|
||||
Ring :: struct($T: typeid) {
|
||||
next, prev: ^Ring(T),
|
||||
value: T,
|
||||
}
|
||||
|
||||
ring_init :: proc(r: ^$R/Ring) -> ^R {
|
||||
r.prev, r.next = r, r;
|
||||
return r;
|
||||
}
|
||||
|
||||
ring_next :: proc(r: ^$R/Ring) -> ^R {
|
||||
if r.next == nil {
|
||||
return ring_init(r);
|
||||
}
|
||||
return r.next;
|
||||
}
|
||||
ring_prev :: proc(r: ^$R/Ring) -> ^R {
|
||||
if r.prev == nil {
|
||||
return ring_init(r);
|
||||
}
|
||||
return r.prev;
|
||||
}
|
||||
|
||||
|
||||
ring_move :: proc(r: ^$R/Ring, n: int) -> ^R {
|
||||
if r.next == nil {
|
||||
return ring_init(r);
|
||||
}
|
||||
|
||||
switch {
|
||||
case n < 0:
|
||||
for _ in n..<0 {
|
||||
r = r.prev;
|
||||
}
|
||||
case n > 0:
|
||||
for _ in 0..<n {
|
||||
r = r.next;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
ring_link :: proc(r, s: ^$R/Ring) -> ^R {
|
||||
n := ring_next(r);
|
||||
if s != nil {
|
||||
p := ring_prev(s);
|
||||
r.next = s;
|
||||
s.prev = r;
|
||||
n.prev = p;
|
||||
p.next = n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
ring_unlink :: proc(r: ^$R/Ring, n: int) -> ^R {
|
||||
if n <= 0 {
|
||||
return nil;
|
||||
}
|
||||
return ring_link(r, ring_move(r, n+1));
|
||||
}
|
||||
ring_len :: proc(r: ^$R/Ring) -> int {
|
||||
n := 0;
|
||||
if r != nil {
|
||||
n = 1;
|
||||
for p := ring_next(&p); p != r; p = p.next {
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
package container
|
||||
|
||||
Set :: struct {
|
||||
hash: Array(int),
|
||||
entries: Array(Set_Entry),
|
||||
}
|
||||
|
||||
Set_Entry :: struct {
|
||||
key: u64,
|
||||
next: int,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
set_init :: proc{
|
||||
set_init_none,
|
||||
set_init_cap,
|
||||
}
|
||||
set_delete
|
||||
|
||||
set_in
|
||||
set_not_in
|
||||
set_add
|
||||
set_remove
|
||||
set_reserve
|
||||
set_clear
|
||||
*/
|
||||
|
||||
set_init :: proc{set_init_none, set_init_cap};
|
||||
|
||||
set_init_none :: proc(m: ^Set, allocator := context.allocator) {
|
||||
m.hash.allocator = allocator;
|
||||
m.entries.allocator = allocator;
|
||||
}
|
||||
|
||||
set_init_cap :: proc(m: ^Set, cap: int, allocator := context.allocator) {
|
||||
m.hash.allocator = allocator;
|
||||
m.entries.allocator = allocator;
|
||||
set_reserve(m, cap);
|
||||
}
|
||||
|
||||
set_delete :: proc(m: Set) {
|
||||
array_delete(m.hash);
|
||||
array_delete(m.entries);
|
||||
}
|
||||
|
||||
|
||||
set_in :: proc(m: Set, key: u64) -> bool {
|
||||
return _set_find_or_fail(m, key) >= 0;
|
||||
}
|
||||
set_not_in :: proc(m: Set, key: u64) -> bool {
|
||||
return _set_find_or_fail(m, key) < 0;
|
||||
}
|
||||
|
||||
set_add :: proc(m: ^Set, key: u64) {
|
||||
if array_len(m.hash) == 0 {
|
||||
_set_grow(m);
|
||||
}
|
||||
|
||||
_ = _set_find_or_make(m, key);
|
||||
if _set_full(m^) {
|
||||
_set_grow(m);
|
||||
}
|
||||
}
|
||||
|
||||
set_remove :: proc(m: ^Set, key: u64) {
|
||||
fr := _set_find_key(m^, key);
|
||||
if fr.entry_index >= 0 {
|
||||
_set_erase(m, fr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set_reserve :: proc(m: ^Set, new_size: int) {
|
||||
nm: Set;
|
||||
set_init(&nm, m.hash.allocator);
|
||||
array_resize(&nm.hash, new_size);
|
||||
array_reserve(&nm.entries, array_len(m.entries));
|
||||
|
||||
for i in 0..<new_size {
|
||||
array_set(&nm.hash, i, -1);
|
||||
}
|
||||
for i in 0..<array_len(m.entries) {
|
||||
e := array_get(m.entries, i);
|
||||
set_add(&nm, e.key);
|
||||
}
|
||||
|
||||
set_delete(m^);
|
||||
m^ = nm;
|
||||
}
|
||||
|
||||
set_clear :: proc(m: ^Set) {
|
||||
array_clear(&m.hash);
|
||||
array_clear(&m.entries);
|
||||
}
|
||||
|
||||
|
||||
set_equal :: proc(a, b: Set) -> bool {
|
||||
a_entries := array_slice(a.entries);
|
||||
b_entries := array_slice(b.entries);
|
||||
if len(a_entries) != len(b_entries) {
|
||||
return false;
|
||||
}
|
||||
for e in a_entries {
|
||||
if set_not_in(b, e.key) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Internal
|
||||
|
||||
_set_add_entry :: proc(m: ^Set, key: u64) -> int {
|
||||
e: Set_Entry;
|
||||
e.key = key;
|
||||
e.next = -1;
|
||||
idx := array_len(m.entries);
|
||||
array_push(&m.entries, e);
|
||||
return idx;
|
||||
}
|
||||
|
||||
_set_erase :: proc(m: ^Set, fr: Map_Find_Result) {
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next;
|
||||
}
|
||||
|
||||
if fr.entry_index == array_len(m.entries)-1 {
|
||||
array_pop_back(&m.entries);
|
||||
return;
|
||||
}
|
||||
|
||||
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1));
|
||||
last := _set_find_key(m^, array_get(m.entries, fr.entry_index).key);
|
||||
|
||||
if last.entry_prev < 0 {
|
||||
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index;
|
||||
} else {
|
||||
array_set(&m.hash, last.hash_index, fr.entry_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_set_find_key :: proc(m: Set, key: u64) -> Map_Find_Result {
|
||||
fr: Map_Find_Result;
|
||||
fr.hash_index = -1;
|
||||
fr.entry_prev = -1;
|
||||
fr.entry_index = -1;
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr;
|
||||
}
|
||||
|
||||
fr.hash_index = int(key % u64(array_len(m.hash)));
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index);
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index);
|
||||
if it.key == key {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = it.next;
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
_set_find_entry :: proc(m: ^Set, e: ^Set_Entry) -> Map_Find_Result {
|
||||
fr: Map_Find_Result;
|
||||
fr.hash_index = -1;
|
||||
fr.entry_prev = -1;
|
||||
fr.entry_index = -1;
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr;
|
||||
}
|
||||
|
||||
fr.hash_index = int(e.key % u64(array_len(m.hash)));
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index);
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index);
|
||||
if it == e {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = it.next;
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
_set_find_or_fail :: proc(m: Set, key: u64) -> int {
|
||||
return _set_find_key(m, key).entry_index;
|
||||
}
|
||||
_set_find_or_make :: proc(m: ^Set, key: u64) -> int {
|
||||
fr := _set_find_key(m^, key);
|
||||
if fr.entry_index >= 0 {
|
||||
return fr.entry_index;
|
||||
}
|
||||
|
||||
i := _set_add_entry(m, key);
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
_set_make :: proc(m: ^Set, key: u64) -> int {
|
||||
fr := _set_find_key(m^, key);
|
||||
i := _set_add_entry(m, key);
|
||||
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i);
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i;
|
||||
}
|
||||
|
||||
array_get_ptr(m.entries, i).next = fr.entry_index;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
_set_full :: proc(m: Set) -> bool {
|
||||
// TODO(bill): Determine good max load factor
|
||||
return array_len(m.entries) >= (array_len(m.hash) / 4)*3;
|
||||
}
|
||||
|
||||
_set_grow :: proc(m: ^Set) {
|
||||
new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate
|
||||
set_reserve(m, new_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package container
|
||||
|
||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||
data: [N]T,
|
||||
len: int,
|
||||
}
|
||||
|
||||
|
||||
small_array_len :: proc(a: $A/Small_Array) -> int {
|
||||
return a.len;
|
||||
}
|
||||
|
||||
small_array_cap :: proc(a: $A/Small_Array) -> int {
|
||||
return len(a.data);
|
||||
}
|
||||
|
||||
small_array_space :: proc(a: $A/Small_Array) -> int {
|
||||
return len(a.data) - a.len;
|
||||
}
|
||||
|
||||
small_array_slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
return a.data[:a.len];
|
||||
}
|
||||
|
||||
|
||||
small_array_get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T {
|
||||
return a.data[index];
|
||||
}
|
||||
small_array_get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
|
||||
return &a.data[index];
|
||||
}
|
||||
|
||||
small_array_set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
|
||||
a.data[index] = item;
|
||||
}
|
||||
|
||||
small_array_resize :: proc(a: ^$A/Small_Array, length: int) {
|
||||
a.len = min(length, len(a.data));
|
||||
}
|
||||
|
||||
|
||||
small_array_push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < len(a.data) {
|
||||
a.len += 1;
|
||||
a.data[a.len-1] = item;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
small_array_push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < len(a.data) {
|
||||
a.len += 1;
|
||||
data := small_array_slice(a);
|
||||
copy(data[1:], data[:]);
|
||||
data[0] = item;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
small_array_pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc);
|
||||
item := a.data[a.len-1];
|
||||
a.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
small_array_pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc);
|
||||
item := a.data[0];
|
||||
s := small_array_slice(a);
|
||||
copy(s[:], s[1:]);
|
||||
a.len -= 1;
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
small_array_consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
assert(condition=a.len >= count, loc=loc);
|
||||
a.len -= count;
|
||||
}
|
||||
|
||||
small_array_clear :: proc(a: ^$A/Small_Array($N, $T)) {
|
||||
small_array_resize(a, 0);
|
||||
}
|
||||
|
||||
small_array_push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
n := copy(a.data[a.len:], items[:]);
|
||||
a.len += n;
|
||||
}
|
||||
|
||||
small_array_push :: proc{small_array_push_back, small_array_push_back_elems};
|
||||
small_array_append :: proc{small_array_push_back, small_array_push_back_elems};
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package dynlib
|
||||
|
||||
Library :: opaque rawptr;
|
||||
Library :: distinct rawptr;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// +build linux, darwin
|
||||
// +build linux, darwin, freebsd
|
||||
package dynlib
|
||||
|
||||
import "core:os"
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
flags := os.RTLD_NOW;
|
||||
if global_symbols do flags |= os.RTLD_GLOBAL;
|
||||
if global_symbols {
|
||||
flags |= os.RTLD_GLOBAL;
|
||||
}
|
||||
lib := os.dlopen(path, flags);
|
||||
return Library(lib), lib != nil;
|
||||
}
|
||||
@@ -18,4 +20,4 @@ symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found:
|
||||
ptr = os.dlsym(rawptr(library), symbol);
|
||||
found = ptr != nil;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
// +build windows
|
||||
package dynlib
|
||||
|
||||
import "core:sys/win32"
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
||||
|
||||
wide_path := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
handle := cast(Library)win32.load_library_w(wide_path);
|
||||
handle := cast(Library)win32.LoadLibraryW(wide_path);
|
||||
return handle, handle != nil;
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) -> bool {
|
||||
ok := win32.free_library(cast(win32.Hmodule)library);
|
||||
ok := win32.FreeLibrary(cast(win32.HMODULE)library);
|
||||
return bool(ok);
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
c_str := strings.clone_to_cstring(symbol, context.temp_allocator);
|
||||
ptr = win32.get_proc_address(cast(win32.Hmodule)library, c_str);
|
||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str);
|
||||
found = ptr != nil;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,35 +2,35 @@ package base32
|
||||
|
||||
// @note(zh): Encoding utility for Base32
|
||||
// A secondary param can be used to supply a custom alphabet to
|
||||
// @link(encode) and a matching decoding table to @link(decode).
|
||||
// @link(encode) and a matching decoding table to @link(decode).
|
||||
// If none is supplied it just uses the standard Base32 alphabet.
|
||||
// Incase your specific version does not use padding, you may
|
||||
// truncate it from the encoded output.
|
||||
|
||||
ENC_TABLE := [32]byte {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7'
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7',
|
||||
};
|
||||
|
||||
PADDING :: '=';
|
||||
|
||||
DEC_TABLE := [?]u8 {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
|
||||
@@ -41,7 +41,7 @@ encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocato
|
||||
}
|
||||
|
||||
@private
|
||||
_encode :: inline proc "contextless"(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
|
||||
_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
|
||||
out := out;
|
||||
data := data;
|
||||
|
||||
@@ -92,10 +92,11 @@ _encode :: inline proc "contextless"(out, data: []byte, ENC_TBL := ENC_TABLE, al
|
||||
}
|
||||
|
||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
|
||||
if len(data) == 0 do return []byte{};
|
||||
if len(data) == 0 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
outi := 0;
|
||||
olen := len(data);
|
||||
data := data;
|
||||
|
||||
out := make([]byte, len(data) / 8 * 5, allocator);
|
||||
@@ -113,7 +114,9 @@ decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocato
|
||||
data = data[1:];
|
||||
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
|
||||
assert(!(len(data) + j < 8 - 1), "Corrupted input");
|
||||
for k := 0; k < 8-1-j; k +=1 do assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input");
|
||||
for k := 0; k < 8-1-j; k +=1 {
|
||||
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input");
|
||||
}
|
||||
dlen, end = j, true;
|
||||
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input");
|
||||
break;
|
||||
@@ -142,4 +145,4 @@ decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocato
|
||||
outi += 5;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,46 +2,48 @@ package base64
|
||||
|
||||
// @note(zh): Encoding utility for Base64
|
||||
// A secondary param can be used to supply a custom alphabet to
|
||||
// @link(encode) and a matching decoding table to @link(decode).
|
||||
// @link(encode) and a matching decoding table to @link(decode).
|
||||
// If none is supplied it just uses the standard Base64 alphabet.
|
||||
// Incase your specific version does not use padding, you may
|
||||
// truncate it from the encoded output.
|
||||
|
||||
ENC_TABLE := [64]byte {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/',
|
||||
};
|
||||
|
||||
PADDING :: '=';
|
||||
|
||||
DEC_TABLE := [128]int {
|
||||
-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, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, -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, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, -1, -1, -1, -1, -1,
|
||||
};
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check {
|
||||
length := len(data);
|
||||
if length == 0 do return "";
|
||||
if length == 0 {
|
||||
return "";
|
||||
}
|
||||
|
||||
out_length := ((4 * length / 3) + 3) &~ 3;
|
||||
out := make([]byte, out_length, allocator);
|
||||
@@ -49,24 +51,26 @@ encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocato
|
||||
c0, c1, c2, block: int;
|
||||
|
||||
for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
|
||||
c0, c1, c2 = int(data[i]), 0, 0;
|
||||
c0, c1, c2 = int(data[i]), -1, -1;
|
||||
|
||||
if i + 1 < length do c1 = int(data[i + 1]);
|
||||
if i + 2 < length do c2 = int(data[i + 2]);
|
||||
if i + 1 < length { c1 = int(data[i + 1]); }
|
||||
if i + 2 < length { c2 = int(data[i + 2]); }
|
||||
|
||||
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0);
|
||||
|
||||
out[d] = ENC_TBL[block >> 18 & 63];
|
||||
out[d + 1] = ENC_TBL[block >> 12 & 63];
|
||||
out[d + 2] = c1 == 0 ? PADDING : ENC_TBL[block >> 6 & 63];
|
||||
out[d + 3] = c2 == 0 ? PADDING : ENC_TBL[block & 63];
|
||||
out[d + 2] = c1 == -1 ? PADDING : ENC_TBL[block >> 6 & 63];
|
||||
out[d + 3] = c2 == -1 ? PADDING : ENC_TBL[block & 63];
|
||||
}
|
||||
return string(out);
|
||||
}
|
||||
|
||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
|
||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check {
|
||||
length := len(data);
|
||||
if length == 0 do return []byte{};
|
||||
if length == 0 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0;
|
||||
out_length := ((length * 6) >> 3) - pad_count;
|
||||
@@ -90,4 +94,4 @@ decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocato
|
||||
out[j + 2] = byte(b2);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
+43
-31
@@ -32,7 +32,9 @@ Parser :: struct {
|
||||
|
||||
print_value :: proc(value: Value, pretty := true, indent := 0) {
|
||||
print_indent :: proc(indent: int) {
|
||||
for _ in 0..<indent do fmt.print("\t");
|
||||
for _ in 0..<indent {
|
||||
fmt.print("\t");
|
||||
}
|
||||
}
|
||||
|
||||
switch v in value {
|
||||
@@ -42,22 +44,22 @@ print_value :: proc(value: Value, pretty := true, indent := 0) {
|
||||
case string: fmt.print(v);
|
||||
case Array:
|
||||
fmt.print("[");
|
||||
if pretty do fmt.println();
|
||||
if pretty { fmt.println(); }
|
||||
for e, i in v {
|
||||
if pretty {
|
||||
print_indent(indent+1);
|
||||
print_value(e, pretty, indent+1);
|
||||
fmt.println(",");
|
||||
} else {
|
||||
if i > 0 do fmt.print(", ");
|
||||
if i > 0 { fmt.print(", "); }
|
||||
print_value(e);
|
||||
}
|
||||
}
|
||||
if pretty do print_indent(indent);
|
||||
if pretty { print_indent(indent); }
|
||||
fmt.print("]");
|
||||
case Dict:
|
||||
fmt.print("{");
|
||||
if pretty do fmt.println();
|
||||
if pretty { fmt.println(); }
|
||||
|
||||
i := 0;
|
||||
for name, val in v {
|
||||
@@ -67,14 +69,14 @@ print_value :: proc(value: Value, pretty := true, indent := 0) {
|
||||
print_value(val, pretty, indent+1);
|
||||
fmt.println(",");
|
||||
} else {
|
||||
if i > 0 do fmt.print(", ");
|
||||
if i > 0 { fmt.print(", "); }
|
||||
fmt.printf("%s = ", name);
|
||||
print_value(val, pretty, indent+1);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if pretty do print_indent(indent);
|
||||
if pretty { print_indent(indent); }
|
||||
fmt.print("}");
|
||||
case:
|
||||
fmt.print("nil");
|
||||
@@ -149,17 +151,23 @@ destroy :: proc(p: ^Parser) {
|
||||
destroy_value :: proc(value: Value) {
|
||||
#partial switch v in value {
|
||||
case Array:
|
||||
for elem in v do destroy_value(elem);
|
||||
for elem in v {
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(v);
|
||||
|
||||
case Dict:
|
||||
for _, dv in v do destroy_value(dv);
|
||||
for _, dv in v {
|
||||
destroy_value(dv);
|
||||
}
|
||||
delete(v);
|
||||
}
|
||||
}
|
||||
|
||||
delete(p.tokens);
|
||||
for s in p.allocated_strings do delete(s);
|
||||
for s in p.allocated_strings {
|
||||
delete(s);
|
||||
}
|
||||
delete(p.allocated_strings);
|
||||
delete(p.dict_stack);
|
||||
|
||||
@@ -193,9 +201,9 @@ next_token :: proc(p: ^Parser) -> Token {
|
||||
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
||||
hex_to_int :: proc(c: byte) -> int {
|
||||
switch c {
|
||||
case '0'..'9': return int(c-'0');
|
||||
case 'a'..'f': return int(c-'a')+10;
|
||||
case 'A'..'F': return int(c-'A')+10;
|
||||
case '0'..='9': return int(c-'0');
|
||||
case 'a'..='f': return int(c-'a')+10;
|
||||
case 'A'..='F': return int(c-'A')+10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -233,7 +241,7 @@ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool
|
||||
case '"': r = '"';
|
||||
case '\'': r = '\'';
|
||||
|
||||
case '0'..'7':
|
||||
case '0'..='7':
|
||||
v := int(c-'0');
|
||||
if len(s) < 2 {
|
||||
return;
|
||||
@@ -348,7 +356,9 @@ expect_token :: proc(p: ^Parser, kind: Kind) -> Token {
|
||||
prev := p.curr_token;
|
||||
if prev.kind != kind {
|
||||
got := prev.lit;
|
||||
if got == "\n" do got = ";";
|
||||
if got == "\n" {
|
||||
got = ";";
|
||||
}
|
||||
error(p, prev.pos, "Expected %s, got %s", kind_to_string[kind], got);
|
||||
}
|
||||
next_token(p);
|
||||
@@ -411,7 +421,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
case .Ident:
|
||||
next_token(p);
|
||||
v, ok := lookup_value(p, tok.lit);
|
||||
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
|
||||
if !ok { error(p, tok.pos, "Undeclared identifier %s", tok.lit); }
|
||||
return v, tok.pos;
|
||||
|
||||
case .True:
|
||||
@@ -427,16 +437,18 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
|
||||
case .Integer:
|
||||
next_token(p);
|
||||
return strconv.parse_i64(tok.lit), tok.pos;
|
||||
i, _ := strconv.parse_i64(tok.lit);
|
||||
return i, tok.pos;
|
||||
|
||||
case .Float:
|
||||
next_token(p);
|
||||
return strconv.parse_f64(tok.lit), tok.pos;
|
||||
f, _ := strconv.parse_f64(tok.lit);
|
||||
return f, tok.pos;
|
||||
|
||||
case .String:
|
||||
next_token(p);
|
||||
str, ok := unquote_string(p, tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
if !ok { error(p, tok.pos, "Unable to unquote string"); }
|
||||
return string(str), tok.pos;
|
||||
|
||||
case .Open_Paren:
|
||||
@@ -478,7 +490,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
}
|
||||
|
||||
name, ok := unquote_string(p, name_tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
if !ok { error(p, tok.pos, "Unable to unquote string"); }
|
||||
expect_token(p, .Assign);
|
||||
elem, _ := parse_expr(p);
|
||||
|
||||
@@ -518,7 +530,7 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
|
||||
continue;
|
||||
}
|
||||
name, usok := unquote_string(p, tok);
|
||||
if !usok do error(p, tok.pos, "Unable to unquote string");
|
||||
if !usok { error(p, tok.pos, "Unable to unquote string"); }
|
||||
val, found := d[name];
|
||||
if !found {
|
||||
error(p, tok.pos, "Field %s not found in dictionary", name);
|
||||
@@ -592,7 +604,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
next_token(p);
|
||||
tok := expect_token(p, .String);
|
||||
v, ok := lookup_value(p, tok.lit);
|
||||
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
|
||||
if !ok { error(p, tok.pos, "Undeclared identifier %s", tok.lit); }
|
||||
return parse_atom_expr(p, v, tok.pos);
|
||||
|
||||
case .Add, .Sub:
|
||||
@@ -601,8 +613,8 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
expr, pos := parse_unary_expr(p);
|
||||
|
||||
#partial switch e in expr {
|
||||
case i64: if op.kind == .Sub do return -e, pos;
|
||||
case f64: if op.kind == .Sub do return -e, pos;
|
||||
case i64: if op.kind == .Sub { return -e, pos; }
|
||||
case f64: if op.kind == .Sub { return -e, pos; }
|
||||
case:
|
||||
error(p, op.pos, "Unary operator %s can only be used on integers or floats", op.lit);
|
||||
return nil, op.pos;
|
||||
@@ -665,9 +677,9 @@ match_values :: proc(left, right: ^Value) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, bool) {
|
||||
calculate_binary_value :: proc(p: ^Parser, op: Kind, a_, b_: Value) -> (Value, bool) {
|
||||
// TODO(bill): Calculate value as you go!
|
||||
x, y := a, b;
|
||||
x, y := a_, b_;
|
||||
match_values(&x, &y);
|
||||
|
||||
|
||||
@@ -676,7 +688,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
|
||||
|
||||
case bool:
|
||||
b, ok := y.(bool);
|
||||
if !ok do return nil, false;
|
||||
if !ok { return nil, false; }
|
||||
#partial switch op {
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
@@ -686,7 +698,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
|
||||
|
||||
case i64:
|
||||
b, ok := y.(i64);
|
||||
if !ok do return nil, false;
|
||||
if !ok { return nil, false; }
|
||||
#partial switch op {
|
||||
case .Add: return a + b, true;
|
||||
case .Sub: return a - b, true;
|
||||
@@ -703,7 +715,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
|
||||
|
||||
case f64:
|
||||
b, ok := y.(f64);
|
||||
if !ok do return nil, false;
|
||||
if !ok { return nil, false; }
|
||||
|
||||
#partial switch op {
|
||||
case .Add: return a + b, true;
|
||||
@@ -720,7 +732,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
|
||||
|
||||
case string:
|
||||
b, ok := y.(string);
|
||||
if !ok do return nil, false;
|
||||
if !ok { return nil, false; }
|
||||
|
||||
#partial switch op {
|
||||
case .Add:
|
||||
@@ -823,7 +835,7 @@ parse_assignment :: proc(p: ^Parser) -> bool {
|
||||
if allow_token(p, .Ident) || allow_token(p, .String) {
|
||||
expect_token(p, .Assign);
|
||||
name, ok := unquote_string(p, tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
if !ok { error(p, tok.pos, "Unable to unquote string"); }
|
||||
expr, _ := parse_expr(p);
|
||||
d := top_dict(p);
|
||||
if _, ok2 := d[name]; ok2 {
|
||||
|
||||
@@ -162,9 +162,9 @@ token_lookup :: proc(ident: string) -> Kind {
|
||||
return Ident;
|
||||
}
|
||||
|
||||
is_literal :: proc(tok: Kind) -> bool do return _literal_start < tok && tok < _literal_end;
|
||||
is_operator :: proc(tok: Kind) -> bool do return _operator_start < tok && tok < _operator_end;
|
||||
is_keyword :: proc(tok: Kind) -> bool do return _keyword_start < tok && tok < _keyword_end;
|
||||
is_literal :: proc(tok: Kind) -> bool { return _literal_start < tok && tok < _literal_end; }
|
||||
is_operator :: proc(tok: Kind) -> bool { return _operator_start < tok && tok < _operator_end; }
|
||||
is_keyword :: proc(tok: Kind) -> bool { return _keyword_start < tok && tok < _keyword_end; }
|
||||
|
||||
|
||||
tokenizer_init :: proc(t: ^Tokenizer, src: []byte, file := "") {
|
||||
@@ -232,7 +232,7 @@ get_pos :: proc(t: ^Tokenizer) -> Pos {
|
||||
|
||||
is_letter :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case 'a'..'z', 'A'..'Z', '_':
|
||||
case 'a'..='z', 'A'..='Z', '_':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -240,7 +240,7 @@ is_letter :: proc(r: rune) -> bool {
|
||||
|
||||
is_digit :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case '0'..'9':
|
||||
case '0'..='9':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -273,9 +273,9 @@ scan_identifier :: proc(t: ^Tokenizer) -> string {
|
||||
|
||||
digit_value :: proc(r: rune) -> int {
|
||||
switch r {
|
||||
case '0'..'9': return int(r - '0');
|
||||
case 'a'..'f': return int(r - 'a' + 10);
|
||||
case 'A'..'F': return int(r - 'A' + 10);
|
||||
case '0'..='9': return int(r - '0');
|
||||
case 'a'..='f': return int(r - 'a' + 10);
|
||||
case 'A'..='F': return int(r - 'A' + 10);
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
// package csv reads and writes comma-separated values (CSV) files.
|
||||
// This package supports the format described in RFC 4180 <https://tools.ietf.org/html/rfc4180.html>
|
||||
package csv
|
||||
|
||||
import "core:bufio"
|
||||
import "core:bytes"
|
||||
import "core:io"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
// Reader is a data structure used for reading records from a CSV-encoded file
|
||||
//
|
||||
// The associated procedures for Reader expects its input to conform to RFC 4180.
|
||||
Reader :: struct {
|
||||
// comma is the field delimiter
|
||||
// reader_init will set it to be ','
|
||||
// A "comma" must be a valid rune, nor can it be \r, \n, or the Unicode replacement character (0xfffd)
|
||||
comma: rune,
|
||||
|
||||
// comment, if not 0, is the comment character
|
||||
// Lines beginning with the comment character without a preceding whitespace are ignored
|
||||
comment: rune,
|
||||
|
||||
// fields_per_record is the number of expected fields per record
|
||||
// if fields_per_record is >0, 'read' requires each record to have that field count
|
||||
// if fields_per_record is 0, 'read' sets it to the field count in the first record
|
||||
// if fields_per_record is <0, no check is made and records may have a variable field count
|
||||
fields_per_record: int,
|
||||
|
||||
// If trim_leading_space is true, leading whitespace in a field is ignored
|
||||
// This is done even if the field delimiter (comma), is whitespace
|
||||
trim_leading_space: bool,
|
||||
|
||||
// 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,
|
||||
|
||||
// reuse_record controls whether calls to 'read' may return a slice using the backing buffer
|
||||
// for performance
|
||||
// By default, each call to 'read' returns a newly allocated slice
|
||||
reuse_record: bool,
|
||||
|
||||
// reuse_record_buffer controls whether calls to 'read' clone the strings of each field or uses
|
||||
// the data stored in record buffer for performance
|
||||
// By default, each call to 'read' clones the strings of each field
|
||||
reuse_record_buffer: bool,
|
||||
|
||||
|
||||
// internal buffers
|
||||
r: bufio.Reader,
|
||||
line_count: int, // current line being read in the CSV file
|
||||
raw_buffer: [dynamic]byte,
|
||||
record_buffer: [dynamic]byte,
|
||||
field_indices: [dynamic]int,
|
||||
last_record: [dynamic]string,
|
||||
sr: strings.Reader, // used by reader_init_with_string
|
||||
}
|
||||
|
||||
|
||||
Reader_Error_Kind :: enum {
|
||||
Bare_Quote,
|
||||
Quote,
|
||||
Field_Count,
|
||||
Invalid_Delim,
|
||||
}
|
||||
|
||||
reader_error_kind_string := [Reader_Error_Kind]string{
|
||||
.Bare_Quote = "bare \" in non-quoted field",
|
||||
.Quote = "extra or missing \" in quoted field",
|
||||
.Field_Count = "wrong field count",
|
||||
.Invalid_Delim = "invalid delimiter",
|
||||
};
|
||||
|
||||
Reader_Error :: struct {
|
||||
kind: Reader_Error_Kind,
|
||||
start_line: int,
|
||||
line: int,
|
||||
column: int,
|
||||
expected, got: int, // used by .Field_Count
|
||||
}
|
||||
|
||||
Error :: union {
|
||||
Reader_Error,
|
||||
io.Error,
|
||||
}
|
||||
|
||||
DEFAULT_RECORD_BUFFER_CAPACITY :: 256;
|
||||
|
||||
// reader_init initializes a new Reader from r
|
||||
reader_init :: proc(reader: ^Reader, r: io.Reader, buffer_allocator := context.allocator) {
|
||||
reader.comma = ',';
|
||||
|
||||
context.allocator = buffer_allocator;
|
||||
reserve(&reader.record_buffer, DEFAULT_RECORD_BUFFER_CAPACITY);
|
||||
reserve(&reader.raw_buffer, 0);
|
||||
reserve(&reader.field_indices, 0);
|
||||
reserve(&reader.last_record, 0);
|
||||
bufio.reader_init(&reader.r, r);
|
||||
}
|
||||
|
||||
|
||||
// reader_init_with_string initializes a new Reader from s
|
||||
reader_init_with_string :: proc(reader: ^Reader, s: string, buffer_allocator := context.allocator) {
|
||||
strings.reader_init(&reader.sr, s);
|
||||
r, _ := io.to_reader(strings.reader_to_stream(&reader.sr));
|
||||
reader_init(reader, r, buffer_allocator);
|
||||
}
|
||||
|
||||
// reader_destroy destroys a Reader
|
||||
reader_destroy :: proc(r: ^Reader) {
|
||||
delete(r.raw_buffer);
|
||||
delete(r.record_buffer);
|
||||
delete(r.field_indices);
|
||||
delete(r.last_record);
|
||||
bufio.reader_destroy(&r.r);
|
||||
}
|
||||
|
||||
// read reads a single record (a slice of fields) from r
|
||||
//
|
||||
// All \r\n sequences are normalized to \n, including multi-line field
|
||||
read :: proc(r: ^Reader, allocator := context.allocator) -> (record: []string, err: Error) {
|
||||
if r.reuse_record {
|
||||
record, err = _read_record(r, &r.last_record, allocator);
|
||||
resize(&r.last_record, len(record));
|
||||
copy(r.last_record[:], record);
|
||||
} else {
|
||||
record, err = _read_record(r, nil, allocator);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// is_io_error checks where an Error is a specific io.Error kind
|
||||
is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
|
||||
if v, ok := err.(io.Error); ok {
|
||||
return v == io_err;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// read_all reads all the remaining records from r.
|
||||
// Each record is a slice of fields.
|
||||
// read_all is defined to read until an EOF, and does not treat, and does not treat EOF as an error
|
||||
read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Error) {
|
||||
context.allocator = allocator;
|
||||
records: [dynamic][]string;
|
||||
for {
|
||||
record, rerr := _read_record(r, nil, allocator);
|
||||
if is_io_error(rerr, .EOF) {
|
||||
return records[:], nil;
|
||||
}
|
||||
if rerr != nil {
|
||||
return nil, rerr;
|
||||
}
|
||||
append(&records, record);
|
||||
}
|
||||
}
|
||||
|
||||
// read reads a single record (a slice of fields) from the provided input.
|
||||
read_from_string :: proc(input: string, record_allocator := context.allocator, buffer_allocator := context.allocator) -> (record: []string, n: int, err: Error) {
|
||||
ir: strings.Reader;
|
||||
strings.reader_init(&ir, input);
|
||||
input_reader, _ := io.to_reader(strings.reader_to_stream(&ir));
|
||||
|
||||
r: Reader;
|
||||
reader_init(&r, input_reader, buffer_allocator);
|
||||
defer reader_destroy(&r);
|
||||
record, err = read(&r, record_allocator);
|
||||
n = int(r.r.r);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// read_all reads all the remaining records from the provided input.
|
||||
read_all_from_string :: proc(input: string, records_allocator := context.allocator, buffer_allocator := context.allocator) -> ([][]string, Error) {
|
||||
ir: strings.Reader;
|
||||
strings.reader_init(&ir, input);
|
||||
input_reader, _ := io.to_reader(strings.reader_to_stream(&ir));
|
||||
|
||||
r: Reader;
|
||||
reader_init(&r, input_reader, buffer_allocator);
|
||||
defer reader_destroy(&r);
|
||||
return read_all(&r, records_allocator);
|
||||
}
|
||||
|
||||
@private
|
||||
is_valid_delim :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case 0, '"', '\r', '\n', utf8.RUNE_ERROR:
|
||||
return false;
|
||||
}
|
||||
return utf8.valid_rune(r);
|
||||
}
|
||||
|
||||
@private
|
||||
_read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) {
|
||||
read_line :: proc(r: ^Reader) -> ([]byte, io.Error) {
|
||||
line, err := bufio.reader_read_slice(&r.r, '\n');
|
||||
if err == .Buffer_Full {
|
||||
clear(&r.raw_buffer);
|
||||
append(&r.raw_buffer, ..line);
|
||||
for err == .Buffer_Full {
|
||||
line, err = bufio.reader_read_slice(&r.r, '\n');
|
||||
append(&r.raw_buffer, ..line);
|
||||
}
|
||||
line = r.raw_buffer[:];
|
||||
}
|
||||
if len(line) > 0 && err == .EOF {
|
||||
err = nil;
|
||||
if line[len(line)-1] == '\r' {
|
||||
line = line[:len(line)-1];
|
||||
}
|
||||
}
|
||||
r.line_count += 1;
|
||||
|
||||
// normalize \r\n to \n
|
||||
n := len(line);
|
||||
for n >= 2 && string(line[n-2:]) == "\r\n" {
|
||||
line[n-2] = '\n';
|
||||
line = line[:n-1];
|
||||
}
|
||||
|
||||
return line, err;
|
||||
}
|
||||
|
||||
length_newline :: proc(b: []byte) -> int {
|
||||
if len(b) > 0 && b[len(b)-1] == '\n' {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
next_rune :: proc(b: []byte) -> rune {
|
||||
r, _ := utf8.decode_rune(b);
|
||||
return r;
|
||||
}
|
||||
|
||||
if r.comma == r.comment ||
|
||||
!is_valid_delim(r.comma) ||
|
||||
(r.comment != 0 && !is_valid_delim(r.comment)) {
|
||||
err := Reader_Error{
|
||||
kind = .Invalid_Delim,
|
||||
line = r.line_count,
|
||||
};
|
||||
return nil, err;
|
||||
}
|
||||
|
||||
line, full_line: []byte;
|
||||
err_read: io.Error;
|
||||
for err_read == nil {
|
||||
line, err_read = read_line(r);
|
||||
if r.comment != 0 && next_rune(line) == r.comment {
|
||||
line = nil;
|
||||
continue;
|
||||
}
|
||||
if err_read == nil && len(line) == length_newline(line) {
|
||||
line = nil;
|
||||
continue;
|
||||
}
|
||||
full_line = line;
|
||||
break;
|
||||
}
|
||||
|
||||
if is_io_error(err_read, .EOF) {
|
||||
return nil, err_read;
|
||||
}
|
||||
|
||||
err: Error;
|
||||
quote_len :: len(`"`);
|
||||
comma_len := utf8.rune_size(r.comma);
|
||||
record_line := r.line_count;
|
||||
clear(&r.record_buffer);
|
||||
clear(&r.field_indices);
|
||||
|
||||
parse_field: for {
|
||||
if r.trim_leading_space {
|
||||
line = bytes.trim_left_space(line);
|
||||
}
|
||||
if len(line) == 0 || line[0] != '"' {
|
||||
i := bytes.index_rune(line, r.comma);
|
||||
field := line;
|
||||
if i >= 0 {
|
||||
field = field[:i];
|
||||
} else {
|
||||
field = field[:len(field) - length_newline(field)];
|
||||
}
|
||||
|
||||
if !r.lazy_quotes {
|
||||
if j := bytes.index_byte(field, '"'); j >= 0 {
|
||||
column := utf8.rune_count(full_line[:len(full_line) - len(line[j:])]);
|
||||
err = Reader_Error{
|
||||
kind = .Bare_Quote,
|
||||
start_line = record_line,
|
||||
line = r.line_count,
|
||||
column = column,
|
||||
};
|
||||
break parse_field;
|
||||
}
|
||||
}
|
||||
append(&r.record_buffer, ..field);
|
||||
append(&r.field_indices, len(r.record_buffer));
|
||||
if i >= 0 {
|
||||
line = line[i+comma_len:];
|
||||
continue parse_field;
|
||||
}
|
||||
break parse_field;
|
||||
|
||||
} else {
|
||||
line = line[quote_len:];
|
||||
for {
|
||||
i := bytes.index_byte(line, '"');
|
||||
switch {
|
||||
case i >= 0:
|
||||
append(&r.record_buffer, ..line[:i]);
|
||||
line = line[i+quote_len:];
|
||||
switch ch := next_rune(line); {
|
||||
case ch == '"': // append quote
|
||||
append(&r.record_buffer, '"');
|
||||
line = line[quote_len:];
|
||||
case ch == r.comma: // end of field
|
||||
line = line[comma_len:];
|
||||
append(&r.field_indices, len(r.record_buffer));
|
||||
continue parse_field;
|
||||
case length_newline(line) == len(line): // end of line
|
||||
append(&r.field_indices, len(r.record_buffer));
|
||||
break parse_field;
|
||||
case r.lazy_quotes: // bare quote
|
||||
append(&r.record_buffer, '"');
|
||||
case: // invalid non-escaped quote
|
||||
column := utf8.rune_count(full_line[:len(full_line) - len(line) - quote_len]);
|
||||
err = Reader_Error{
|
||||
kind = .Quote,
|
||||
start_line = record_line,
|
||||
line = r.line_count,
|
||||
column = column,
|
||||
};
|
||||
break parse_field;
|
||||
}
|
||||
|
||||
case len(line) > 0:
|
||||
append(&r.record_buffer, ..line);
|
||||
if err_read != nil {
|
||||
break parse_field;
|
||||
}
|
||||
line, err_read = read_line(r);
|
||||
if is_io_error(err_read, .EOF) {
|
||||
err_read = nil;
|
||||
}
|
||||
full_line = line;
|
||||
|
||||
case:
|
||||
if !r.lazy_quotes && err_read == nil {
|
||||
column := utf8.rune_count(full_line);
|
||||
err = Reader_Error{
|
||||
kind = .Quote,
|
||||
start_line = record_line,
|
||||
line = r.line_count,
|
||||
column = column,
|
||||
};
|
||||
break parse_field;
|
||||
}
|
||||
append(&r.field_indices, len(r.record_buffer));
|
||||
break parse_field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && err_read != nil {
|
||||
err = err_read;
|
||||
}
|
||||
|
||||
context.allocator = allocator;
|
||||
dst := dst;
|
||||
str := string(r.record_buffer[:]);
|
||||
if dst == nil {
|
||||
// use local variable
|
||||
dst = &([dynamic]string){};
|
||||
}
|
||||
clear(dst);
|
||||
resize(dst, len(r.field_indices));
|
||||
pre_idx: int;
|
||||
for idx, i in r.field_indices {
|
||||
field := str[pre_idx:idx];
|
||||
if !r.reuse_record_buffer {
|
||||
field = strings.clone(field);
|
||||
}
|
||||
dst[i] = field;
|
||||
pre_idx = idx;
|
||||
}
|
||||
|
||||
if r.fields_per_record > 0 {
|
||||
if len(dst) != r.fields_per_record && err == nil {
|
||||
err = Reader_Error{
|
||||
kind = .Field_Count,
|
||||
start_line = record_line,
|
||||
line = r.line_count,
|
||||
expected = r.fields_per_record,
|
||||
got = len(dst),
|
||||
};
|
||||
}
|
||||
} else if r.fields_per_record == 0 {
|
||||
r.fields_per_record = len(dst);
|
||||
}
|
||||
return dst[:], err;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package csv
|
||||
|
||||
import "core:io"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
// Writer is a data structure used for writing records using a CSV-encoding.
|
||||
Writer :: struct {
|
||||
// Field delimiter (set to ',' with writer_init)
|
||||
comma: rune,
|
||||
|
||||
// if set to true, \r\n will be used as the line terminator
|
||||
use_crlf: bool,
|
||||
|
||||
w: io.Writer,
|
||||
}
|
||||
|
||||
// writer_init initializes a Writer that writes to w
|
||||
writer_init :: proc(writer: ^Writer, w: io.Writer) {
|
||||
writer.comma = ',';
|
||||
writer.w = w;
|
||||
}
|
||||
|
||||
// write writes a single CSV records to w with any of the necessarily quoting.
|
||||
// A record is a slice of strings, where each string is a single field.
|
||||
//
|
||||
// If the underlying io.Writer requires flushing, make sure to call io.flush
|
||||
write :: proc(w: ^Writer, record: []string) -> io.Error {
|
||||
CHAR_SET :: "\n\r\"";
|
||||
|
||||
field_needs_quoting :: proc(w: ^Writer, field: string) -> bool {
|
||||
switch {
|
||||
case field == "": // No need to quote empty strings
|
||||
return false;
|
||||
case field == `\.`: // Postgres is weird
|
||||
return true;
|
||||
case w.comma < utf8.RUNE_SELF: // ASCII optimization
|
||||
for i in 0..<len(field) {
|
||||
switch field[i] {
|
||||
case '\n', '\r', '"', byte(w.comma):
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case:
|
||||
if strings.contains_rune(field, w.comma) >= 0 {
|
||||
return true;
|
||||
}
|
||||
if strings.contains_any(field, CHAR_SET) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Leading spaces need quoting
|
||||
r, _ := utf8.decode_rune_in_string(field);
|
||||
return strings.is_space(r);
|
||||
}
|
||||
|
||||
if !is_valid_delim(w.comma) {
|
||||
return .No_Progress; // TODO(bill): Is this a good error?
|
||||
}
|
||||
|
||||
for _, field_idx in record {
|
||||
// NOTE(bill): declared like this so that the field can be modified later if necessary
|
||||
field := record[field_idx];
|
||||
|
||||
if field_idx > 0 {
|
||||
if _, err := io.write_rune(w.w, w.comma); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if !field_needs_quoting(w, field) {
|
||||
if _, err := io.write_string(w.w, field); err != nil {
|
||||
return err;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if err := io.write_byte(w.w, '"'); err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
for len(field) > 0 {
|
||||
i := strings.index_any(field, CHAR_SET);
|
||||
if i < 0 {
|
||||
i = len(field);
|
||||
}
|
||||
|
||||
if _, err := io.write_string(w.w, field[:i]); err != nil {
|
||||
return err;
|
||||
}
|
||||
field = field[i:];
|
||||
|
||||
if len(field) > 0 {
|
||||
switch field[0] {
|
||||
case '\r':
|
||||
if !w.use_crlf {
|
||||
if err := io.write_byte(w.w, '\r'); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
case '\n':
|
||||
if w.use_crlf {
|
||||
if _, err := io.write_string(w.w, "\r\n"); err != nil {
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
if err := io.write_byte(w.w, '\n'); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
case '"':
|
||||
if _, err := io.write_string(w.w, `""`); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
field = field[1:];
|
||||
}
|
||||
}
|
||||
if err := io.write_byte(w.w, '"'); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if w.use_crlf {
|
||||
_, err := io.write_string(w.w, "\r\n");
|
||||
return err;
|
||||
}
|
||||
return io.write_byte(w.w, '\n');
|
||||
}
|
||||
|
||||
// write_all writes multiple CSV records to w using write, and then flushes (if necessary).
|
||||
write_all :: proc(w: ^Writer, records: [][]string) -> io.Error {
|
||||
for record in records {
|
||||
err := write(w, record);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return writer_flush(w);
|
||||
}
|
||||
|
||||
// writer_flush flushes the underlying io.Writer.
|
||||
// If the underlying io.Writer does not support flush, nil is returned.
|
||||
writer_flush :: proc(w: ^Writer) -> io.Error {
|
||||
return io.flush(auto_cast w.w);
|
||||
}
|
||||
@@ -5,19 +5,20 @@ import "core:math/bits"
|
||||
import "core:runtime"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
|
||||
Marshal_Error :: enum {
|
||||
None,
|
||||
Unsupported_Type,
|
||||
Invalid_Data,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
|
||||
b := strings.make_builder(allocator);
|
||||
b: strings.Builder;
|
||||
strings.init_builder(&b, allocator);
|
||||
|
||||
err := marshal_arg(&b, v);
|
||||
|
||||
if err != Marshal_Error.None {
|
||||
if err != .None {
|
||||
strings.destroy_builder(&b);
|
||||
return nil, err;
|
||||
}
|
||||
@@ -34,15 +35,15 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
using runtime;
|
||||
if v == nil {
|
||||
write_string(b, "null");
|
||||
return Marshal_Error.None;
|
||||
return .None;
|
||||
}
|
||||
|
||||
ti := type_info_base(type_info_of(v.id));
|
||||
a := any{v.data, ti.id};
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
switch info in ti.variant {
|
||||
case Type_Info_Named:
|
||||
panic("Unreachable");
|
||||
unreachable();
|
||||
|
||||
case Type_Info_Integer:
|
||||
buf: [21]byte;
|
||||
@@ -88,6 +89,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
case Type_Info_Float:
|
||||
val: f64;
|
||||
switch f in a {
|
||||
case f16: val = f64(f);
|
||||
case f32: val = f64(f);
|
||||
case f64: val = f64(f);
|
||||
}
|
||||
@@ -108,7 +110,10 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
write_string(b, string(s));
|
||||
|
||||
case Type_Info_Complex:
|
||||
return Marshal_Error.Unsupported_Type;
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Quaternion:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_String:
|
||||
switch s in a {
|
||||
@@ -125,27 +130,39 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
case b32: val = bool(b);
|
||||
case b64: val = bool(b);
|
||||
}
|
||||
write_string(b, val ? "true" : "false");
|
||||
write_string_builder(b, val ? "true" : "false");
|
||||
|
||||
case Type_Info_Any:
|
||||
return Marshal_Error.Unsupported_Type;
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Type_Id:
|
||||
return Marshal_Error.Unsupported_Type;
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Pointer:
|
||||
return Marshal_Error.Unsupported_Type;
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Procedure:
|
||||
return Marshal_Error.Unsupported_Type;
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Tuple:
|
||||
return Marshal_Error.Unsupported_Type;
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Relative_Slice:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Array:
|
||||
write_byte(b, '[');
|
||||
for i in 0..<info.count {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
@@ -156,7 +173,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
write_byte(b, '[');
|
||||
array := cast(^mem.Raw_Dynamic_Array)v.data;
|
||||
for i in 0..<array.len {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(array.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
@@ -167,7 +184,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
write_byte(b, '[');
|
||||
slice := cast(^mem.Raw_Slice)v.data;
|
||||
for i in 0..<slice.len {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(slice.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
@@ -180,7 +197,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
write_byte(b, '{');
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
return Marshal_Error.Unsupported_Type;
|
||||
return .Unsupported_Type;
|
||||
}
|
||||
entries := &m.entries;
|
||||
gs := type_info_base(info.generated_struct).variant.(Type_Info_Struct);
|
||||
@@ -189,21 +206,15 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
entry_size := ed.elem_size;
|
||||
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size);
|
||||
header := cast(^Map_Entry_Header)data;
|
||||
|
||||
if reflect.is_string(info.key) {
|
||||
marshal_arg(b, header.key.str);
|
||||
} else {
|
||||
marshal_arg(b, any{rawptr(&header.key.hash), info.key.id});
|
||||
}
|
||||
key := rawptr(data + entry_type.offsets[2]);
|
||||
value := rawptr(data + entry_type.offsets[3]);
|
||||
|
||||
marshal_arg(b, any{key, info.key.id});
|
||||
write_string(b, ": ");
|
||||
|
||||
value := data + entry_type.offsets[2];
|
||||
marshal_arg(b, any{rawptr(value), info.value.id});
|
||||
marshal_arg(b, any{value, info.value.id});
|
||||
}
|
||||
}
|
||||
write_byte(b, '}');
|
||||
@@ -211,7 +222,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
case Type_Info_Struct:
|
||||
write_byte(b, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
write_quoted_string(b, name);
|
||||
write_string(b, ": ");
|
||||
|
||||
@@ -248,34 +259,6 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
case Type_Info_Enum:
|
||||
return marshal_arg(b, any{v.data, info.base.id});
|
||||
|
||||
case Type_Info_Bit_Field:
|
||||
data: u64 = 0;
|
||||
switch ti.size {
|
||||
case 1: data = cast(u64) (^u8)(v.data)^;
|
||||
case 2: data = cast(u64)(^u16)(v.data)^;
|
||||
case 4: data = cast(u64)(^u32)(v.data)^;
|
||||
case 8: data = cast(u64)(^u64)(v.data)^;
|
||||
}
|
||||
|
||||
write_byte(b, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
|
||||
bits := u64(info.bits[i]);
|
||||
offset := u64(info.offsets[i]);
|
||||
marshal_arg(b, name);
|
||||
write_string(b, ": ");
|
||||
|
||||
n := 8*u64(size_of(u64));
|
||||
sa := n - bits;
|
||||
u := data>>offset;
|
||||
u <<= sa;
|
||||
u >>= sa;
|
||||
|
||||
write_u64(b, u, 10);
|
||||
}
|
||||
write_byte(b, '}');
|
||||
|
||||
case Type_Info_Bit_Set:
|
||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||
if ti == nil {
|
||||
@@ -305,26 +288,29 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
bit_data = u64(x);
|
||||
case 16:
|
||||
x := (^u16)(v.data)^;
|
||||
if do_byte_swap do x = bits.byte_swap(x);
|
||||
if do_byte_swap {
|
||||
x = bits.byte_swap(x);
|
||||
}
|
||||
bit_data = u64(x);
|
||||
case 32:
|
||||
x := (^u32)(v.data)^;
|
||||
if do_byte_swap do x = bits.byte_swap(x);
|
||||
if do_byte_swap {
|
||||
x = bits.byte_swap(x);
|
||||
}
|
||||
bit_data = u64(x);
|
||||
case 64:
|
||||
x := (^u64)(v.data)^;
|
||||
if do_byte_swap do x = bits.byte_swap(x);
|
||||
if do_byte_swap {
|
||||
x = bits.byte_swap(x);
|
||||
}
|
||||
bit_data = u64(x);
|
||||
case: panic("unknown bit_size size");
|
||||
}
|
||||
write_u64(b, bit_data);
|
||||
|
||||
|
||||
return Marshal_Error.Unsupported_Type;
|
||||
|
||||
case Type_Info_Opaque:
|
||||
return Marshal_Error.Unsupported_Type;
|
||||
return .Unsupported_Type;
|
||||
}
|
||||
|
||||
return Marshal_Error.None;
|
||||
return .None;
|
||||
}
|
||||
|
||||
@@ -5,16 +5,18 @@ import "core:unicode/utf8"
|
||||
import "core:strconv"
|
||||
|
||||
Parser :: struct {
|
||||
tok: Tokenizer,
|
||||
prev_token: Token,
|
||||
curr_token: Token,
|
||||
spec: Specification,
|
||||
allocator: mem.Allocator,
|
||||
tok: Tokenizer,
|
||||
prev_token: Token,
|
||||
curr_token: Token,
|
||||
spec: Specification,
|
||||
allocator: mem.Allocator,
|
||||
unmarshal_data: any,
|
||||
parse_integers: bool,
|
||||
}
|
||||
|
||||
make_parser :: proc(data: []byte, spec := Specification.JSON, allocator := context.allocator) -> Parser {
|
||||
make_parser :: proc(data: []byte, spec := Specification.JSON, parse_integers := false, allocator := context.allocator) -> Parser {
|
||||
p: Parser;
|
||||
p.tok = make_tokenizer(data, spec);
|
||||
p.tok = make_tokenizer(data, spec, parse_integers);
|
||||
p.spec = spec;
|
||||
p.allocator = allocator;
|
||||
assert(p.allocator.procedure != nil);
|
||||
@@ -22,9 +24,9 @@ make_parser :: proc(data: []byte, spec := Specification.JSON, allocator := conte
|
||||
return p;
|
||||
}
|
||||
|
||||
parse :: proc(data: []byte, spec := Specification.JSON, allocator := context.allocator) -> (Value, Error) {
|
||||
parse :: proc(data: []byte, spec := Specification.JSON, parse_integers := false, allocator := context.allocator) -> (Value, Error) {
|
||||
context.allocator = allocator;
|
||||
p := make_parser(data, spec, allocator);
|
||||
p := make_parser(data, spec, parse_integers, allocator);
|
||||
|
||||
if p.spec == Specification.JSON5 {
|
||||
return parse_value(&p);
|
||||
@@ -46,7 +48,7 @@ advance_token :: proc(p: ^Parser) -> (Token, Error) {
|
||||
}
|
||||
|
||||
|
||||
allow_token :: proc(p: ^Parser, kind: Kind) -> bool {
|
||||
allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool {
|
||||
if p.curr_token.kind == kind {
|
||||
advance_token(p);
|
||||
return true;
|
||||
@@ -54,13 +56,13 @@ allow_token :: proc(p: ^Parser, kind: Kind) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
expect_token :: proc(p: ^Parser, kind: Kind) -> Error {
|
||||
expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
|
||||
prev := p.curr_token;
|
||||
advance_token(p);
|
||||
if prev.kind == kind {
|
||||
return Error.None;
|
||||
return .None;
|
||||
}
|
||||
return Error.Unexpected_Token;
|
||||
return .Unexpected_Token;
|
||||
}
|
||||
|
||||
|
||||
@@ -71,42 +73,44 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
|
||||
token := p.curr_token;
|
||||
#partial switch token.kind {
|
||||
case Kind.Null:
|
||||
case .Null:
|
||||
value.value = Null{};
|
||||
advance_token(p);
|
||||
return;
|
||||
case Kind.False:
|
||||
case .False:
|
||||
value.value = Boolean(false);
|
||||
advance_token(p);
|
||||
return;
|
||||
case Kind.True:
|
||||
case .True:
|
||||
value.value = Boolean(true);
|
||||
advance_token(p);
|
||||
return;
|
||||
|
||||
case Kind.Integer:
|
||||
value.value = Integer(strconv.parse_i64(token.text));
|
||||
case .Integer:
|
||||
i, _ := strconv.parse_i64(token.text);
|
||||
value.value = Integer(i);
|
||||
advance_token(p);
|
||||
return;
|
||||
case Kind.Float:
|
||||
value.value = Float(strconv.parse_f64(token.text));
|
||||
case .Float:
|
||||
f, _ := strconv.parse_f64(token.text);
|
||||
value.value = Float(f);
|
||||
advance_token(p);
|
||||
return;
|
||||
case Kind.String:
|
||||
case .String:
|
||||
value.value = String(unquote_string(token, p.spec, p.allocator));
|
||||
advance_token(p);
|
||||
return;
|
||||
|
||||
case Kind.Open_Brace:
|
||||
case .Open_Brace:
|
||||
return parse_object(p);
|
||||
|
||||
case Kind.Open_Bracket:
|
||||
case .Open_Bracket:
|
||||
return parse_array(p);
|
||||
|
||||
case:
|
||||
if p.spec == Specification.JSON5 {
|
||||
#partial switch token.kind {
|
||||
case Kind.Infinity:
|
||||
case .Infinity:
|
||||
inf: u64 = 0x7ff0000000000000;
|
||||
if token.text[0] == '-' {
|
||||
inf = 0xfff0000000000000;
|
||||
@@ -114,7 +118,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
value.value = transmute(f64)inf;
|
||||
advance_token(p);
|
||||
return;
|
||||
case Kind.NaN:
|
||||
case .NaN:
|
||||
nan: u64 = 0x7ff7ffffffffffff;
|
||||
if token.text[0] == '-' {
|
||||
nan = 0xfff7ffffffffffff;
|
||||
@@ -126,7 +130,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
}
|
||||
}
|
||||
|
||||
err = Error.Unexpected_Token;
|
||||
err = .Unexpected_Token;
|
||||
advance_token(p);
|
||||
return;
|
||||
}
|
||||
@@ -134,36 +138,36 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
value.pos = p.curr_token.pos;
|
||||
defer value.end = token_end_pos(p.prev_token);
|
||||
if err = expect_token(p, Kind.Open_Bracket); err != Error.None {
|
||||
if err = expect_token(p, .Open_Bracket); err != .None {
|
||||
return;
|
||||
}
|
||||
|
||||
array: Array;
|
||||
array.allocator = p.allocator;
|
||||
defer if err != Error.None {
|
||||
defer if err != .None {
|
||||
for elem in array {
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(array);
|
||||
}
|
||||
|
||||
for p.curr_token.kind != Kind.Close_Bracket {
|
||||
for p.curr_token.kind != .Close_Bracket {
|
||||
elem, elem_err := parse_value(p);
|
||||
if elem_err != Error.None {
|
||||
if elem_err != .None {
|
||||
err = elem_err;
|
||||
return;
|
||||
}
|
||||
append(&array, elem);
|
||||
|
||||
// Disallow trailing commas for the time being
|
||||
if allow_token(p, Kind.Comma) {
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if err = expect_token(p, Kind.Close_Bracket); err != Error.None {
|
||||
if err = expect_token(p, .Close_Bracket); err != .None {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -182,18 +186,18 @@ clone_string :: proc(s: string, allocator: mem.Allocator) -> string {
|
||||
parse_object_key :: proc(p: ^Parser) -> (key: string, err: Error) {
|
||||
tok := p.curr_token;
|
||||
if p.spec == Specification.JSON5 {
|
||||
if tok.kind == Kind.String {
|
||||
expect_token(p, Kind.String);
|
||||
if tok.kind == .String {
|
||||
expect_token(p, .String);
|
||||
key = unquote_string(tok, p.spec, p.allocator);
|
||||
return;
|
||||
} else if tok.kind == Kind.Ident {
|
||||
expect_token(p, Kind.Ident);
|
||||
} else if tok.kind == .Ident {
|
||||
expect_token(p, .Ident);
|
||||
key = clone_string(tok.text, p.allocator);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if tok_err := expect_token(p, Kind.String); tok_err != Error.None {
|
||||
err = Error.Expected_String_For_Object_Key;
|
||||
if tok_err := expect_token(p, .String); tok_err != .None {
|
||||
err = .Expected_String_For_Object_Key;
|
||||
return;
|
||||
}
|
||||
key = unquote_string(tok, p.spec, p.allocator);
|
||||
@@ -204,14 +208,14 @@ parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
value.pos = p.curr_token.pos;
|
||||
defer value.end = token_end_pos(p.prev_token);
|
||||
|
||||
if err = expect_token(p, Kind.Open_Brace); err != Error.None {
|
||||
if err = expect_token(p, .Open_Brace); err != .None {
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
obj: Object;
|
||||
obj.allocator = p.allocator;
|
||||
defer if err != Error.None {
|
||||
defer if err != .None {
|
||||
for key, elem in obj {
|
||||
delete(key, p.allocator);
|
||||
destroy_value(elem);
|
||||
@@ -219,30 +223,30 @@ parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
delete(obj);
|
||||
}
|
||||
|
||||
for p.curr_token.kind != Kind.Close_Brace {
|
||||
for p.curr_token.kind != .Close_Brace {
|
||||
key: string;
|
||||
key, err = parse_object_key(p);
|
||||
if err != Error.None {
|
||||
if err != .None {
|
||||
delete(key, p.allocator);
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
if colon_err := expect_token(p, Kind.Colon); colon_err != Error.None {
|
||||
err = Error.Expected_Colon_After_Key;
|
||||
if colon_err := expect_token(p, .Colon); colon_err != .None {
|
||||
err = .Expected_Colon_After_Key;
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
elem, elem_err := parse_value(p);
|
||||
if elem_err != Error.None {
|
||||
if elem_err != .None {
|
||||
err = elem_err;
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
if key in obj {
|
||||
err = Error.Duplicate_Object_Key;
|
||||
err = .Duplicate_Object_Key;
|
||||
value.pos = p.curr_token.pos;
|
||||
delete(key, p.allocator);
|
||||
return;
|
||||
@@ -252,12 +256,12 @@ parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
|
||||
if p.spec == Specification.JSON5 {
|
||||
// Allow trailing commas
|
||||
if allow_token(p, Kind.Comma) {
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Disallow trailing commas
|
||||
if allow_token(p, Kind.Comma) {
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
@@ -265,7 +269,7 @@ parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
}
|
||||
}
|
||||
|
||||
if err = expect_token(p, Kind.Close_Brace); err != Error.None {
|
||||
if err = expect_token(p, .Close_Brace); err != .None {
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
@@ -286,9 +290,9 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
|
||||
for c in s[2:4] {
|
||||
x: rune;
|
||||
switch c {
|
||||
case '0'..'9': x = c - '0';
|
||||
case 'a'..'f': x = c - 'a' + 10;
|
||||
case 'A'..'F': x = c - 'A' + 10;
|
||||
case '0'..='9': x = c - '0';
|
||||
case 'a'..='f': x = c - 'a' + 10;
|
||||
case 'A'..='F': x = c - 'A' + 10;
|
||||
case: return -1;
|
||||
}
|
||||
r = r*16 + x;
|
||||
@@ -304,9 +308,9 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
|
||||
for c in s[2:6] {
|
||||
x: rune;
|
||||
switch c {
|
||||
case '0'..'9': x = c - '0';
|
||||
case 'a'..'f': x = c - 'a' + 10;
|
||||
case 'A'..'F': x = c - 'A' + 10;
|
||||
case '0'..='9': x = c - '0';
|
||||
case 'a'..='f': x = c - 'a' + 10;
|
||||
case 'A'..='F': x = c - 'A' + 10;
|
||||
case: return -1;
|
||||
}
|
||||
r = r*16 + x;
|
||||
@@ -314,7 +318,7 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
|
||||
return r;
|
||||
}
|
||||
|
||||
if token.kind != Kind.String {
|
||||
if token.kind != .String {
|
||||
return "";
|
||||
}
|
||||
s := token.text;
|
||||
|
||||
@@ -4,12 +4,13 @@ import "core:unicode/utf8"
|
||||
|
||||
Token :: struct {
|
||||
using pos: Pos,
|
||||
kind: Kind,
|
||||
kind: Token_Kind,
|
||||
text: string,
|
||||
}
|
||||
|
||||
Kind :: enum {
|
||||
Token_Kind :: enum {
|
||||
Invalid,
|
||||
EOF,
|
||||
|
||||
Null,
|
||||
False,
|
||||
@@ -41,12 +42,13 @@ Tokenizer :: struct {
|
||||
w: int, // current rune width in bytes
|
||||
curr_line_offset: int,
|
||||
spec: Specification,
|
||||
parse_integers: bool,
|
||||
}
|
||||
|
||||
|
||||
|
||||
make_tokenizer :: proc(data: []byte, spec := Specification.JSON) -> Tokenizer {
|
||||
t := Tokenizer{pos = {line=1}, data = data, spec = spec};
|
||||
make_tokenizer :: proc(data: []byte, spec := Specification.JSON, parse_integers := false) -> Tokenizer {
|
||||
t := Tokenizer{pos = {line=1}, data = data, spec = spec, parse_integers = parse_integers};
|
||||
next_rune(&t);
|
||||
if t.r == utf8.RUNE_BOM {
|
||||
next_rune(&t);
|
||||
@@ -80,7 +82,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
for t.offset < len(t.data) {
|
||||
next_rune(t);
|
||||
switch t.r {
|
||||
case '0'..'9', 'a'..'f', 'A'..'F':
|
||||
case '0'..='9', 'a'..='f', 'A'..='F':
|
||||
// Okay
|
||||
case:
|
||||
return;
|
||||
@@ -98,7 +100,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
for i := 0; i < 4; i += 1 {
|
||||
r := next_rune(t);
|
||||
switch r {
|
||||
case '0'..'9', 'a'..'f', 'A'..'F':
|
||||
case '0'..='9', 'a'..='f', 'A'..='F':
|
||||
// Okay
|
||||
case:
|
||||
return false;
|
||||
@@ -122,7 +124,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
t.pos.column = 1;
|
||||
next_rune(t);
|
||||
case:
|
||||
if t.spec == Specification.JSON5 {
|
||||
if t.spec == .JSON5 {
|
||||
switch t.r {
|
||||
case 0x2028, 0x2029, 0xFEFF:
|
||||
next_rune(t);
|
||||
@@ -147,7 +149,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
skip_alphanum :: proc(t: ^Tokenizer) {
|
||||
for t.offset < len(t.data) {
|
||||
switch next_rune(t) {
|
||||
case 'A'..'Z', 'a'..'z', '0'..'9', '_':
|
||||
case 'A'..='Z', 'a'..='z', '0'..='9', '_':
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -159,64 +161,67 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
|
||||
token.pos = t.pos;
|
||||
|
||||
token.kind = Kind.Invalid;
|
||||
token.kind = .Invalid;
|
||||
|
||||
curr_rune := t.r;
|
||||
next_rune(t);
|
||||
|
||||
block: switch curr_rune {
|
||||
case utf8.RUNE_ERROR:
|
||||
err = Error.Illegal_Character;
|
||||
err = .Illegal_Character;
|
||||
case utf8.RUNE_EOF, '\x00':
|
||||
err = Error.EOF;
|
||||
token.kind = .EOF;
|
||||
err = .EOF;
|
||||
|
||||
case 'A'..'Z', 'a'..'z', '_':
|
||||
token.kind = Kind.Ident;
|
||||
case 'A'..='Z', 'a'..='z', '_':
|
||||
token.kind = .Ident;
|
||||
|
||||
skip_alphanum(t);
|
||||
|
||||
switch str := string(t.data[token.offset:t.offset]); str {
|
||||
case "null": token.kind = Kind.Null;
|
||||
case "false": token.kind = Kind.False;
|
||||
case "true": token.kind = Kind.True;
|
||||
case "null": token.kind = .Null;
|
||||
case "false": token.kind = .False;
|
||||
case "true": token.kind = .True;
|
||||
case:
|
||||
if t.spec == Specification.JSON5 do switch str {
|
||||
case "Infinity": token.kind = Kind.Infinity;
|
||||
case "NaN": token.kind = Kind.NaN;
|
||||
if t.spec == .JSON5 {
|
||||
switch str {
|
||||
case "Infinity": token.kind = .Infinity;
|
||||
case "NaN": token.kind = .NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case '+':
|
||||
err = Error.Illegal_Character;
|
||||
if t.spec != Specification.JSON5 {
|
||||
err = .Illegal_Character;
|
||||
if t.spec != .JSON5 {
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
|
||||
case '-':
|
||||
switch t.r {
|
||||
case '0'..'9':
|
||||
case '0'..='9':
|
||||
// Okay
|
||||
case:
|
||||
// Illegal use of +/-
|
||||
err = Error.Illegal_Character;
|
||||
err = .Illegal_Character;
|
||||
|
||||
if t.spec == Specification.JSON5 {
|
||||
if t.spec == .JSON5 {
|
||||
if t.r == 'I' || t.r == 'N' {
|
||||
skip_alphanum(t);
|
||||
}
|
||||
switch string(t.data[token.offset:t.offset]) {
|
||||
case "-Infinity": token.kind = Kind.Infinity;
|
||||
case "-NaN": token.kind = Kind.NaN;
|
||||
case "-Infinity": token.kind = .Infinity;
|
||||
case "-NaN": token.kind = .NaN;
|
||||
}
|
||||
}
|
||||
break block;
|
||||
}
|
||||
fallthrough;
|
||||
|
||||
case '0'..'9':
|
||||
token.kind = Kind.Integer;
|
||||
if t.spec == Specification.JSON5 { // Hexadecimal Numbers
|
||||
case '0'..='9':
|
||||
token.kind = t.parse_integers ? .Integer : .Float;
|
||||
if t.spec == .JSON5 { // Hexadecimal Numbers
|
||||
if curr_rune == '0' && (t.r == 'x' || t.r == 'X') {
|
||||
next_rune(t);
|
||||
skip_hex_digits(t);
|
||||
@@ -227,7 +232,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
skip_digits(t);
|
||||
|
||||
if t.r == '.' {
|
||||
token.kind = Kind.Float;
|
||||
token.kind = .Float;
|
||||
next_rune(t);
|
||||
skip_digits(t);
|
||||
}
|
||||
@@ -241,12 +246,12 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
|
||||
str := string(t.data[token.offset:t.offset]);
|
||||
if !is_valid_number(str, t.spec) {
|
||||
err = Error.Invalid_Number;
|
||||
err = .Invalid_Number;
|
||||
}
|
||||
|
||||
case '.':
|
||||
err = Error.Illegal_Character;
|
||||
if t.spec == Specification.JSON5 { // Allow leading decimal point
|
||||
err = .Illegal_Character;
|
||||
if t.spec == .JSON5 { // Allow leading decimal point
|
||||
skip_digits(t);
|
||||
if t.r == 'e' || t.r == 'E' {
|
||||
switch r := next_rune(t); r {
|
||||
@@ -257,24 +262,24 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
}
|
||||
str := string(t.data[token.offset:t.offset]);
|
||||
if !is_valid_number(str, t.spec) {
|
||||
err = Error.Invalid_Number;
|
||||
err = .Invalid_Number;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case '\'':
|
||||
err = Error.Illegal_Character;
|
||||
if t.spec != Specification.JSON5 {
|
||||
err = .Illegal_Character;
|
||||
if t.spec != .JSON5 {
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
case '"':
|
||||
token.kind = Kind.String;
|
||||
token.kind = .String;
|
||||
quote := curr_rune;
|
||||
for t.offset < len(t.data) {
|
||||
r := t.r;
|
||||
if r == '\n' || r < 0 {
|
||||
err = Error.String_Not_Terminated;
|
||||
err = .String_Not_Terminated;
|
||||
break;
|
||||
}
|
||||
next_rune(t);
|
||||
@@ -288,20 +293,20 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
|
||||
str := string(t.data[token.offset : t.offset]);
|
||||
if !is_valid_string_literal(str, t.spec) {
|
||||
err = Error.Invalid_String;
|
||||
err = .Invalid_String;
|
||||
}
|
||||
|
||||
|
||||
case ',': token.kind = Kind.Comma;
|
||||
case ':': token.kind = Kind.Colon;
|
||||
case '{': token.kind = Kind.Open_Brace;
|
||||
case '}': token.kind = Kind.Close_Brace;
|
||||
case '[': token.kind = Kind.Open_Bracket;
|
||||
case ']': token.kind = Kind.Close_Bracket;
|
||||
case ',': token.kind = .Comma;
|
||||
case ':': token.kind = .Colon;
|
||||
case '{': token.kind = .Open_Brace;
|
||||
case '}': token.kind = .Close_Brace;
|
||||
case '[': token.kind = .Open_Bracket;
|
||||
case ']': token.kind = .Close_Bracket;
|
||||
|
||||
case '/':
|
||||
err = Error.Illegal_Character;
|
||||
if t.spec == Specification.JSON5 {
|
||||
err = .Illegal_Character;
|
||||
if t.spec == .JSON5 {
|
||||
switch t.r {
|
||||
case '/':
|
||||
// Single-line comments
|
||||
@@ -319,11 +324,11 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
err = Error.EOF;
|
||||
err = .EOF;
|
||||
}
|
||||
}
|
||||
|
||||
case: err = Error.Illegal_Character;
|
||||
case: err = .Illegal_Character;
|
||||
}
|
||||
|
||||
token.text = string(t.data[token.offset : t.offset]);
|
||||
@@ -344,7 +349,7 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
if s == "" {
|
||||
return false;
|
||||
}
|
||||
} else if spec == Specification.JSON5 {
|
||||
} else if spec == .JSON5 {
|
||||
if s[0] == '+' { // Allow positive sign
|
||||
s = s[1:];
|
||||
if s == "" {
|
||||
@@ -356,11 +361,13 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
switch s[0] {
|
||||
case '0':
|
||||
s = s[1:];
|
||||
case '1'..'9':
|
||||
case '1'..='9':
|
||||
s = s[1:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:];
|
||||
}
|
||||
case '.':
|
||||
if spec == Specification.JSON5 { // Allow leading decimal point
|
||||
if spec == .JSON5 { // Allow leading decimal point
|
||||
s = s[1:];
|
||||
} else {
|
||||
return false;
|
||||
@@ -369,7 +376,7 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
if spec == Specification.JSON5 {
|
||||
if spec == .JSON5 {
|
||||
if len(s) == 1 && s[0] == '.' { // Allow trailing decimal point
|
||||
return true;
|
||||
}
|
||||
@@ -377,7 +384,9 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
|
||||
if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
|
||||
s = s[2:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:];
|
||||
}
|
||||
}
|
||||
|
||||
if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
|
||||
@@ -389,7 +398,9 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:];
|
||||
}
|
||||
}
|
||||
|
||||
// The string should be empty now to be valid
|
||||
@@ -406,7 +417,7 @@ is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
|
||||
return false;
|
||||
}
|
||||
if s[0] != '"' || s[len(s)-1] != '"' {
|
||||
if spec == Specification.JSON5 {
|
||||
if spec == .JSON5 {
|
||||
if s[0] != '\'' || s[len(s)-1] != '\'' {
|
||||
return false;
|
||||
}
|
||||
@@ -442,7 +453,7 @@ is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
|
||||
for j := 0; j < 4; j += 1 {
|
||||
c2 := hex[j];
|
||||
switch c2 {
|
||||
case '0'..'9', 'a'..'z', 'A'..'Z':
|
||||
case '0'..='9', 'a'..='z', 'A'..='Z':
|
||||
// Okay
|
||||
case:
|
||||
return false;
|
||||
|
||||
@@ -2,7 +2,8 @@ package json
|
||||
|
||||
Specification :: enum {
|
||||
JSON,
|
||||
JSON5,
|
||||
JSON5, // https://json5.org/
|
||||
// MJSON, // http://bitsquid.blogspot.com/2009/09/json-configuration-data.html
|
||||
}
|
||||
|
||||
Null :: distinct rawptr;
|
||||
@@ -23,7 +24,7 @@ Value :: struct {
|
||||
String,
|
||||
Array,
|
||||
Object,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Pos :: struct {
|
||||
@@ -64,7 +65,9 @@ destroy_value :: proc(value: Value) {
|
||||
}
|
||||
delete(v);
|
||||
case Array:
|
||||
for elem in v do destroy_value(elem);
|
||||
for elem in v {
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(v);
|
||||
case String:
|
||||
delete(v);
|
||||
|
||||
@@ -3,8 +3,8 @@ package json
|
||||
import "core:mem"
|
||||
|
||||
// NOTE(bill): is_valid will not check for duplicate keys
|
||||
is_valid :: proc(data: []byte, spec := Specification.JSON) -> bool {
|
||||
p := make_parser(data, spec, mem.nil_allocator());
|
||||
is_valid :: proc(data: []byte, spec := Specification.JSON, parse_integers := false) -> bool {
|
||||
p := make_parser(data, spec, parse_integers, mem.nil_allocator());
|
||||
if p.spec == Specification.JSON5 {
|
||||
return validate_value(&p);
|
||||
}
|
||||
@@ -14,27 +14,27 @@ is_valid :: proc(data: []byte, spec := Specification.JSON) -> bool {
|
||||
validate_object_key :: proc(p: ^Parser) -> bool {
|
||||
tok := p.curr_token;
|
||||
if p.spec == Specification.JSON5 {
|
||||
if tok.kind == Kind.String {
|
||||
expect_token(p, Kind.String);
|
||||
if tok.kind == .String {
|
||||
expect_token(p, .String);
|
||||
return true;
|
||||
} else if tok.kind == Kind.Ident {
|
||||
expect_token(p, Kind.Ident);
|
||||
} else if tok.kind == .Ident {
|
||||
expect_token(p, .Ident);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
err := expect_token(p, Kind.String);
|
||||
err := expect_token(p, .String);
|
||||
return err == Error.None;
|
||||
}
|
||||
validate_object :: proc(p: ^Parser) -> bool {
|
||||
if err := expect_token(p, Kind.Open_Brace); err != Error.None {
|
||||
if err := expect_token(p, .Open_Brace); err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
for p.curr_token.kind != Kind.Close_Brace {
|
||||
for p.curr_token.kind != .Close_Brace {
|
||||
if !validate_object_key(p) {
|
||||
return false;
|
||||
}
|
||||
if colon_err := expect_token(p, Kind.Colon); colon_err != Error.None {
|
||||
if colon_err := expect_token(p, .Colon); colon_err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -44,12 +44,12 @@ validate_object :: proc(p: ^Parser) -> bool {
|
||||
|
||||
if p.spec == Specification.JSON5 {
|
||||
// Allow trailing commas
|
||||
if allow_token(p, Kind.Comma) {
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Disallow trailing commas
|
||||
if allow_token(p, Kind.Comma) {
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
@@ -57,31 +57,31 @@ validate_object :: proc(p: ^Parser) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
if err := expect_token(p, Kind.Close_Brace); err != Error.None {
|
||||
if err := expect_token(p, .Close_Brace); err != Error.None {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
validate_array :: proc(p: ^Parser) -> bool {
|
||||
if err := expect_token(p, Kind.Open_Bracket); err != Error.None {
|
||||
if err := expect_token(p, .Open_Bracket); err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
for p.curr_token.kind != Kind.Close_Bracket {
|
||||
for p.curr_token.kind != .Close_Bracket {
|
||||
if !validate_value(p) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disallow trailing commas for the time being
|
||||
if allow_token(p, Kind.Comma) {
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if err := expect_token(p, Kind.Close_Bracket); err != Error.None {
|
||||
if err := expect_token(p, .Close_Bracket); err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+951
-598
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,78 @@
|
||||
package hash
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
crc64 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check {
|
||||
result := ~u64(seed);
|
||||
#no_bounds_check for b in data {
|
||||
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
|
||||
@private _crc64_table := [256]u64{
|
||||
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
||||
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
||||
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
|
||||
0xdb55aacf12c73561, 0x99a54b24bb2d03f2, 0x5eb4691841135847, 0x1c4488f3e8f96ed4,
|
||||
0x663d78ff90e185ef, 0x24cd9914390bb37c, 0xe3dcbb28c335e8c9, 0xa12c5ac36adfde5a,
|
||||
0x2f0e1eba9ea36930, 0x6dfeff5137495fa3, 0xaaefdd6dcd770416, 0xe81f3c86649d3285,
|
||||
0xf45bb4758c645c51, 0xb6ab559e258e6ac2, 0x71ba77a2dfb03177, 0x334a9649765a07e4,
|
||||
0xbd68d2308226b08e, 0xff9833db2bcc861d, 0x388911e7d1f2dda8, 0x7a79f00c7818eb3b,
|
||||
0xcc7af1ff21c30bde, 0x8e8a101488293d4d, 0x499b3228721766f8, 0x0b6bd3c3dbfd506b,
|
||||
0x854997ba2f81e701, 0xc7b97651866bd192, 0x00a8546d7c558a27, 0x4258b586d5bfbcb4,
|
||||
0x5e1c3d753d46d260, 0x1cecdc9e94ace4f3, 0xdbfdfea26e92bf46, 0x990d1f49c77889d5,
|
||||
0x172f5b3033043ebf, 0x55dfbadb9aee082c, 0x92ce98e760d05399, 0xd03e790cc93a650a,
|
||||
0xaa478900b1228e31, 0xe8b768eb18c8b8a2, 0x2fa64ad7e2f6e317, 0x6d56ab3c4b1cd584,
|
||||
0xe374ef45bf6062ee, 0xa1840eae168a547d, 0x66952c92ecb40fc8, 0x2465cd79455e395b,
|
||||
0x3821458aada7578f, 0x7ad1a461044d611c, 0xbdc0865dfe733aa9, 0xff3067b657990c3a,
|
||||
0x711223cfa3e5bb50, 0x33e2c2240a0f8dc3, 0xf4f3e018f031d676, 0xb60301f359dbe0e5,
|
||||
0xda050215ea6c212f, 0x98f5e3fe438617bc, 0x5fe4c1c2b9b84c09, 0x1d14202910527a9a,
|
||||
0x93366450e42ecdf0, 0xd1c685bb4dc4fb63, 0x16d7a787b7faa0d6, 0x5427466c1e109645,
|
||||
0x4863ce9ff6e9f891, 0x0a932f745f03ce02, 0xcd820d48a53d95b7, 0x8f72eca30cd7a324,
|
||||
0x0150a8daf8ab144e, 0x43a04931514122dd, 0x84b16b0dab7f7968, 0xc6418ae602954ffb,
|
||||
0xbc387aea7a8da4c0, 0xfec89b01d3679253, 0x39d9b93d2959c9e6, 0x7b2958d680b3ff75,
|
||||
0xf50b1caf74cf481f, 0xb7fbfd44dd257e8c, 0x70eadf78271b2539, 0x321a3e938ef113aa,
|
||||
0x2e5eb66066087d7e, 0x6cae578bcfe24bed, 0xabbf75b735dc1058, 0xe94f945c9c3626cb,
|
||||
0x676dd025684a91a1, 0x259d31cec1a0a732, 0xe28c13f23b9efc87, 0xa07cf2199274ca14,
|
||||
0x167ff3eacbaf2af1, 0x548f120162451c62, 0x939e303d987b47d7, 0xd16ed1d631917144,
|
||||
0x5f4c95afc5edc62e, 0x1dbc74446c07f0bd, 0xdaad56789639ab08, 0x985db7933fd39d9b,
|
||||
0x84193f60d72af34f, 0xc6e9de8b7ec0c5dc, 0x01f8fcb784fe9e69, 0x43081d5c2d14a8fa,
|
||||
0xcd2a5925d9681f90, 0x8fdab8ce70822903, 0x48cb9af28abc72b6, 0x0a3b7b1923564425,
|
||||
0x70428b155b4eaf1e, 0x32b26afef2a4998d, 0xf5a348c2089ac238, 0xb753a929a170f4ab,
|
||||
0x3971ed50550c43c1, 0x7b810cbbfce67552, 0xbc902e8706d82ee7, 0xfe60cf6caf321874,
|
||||
0xe224479f47cb76a0, 0xa0d4a674ee214033, 0x67c58448141f1b86, 0x253565a3bdf52d15,
|
||||
0xab1721da49899a7f, 0xe9e7c031e063acec, 0x2ef6e20d1a5df759, 0x6c0603e6b3b7c1ca,
|
||||
0xf6fae5c07d3274cd, 0xb40a042bd4d8425e, 0x731b26172ee619eb, 0x31ebc7fc870c2f78,
|
||||
0xbfc9838573709812, 0xfd39626eda9aae81, 0x3a28405220a4f534, 0x78d8a1b9894ec3a7,
|
||||
0x649c294a61b7ad73, 0x266cc8a1c85d9be0, 0xe17dea9d3263c055, 0xa38d0b769b89f6c6,
|
||||
0x2daf4f0f6ff541ac, 0x6f5faee4c61f773f, 0xa84e8cd83c212c8a, 0xeabe6d3395cb1a19,
|
||||
0x90c79d3fedd3f122, 0xd2377cd44439c7b1, 0x15265ee8be079c04, 0x57d6bf0317edaa97,
|
||||
0xd9f4fb7ae3911dfd, 0x9b041a914a7b2b6e, 0x5c1538adb04570db, 0x1ee5d94619af4648,
|
||||
0x02a151b5f156289c, 0x4051b05e58bc1e0f, 0x87409262a28245ba, 0xc5b073890b687329,
|
||||
0x4b9237f0ff14c443, 0x0962d61b56fef2d0, 0xce73f427acc0a965, 0x8c8315cc052a9ff6,
|
||||
0x3a80143f5cf17f13, 0x7870f5d4f51b4980, 0xbf61d7e80f251235, 0xfd913603a6cf24a6,
|
||||
0x73b3727a52b393cc, 0x31439391fb59a55f, 0xf652b1ad0167feea, 0xb4a25046a88dc879,
|
||||
0xa8e6d8b54074a6ad, 0xea16395ee99e903e, 0x2d071b6213a0cb8b, 0x6ff7fa89ba4afd18,
|
||||
0xe1d5bef04e364a72, 0xa3255f1be7dc7ce1, 0x64347d271de22754, 0x26c49cccb40811c7,
|
||||
0x5cbd6cc0cc10fafc, 0x1e4d8d2b65facc6f, 0xd95caf179fc497da, 0x9bac4efc362ea149,
|
||||
0x158e0a85c2521623, 0x577eeb6e6bb820b0, 0x906fc95291867b05, 0xd29f28b9386c4d96,
|
||||
0xcedba04ad0952342, 0x8c2b41a1797f15d1, 0x4b3a639d83414e64, 0x09ca82762aab78f7,
|
||||
0x87e8c60fded7cf9d, 0xc51827e4773df90e, 0x020905d88d03a2bb, 0x40f9e43324e99428,
|
||||
0x2cffe7d5975e55e2, 0x6e0f063e3eb46371, 0xa91e2402c48a38c4, 0xebeec5e96d600e57,
|
||||
0x65cc8190991cb93d, 0x273c607b30f68fae, 0xe02d4247cac8d41b, 0xa2dda3ac6322e288,
|
||||
0xbe992b5f8bdb8c5c, 0xfc69cab42231bacf, 0x3b78e888d80fe17a, 0x7988096371e5d7e9,
|
||||
0xf7aa4d1a85996083, 0xb55aacf12c735610, 0x724b8ecdd64d0da5, 0x30bb6f267fa73b36,
|
||||
0x4ac29f2a07bfd00d, 0x08327ec1ae55e69e, 0xcf235cfd546bbd2b, 0x8dd3bd16fd818bb8,
|
||||
0x03f1f96f09fd3cd2, 0x41011884a0170a41, 0x86103ab85a2951f4, 0xc4e0db53f3c36767,
|
||||
0xd8a453a01b3a09b3, 0x9a54b24bb2d03f20, 0x5d45907748ee6495, 0x1fb5719ce1045206,
|
||||
0x919735e51578e56c, 0xd367d40ebc92d3ff, 0x1476f63246ac884a, 0x568617d9ef46bed9,
|
||||
0xe085162ab69d5e3c, 0xa275f7c11f7768af, 0x6564d5fde549331a, 0x279434164ca30589,
|
||||
0xa9b6706fb8dfb2e3, 0xeb46918411358470, 0x2c57b3b8eb0bdfc5, 0x6ea7525342e1e956,
|
||||
0x72e3daa0aa188782, 0x30133b4b03f2b111, 0xf7021977f9cceaa4, 0xb5f2f89c5026dc37,
|
||||
0x3bd0bce5a45a6b5d, 0x79205d0e0db05dce, 0xbe317f32f78e067b, 0xfcc19ed95e6430e8,
|
||||
0x86b86ed5267cdbd3, 0xc4488f3e8f96ed40, 0x0359ad0275a8b6f5, 0x41a94ce9dc428066,
|
||||
0xcf8b0890283e370c, 0x8d7be97b81d4019f, 0x4a6acb477bea5a2a, 0x089a2aacd2006cb9,
|
||||
0x14dea25f3af9026d, 0x562e43b4931334fe, 0x913f6188692d6f4b, 0xd3cf8063c0c759d8,
|
||||
0x5dedc41a34bbeeb2, 0x1f1d25f19d51d821, 0xd80c07cd676f8394, 0x9afce626ce85b507,
|
||||
};
|
||||
@@ -0,0 +1,401 @@
|
||||
package hash
|
||||
|
||||
import "intrinsics"
|
||||
|
||||
@(optimization_mode="speed")
|
||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
crc := ~seed;
|
||||
buffer := raw_data(data);
|
||||
length := len(data);
|
||||
|
||||
for length != 0 && uintptr(buffer) & 7 != 0 {
|
||||
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8);
|
||||
buffer = intrinsics.ptr_offset(buffer, 1);
|
||||
length -= 1;
|
||||
}
|
||||
|
||||
for length >= 8 {
|
||||
buf := (^[8]byte)(buffer);
|
||||
word := u32((^u32le)(buffer)^);
|
||||
crc ~= word;
|
||||
|
||||
crc = crc32_table[7][crc & 0xff] ~
|
||||
crc32_table[6][(crc >> 8) & 0xff] ~
|
||||
crc32_table[5][(crc >> 16) & 0xff] ~
|
||||
crc32_table[4][(crc >> 24) & 0xff] ~
|
||||
crc32_table[3][buf[4]] ~
|
||||
crc32_table[2][buf[5]] ~
|
||||
crc32_table[1][buf[6]] ~
|
||||
crc32_table[0][buf[7]];
|
||||
|
||||
buffer = intrinsics.ptr_offset(buffer, 8);
|
||||
length -= 8;
|
||||
}
|
||||
|
||||
|
||||
for length != 0 {
|
||||
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8);
|
||||
buffer = intrinsics.ptr_offset(buffer, 1);
|
||||
length -= 1;
|
||||
}
|
||||
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
@(private)
|
||||
crc32_table := [8][256]u32{
|
||||
{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
},
|
||||
{
|
||||
0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, 0x646cc504, 0x7d77f445, 0x565aa786, 0x4f4196c7,
|
||||
0xc8d98a08, 0xd1c2bb49, 0xfaefe88a, 0xe3f4d9cb, 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, 0x87981ccf,
|
||||
0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, 0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496,
|
||||
0x821b9859, 0x9b00a918, 0xb02dfadb, 0xa936cb9a, 0xe6775d5d, 0xff6c6c1c, 0xd4413fdf, 0xcd5a0e9e,
|
||||
0x958424a2, 0x8c9f15e3, 0xa7b24620, 0xbea97761, 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265,
|
||||
0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, 0x39316bae, 0x202a5aef, 0x0b07092c, 0x121c386d,
|
||||
0xdf4636f3, 0xc65d07b2, 0xed705471, 0xf46b6530, 0xbb2af3f7, 0xa231c2b6, 0x891c9175, 0x9007a034,
|
||||
0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c,
|
||||
0xf0794f05, 0xe9627e44, 0xc24f2d87, 0xdb541cc6, 0x94158a01, 0x8d0ebb40, 0xa623e883, 0xbf38d9c2,
|
||||
0x38a0c50d, 0x21bbf44c, 0x0a96a78f, 0x138d96ce, 0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca,
|
||||
0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, 0xded79850, 0xc7cca911, 0xece1fad2, 0xf5facb93,
|
||||
0x7262d75c, 0x6b79e61d, 0x4054b5de, 0x594f849f, 0x160e1258, 0x0f152319, 0x243870da, 0x3d23419b,
|
||||
0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864, 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60,
|
||||
0xad24e1af, 0xb43fd0ee, 0x9f12832d, 0x8609b26c, 0xc94824ab, 0xd05315ea, 0xfb7e4629, 0xe2657768,
|
||||
0x2f3f79f6, 0x362448b7, 0x1d091b74, 0x04122a35, 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31,
|
||||
0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, 0x838a36fa, 0x9a9107bb, 0xb1bc5478, 0xa8a76539,
|
||||
0x3b83984b, 0x2298a90a, 0x09b5fac9, 0x10aecb88, 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, 0x74c20e8c,
|
||||
0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, 0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484,
|
||||
0x71418a1a, 0x685abb5b, 0x4377e898, 0x5a6cd9d9, 0x152d4f1e, 0x0c367e5f, 0x271b2d9c, 0x3e001cdd,
|
||||
0xb9980012, 0xa0833153, 0x8bae6290, 0x92b553d1, 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5,
|
||||
0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, 0xca6b79ed, 0xd37048ac, 0xf85d1b6f, 0xe1462a2e,
|
||||
0x66de36e1, 0x7fc507a0, 0x54e85463, 0x4df36522, 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, 0x299fa026,
|
||||
0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f,
|
||||
0x2c1c24b0, 0x350715f1, 0x1e2a4632, 0x07317773, 0x4870e1b4, 0x516bd0f5, 0x7a468336, 0x635db277,
|
||||
0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, 0xe0d7848d, 0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189,
|
||||
0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, 0x674f9842, 0x7e54a903, 0x5579fac0, 0x4c62cb81,
|
||||
0x8138c51f, 0x9823f45e, 0xb30ea79d, 0xaa1596dc, 0xe554001b, 0xfc4f315a, 0xd7626299, 0xce7953d8,
|
||||
0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0,
|
||||
0x5e7ef3ec, 0x4765c2ad, 0x6c48916e, 0x7553a02f, 0x3a1236e8, 0x230907a9, 0x0824546a, 0x113f652b,
|
||||
0x96a779e4, 0x8fbc48a5, 0xa4911b66, 0xbd8a2a27, 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23,
|
||||
0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e, 0x70d024b9, 0x69cb15f8, 0x42e6463b, 0x5bfd777a,
|
||||
0xdc656bb5, 0xc57e5af4, 0xee530937, 0xf7483876, 0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, 0x9324fd72,
|
||||
},
|
||||
{
|
||||
0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, 0x0709a8dc, 0x06cbc2eb, 0x048d7cb2, 0x054f1685,
|
||||
0x0e1351b8, 0x0fd13b8f, 0x0d9785d6, 0x0c55efe1, 0x091af964, 0x08d89353, 0x0a9e2d0a, 0x0b5c473d,
|
||||
0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, 0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5,
|
||||
0x1235f2c8, 0x13f798ff, 0x11b126a6, 0x10734c91, 0x153c5a14, 0x14fe3023, 0x16b88e7a, 0x177ae44d,
|
||||
0x384d46e0, 0x398f2cd7, 0x3bc9928e, 0x3a0bf8b9, 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065,
|
||||
0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, 0x3157bf84, 0x3095d5b3, 0x32d36bea, 0x331101dd,
|
||||
0x246be590, 0x25a98fa7, 0x27ef31fe, 0x262d5bc9, 0x23624d4c, 0x22a0277b, 0x20e69922, 0x2124f315,
|
||||
0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad,
|
||||
0x709a8dc0, 0x7158e7f7, 0x731e59ae, 0x72dc3399, 0x7793251c, 0x76514f2b, 0x7417f172, 0x75d59b45,
|
||||
0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, 0x7ccf6221, 0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd,
|
||||
0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, 0x6bb5866c, 0x6a77ec5b, 0x68315202, 0x69f33835,
|
||||
0x62af7f08, 0x636d153f, 0x612bab66, 0x60e9c151, 0x65a6d7d4, 0x6464bde3, 0x662203ba, 0x67e0698d,
|
||||
0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579, 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5,
|
||||
0x46c49a98, 0x4706f0af, 0x45404ef6, 0x448224c1, 0x41cd3244, 0x400f5873, 0x4249e62a, 0x438b8c1d,
|
||||
0x54f16850, 0x55330267, 0x5775bc3e, 0x56b7d609, 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5,
|
||||
0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1, 0x5deb9134, 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d,
|
||||
0xe1351b80, 0xe0f771b7, 0xe2b1cfee, 0xe373a5d9, 0xe63cb35c, 0xe7fed96b, 0xe5b86732, 0xe47a0d05,
|
||||
0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd,
|
||||
0xfd13b8f0, 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, 0xfa1a102c, 0xfbd87a1b, 0xf99ec442, 0xf85cae75,
|
||||
0xf300e948, 0xf2c2837f, 0xf0843d26, 0xf1465711, 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd,
|
||||
0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, 0xde71f5bc, 0xdfb39f8b, 0xddf521d2, 0xdc374be5,
|
||||
0xd76b0cd8, 0xd6a966ef, 0xd4efd8b6, 0xd52db281, 0xd062a404, 0xd1a0ce33, 0xd3e6706a, 0xd2241a5d,
|
||||
0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895,
|
||||
0xcb4dafa8, 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1, 0xcc440774, 0xcd866d43, 0xcfc0d31a, 0xce02b92d,
|
||||
0x91af9640, 0x906dfc77, 0x922b422e, 0x93e92819, 0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5,
|
||||
0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, 0x98b56f24, 0x99770513, 0x9b31bb4a, 0x9af3d17d,
|
||||
0x8d893530, 0x8c4b5f07, 0x8e0de15e, 0x8fcf8b69, 0x8a809dec, 0x8b42f7db, 0x89044982, 0x88c623b5,
|
||||
0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d,
|
||||
0xa9e2d0a0, 0xa820ba97, 0xaa6604ce, 0xaba46ef9, 0xaeeb787c, 0xaf29124b, 0xad6fac12, 0xacadc625,
|
||||
0xa7f18118, 0xa633eb2f, 0xa4755576, 0xa5b73f41, 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d,
|
||||
0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89, 0xb2cddb0c, 0xb30fb13b, 0xb1490f62, 0xb08b6555,
|
||||
0xbbd72268, 0xba15485f, 0xb853f606, 0xb9919c31, 0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, 0xbe9834ed,
|
||||
},
|
||||
{
|
||||
0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, 0x8f629757, 0x37def032, 0x256b5fdc, 0x9dd738b9,
|
||||
0xc5b428ef, 0x7d084f8a, 0x6fbde064, 0xd7018701, 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, 0x58631056,
|
||||
0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, 0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26,
|
||||
0x95ad7f70, 0x2d111815, 0x3fa4b7fb, 0x8718d09e, 0x1acfe827, 0xa2738f42, 0xb0c620ac, 0x087a47c9,
|
||||
0xa032af3e, 0x188ec85b, 0x0a3b67b5, 0xb28700d0, 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787,
|
||||
0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, 0xeae41086, 0x525877e3, 0x40edd80d, 0xf851bf68,
|
||||
0xf02bf8a1, 0x48979fc4, 0x5a22302a, 0xe29e574f, 0x7f496ff6, 0xc7f50893, 0xd540a77d, 0x6dfcc018,
|
||||
0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7,
|
||||
0x9b14583d, 0x23a83f58, 0x311d90b6, 0x89a1f7d3, 0x1476cf6a, 0xaccaa80f, 0xbe7f07e1, 0x06c36084,
|
||||
0x5ea070d2, 0xe61c17b7, 0xf4a9b859, 0x4c15df3c, 0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b,
|
||||
0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, 0x446f98f5, 0xfcd3ff90, 0xee66507e, 0x56da371b,
|
||||
0x0eb9274d, 0xb6054028, 0xa4b0efc6, 0x1c0c88a3, 0x81dbb01a, 0x3967d77f, 0x2bd27891, 0x936e1ff4,
|
||||
0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed, 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba,
|
||||
0xfe92dfec, 0x462eb889, 0x549b1767, 0xec277002, 0x71f048bb, 0xc94c2fde, 0xdbf98030, 0x6345e755,
|
||||
0x6b3fa09c, 0xd383c7f9, 0xc1366817, 0x798a0f72, 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825,
|
||||
0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d, 0x21e91f24, 0x99557841, 0x8be0d7af, 0x335cb0ca,
|
||||
0xed59b63b, 0x55e5d15e, 0x47507eb0, 0xffec19d5, 0x623b216c, 0xda874609, 0xc832e9e7, 0x708e8e82,
|
||||
0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, 0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d,
|
||||
0xbd40e1a4, 0x05fc86c1, 0x1749292f, 0xaff54e4a, 0x322276f3, 0x8a9e1196, 0x982bbe78, 0x2097d91d,
|
||||
0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, 0x6a4166a5, 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2,
|
||||
0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, 0xc2098e52, 0x7ab5e937, 0x680046d9, 0xd0bc21bc,
|
||||
0x88df31ea, 0x3063568f, 0x22d6f961, 0x9a6a9e04, 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, 0x15080953,
|
||||
0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623,
|
||||
0xd8c66675, 0x607a0110, 0x72cfaefe, 0xca73c99b, 0x57a4f122, 0xef189647, 0xfdad39a9, 0x45115ecc,
|
||||
0x764dee06, 0xcef18963, 0xdc44268d, 0x64f841e8, 0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf,
|
||||
0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, 0x3c9b51be, 0x842736db, 0x96929935, 0x2e2efe50,
|
||||
0x2654b999, 0x9ee8defc, 0x8c5d7112, 0x34e11677, 0xa9362ece, 0x118a49ab, 0x033fe645, 0xbb838120,
|
||||
0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf,
|
||||
0xd67f4138, 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, 0x591dd66f, 0xe1a1b10a, 0xf3141ee4, 0x4ba87981,
|
||||
0x13cb69d7, 0xab770eb2, 0xb9c2a15c, 0x017ec639, 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e,
|
||||
0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949, 0x090481f0, 0xb1b8e695, 0xa30d497b, 0x1bb12e1e,
|
||||
0x43d23e48, 0xfb6e592d, 0xe9dbf6c3, 0x516791a6, 0xccb0a91f, 0x740cce7a, 0x66b96194, 0xde0506f1,
|
||||
},
|
||||
{
|
||||
0x00000000, 0x3d6029b0, 0x7ac05360, 0x47a07ad0, 0xf580a6c0, 0xc8e08f70, 0x8f40f5a0, 0xb220dc10,
|
||||
0x30704bc1, 0x0d106271, 0x4ab018a1, 0x77d03111, 0xc5f0ed01, 0xf890c4b1, 0xbf30be61, 0x825097d1,
|
||||
0x60e09782, 0x5d80be32, 0x1a20c4e2, 0x2740ed52, 0x95603142, 0xa80018f2, 0xefa06222, 0xd2c04b92,
|
||||
0x5090dc43, 0x6df0f5f3, 0x2a508f23, 0x1730a693, 0xa5107a83, 0x98705333, 0xdfd029e3, 0xe2b00053,
|
||||
0xc1c12f04, 0xfca106b4, 0xbb017c64, 0x866155d4, 0x344189c4, 0x0921a074, 0x4e81daa4, 0x73e1f314,
|
||||
0xf1b164c5, 0xccd14d75, 0x8b7137a5, 0xb6111e15, 0x0431c205, 0x3951ebb5, 0x7ef19165, 0x4391b8d5,
|
||||
0xa121b886, 0x9c419136, 0xdbe1ebe6, 0xe681c256, 0x54a11e46, 0x69c137f6, 0x2e614d26, 0x13016496,
|
||||
0x9151f347, 0xac31daf7, 0xeb91a027, 0xd6f18997, 0x64d15587, 0x59b17c37, 0x1e1106e7, 0x23712f57,
|
||||
0x58f35849, 0x659371f9, 0x22330b29, 0x1f532299, 0xad73fe89, 0x9013d739, 0xd7b3ade9, 0xead38459,
|
||||
0x68831388, 0x55e33a38, 0x124340e8, 0x2f236958, 0x9d03b548, 0xa0639cf8, 0xe7c3e628, 0xdaa3cf98,
|
||||
0x3813cfcb, 0x0573e67b, 0x42d39cab, 0x7fb3b51b, 0xcd93690b, 0xf0f340bb, 0xb7533a6b, 0x8a3313db,
|
||||
0x0863840a, 0x3503adba, 0x72a3d76a, 0x4fc3feda, 0xfde322ca, 0xc0830b7a, 0x872371aa, 0xba43581a,
|
||||
0x9932774d, 0xa4525efd, 0xe3f2242d, 0xde920d9d, 0x6cb2d18d, 0x51d2f83d, 0x167282ed, 0x2b12ab5d,
|
||||
0xa9423c8c, 0x9422153c, 0xd3826fec, 0xeee2465c, 0x5cc29a4c, 0x61a2b3fc, 0x2602c92c, 0x1b62e09c,
|
||||
0xf9d2e0cf, 0xc4b2c97f, 0x8312b3af, 0xbe729a1f, 0x0c52460f, 0x31326fbf, 0x7692156f, 0x4bf23cdf,
|
||||
0xc9a2ab0e, 0xf4c282be, 0xb362f86e, 0x8e02d1de, 0x3c220dce, 0x0142247e, 0x46e25eae, 0x7b82771e,
|
||||
0xb1e6b092, 0x8c869922, 0xcb26e3f2, 0xf646ca42, 0x44661652, 0x79063fe2, 0x3ea64532, 0x03c66c82,
|
||||
0x8196fb53, 0xbcf6d2e3, 0xfb56a833, 0xc6368183, 0x74165d93, 0x49767423, 0x0ed60ef3, 0x33b62743,
|
||||
0xd1062710, 0xec660ea0, 0xabc67470, 0x96a65dc0, 0x248681d0, 0x19e6a860, 0x5e46d2b0, 0x6326fb00,
|
||||
0xe1766cd1, 0xdc164561, 0x9bb63fb1, 0xa6d61601, 0x14f6ca11, 0x2996e3a1, 0x6e369971, 0x5356b0c1,
|
||||
0x70279f96, 0x4d47b626, 0x0ae7ccf6, 0x3787e546, 0x85a73956, 0xb8c710e6, 0xff676a36, 0xc2074386,
|
||||
0x4057d457, 0x7d37fde7, 0x3a978737, 0x07f7ae87, 0xb5d77297, 0x88b75b27, 0xcf1721f7, 0xf2770847,
|
||||
0x10c70814, 0x2da721a4, 0x6a075b74, 0x576772c4, 0xe547aed4, 0xd8278764, 0x9f87fdb4, 0xa2e7d404,
|
||||
0x20b743d5, 0x1dd76a65, 0x5a7710b5, 0x67173905, 0xd537e515, 0xe857cca5, 0xaff7b675, 0x92979fc5,
|
||||
0xe915e8db, 0xd475c16b, 0x93d5bbbb, 0xaeb5920b, 0x1c954e1b, 0x21f567ab, 0x66551d7b, 0x5b3534cb,
|
||||
0xd965a31a, 0xe4058aaa, 0xa3a5f07a, 0x9ec5d9ca, 0x2ce505da, 0x11852c6a, 0x562556ba, 0x6b457f0a,
|
||||
0x89f57f59, 0xb49556e9, 0xf3352c39, 0xce550589, 0x7c75d999, 0x4115f029, 0x06b58af9, 0x3bd5a349,
|
||||
0xb9853498, 0x84e51d28, 0xc34567f8, 0xfe254e48, 0x4c059258, 0x7165bbe8, 0x36c5c138, 0x0ba5e888,
|
||||
0x28d4c7df, 0x15b4ee6f, 0x521494bf, 0x6f74bd0f, 0xdd54611f, 0xe03448af, 0xa794327f, 0x9af41bcf,
|
||||
0x18a48c1e, 0x25c4a5ae, 0x6264df7e, 0x5f04f6ce, 0xed242ade, 0xd044036e, 0x97e479be, 0xaa84500e,
|
||||
0x4834505d, 0x755479ed, 0x32f4033d, 0x0f942a8d, 0xbdb4f69d, 0x80d4df2d, 0xc774a5fd, 0xfa148c4d,
|
||||
0x78441b9c, 0x4524322c, 0x028448fc, 0x3fe4614c, 0x8dc4bd5c, 0xb0a494ec, 0xf704ee3c, 0xca64c78c,
|
||||
},
|
||||
{
|
||||
0x00000000, 0xcb5cd3a5, 0x4dc8a10b, 0x869472ae, 0x9b914216, 0x50cd91b3, 0xd659e31d, 0x1d0530b8,
|
||||
0xec53826d, 0x270f51c8, 0xa19b2366, 0x6ac7f0c3, 0x77c2c07b, 0xbc9e13de, 0x3a0a6170, 0xf156b2d5,
|
||||
0x03d6029b, 0xc88ad13e, 0x4e1ea390, 0x85427035, 0x9847408d, 0x531b9328, 0xd58fe186, 0x1ed33223,
|
||||
0xef8580f6, 0x24d95353, 0xa24d21fd, 0x6911f258, 0x7414c2e0, 0xbf481145, 0x39dc63eb, 0xf280b04e,
|
||||
0x07ac0536, 0xccf0d693, 0x4a64a43d, 0x81387798, 0x9c3d4720, 0x57619485, 0xd1f5e62b, 0x1aa9358e,
|
||||
0xebff875b, 0x20a354fe, 0xa6372650, 0x6d6bf5f5, 0x706ec54d, 0xbb3216e8, 0x3da66446, 0xf6fab7e3,
|
||||
0x047a07ad, 0xcf26d408, 0x49b2a6a6, 0x82ee7503, 0x9feb45bb, 0x54b7961e, 0xd223e4b0, 0x197f3715,
|
||||
0xe82985c0, 0x23755665, 0xa5e124cb, 0x6ebdf76e, 0x73b8c7d6, 0xb8e41473, 0x3e7066dd, 0xf52cb578,
|
||||
0x0f580a6c, 0xc404d9c9, 0x4290ab67, 0x89cc78c2, 0x94c9487a, 0x5f959bdf, 0xd901e971, 0x125d3ad4,
|
||||
0xe30b8801, 0x28575ba4, 0xaec3290a, 0x659ffaaf, 0x789aca17, 0xb3c619b2, 0x35526b1c, 0xfe0eb8b9,
|
||||
0x0c8e08f7, 0xc7d2db52, 0x4146a9fc, 0x8a1a7a59, 0x971f4ae1, 0x5c439944, 0xdad7ebea, 0x118b384f,
|
||||
0xe0dd8a9a, 0x2b81593f, 0xad152b91, 0x6649f834, 0x7b4cc88c, 0xb0101b29, 0x36846987, 0xfdd8ba22,
|
||||
0x08f40f5a, 0xc3a8dcff, 0x453cae51, 0x8e607df4, 0x93654d4c, 0x58399ee9, 0xdeadec47, 0x15f13fe2,
|
||||
0xe4a78d37, 0x2ffb5e92, 0xa96f2c3c, 0x6233ff99, 0x7f36cf21, 0xb46a1c84, 0x32fe6e2a, 0xf9a2bd8f,
|
||||
0x0b220dc1, 0xc07ede64, 0x46eaacca, 0x8db67f6f, 0x90b34fd7, 0x5bef9c72, 0xdd7beedc, 0x16273d79,
|
||||
0xe7718fac, 0x2c2d5c09, 0xaab92ea7, 0x61e5fd02, 0x7ce0cdba, 0xb7bc1e1f, 0x31286cb1, 0xfa74bf14,
|
||||
0x1eb014d8, 0xd5ecc77d, 0x5378b5d3, 0x98246676, 0x852156ce, 0x4e7d856b, 0xc8e9f7c5, 0x03b52460,
|
||||
0xf2e396b5, 0x39bf4510, 0xbf2b37be, 0x7477e41b, 0x6972d4a3, 0xa22e0706, 0x24ba75a8, 0xefe6a60d,
|
||||
0x1d661643, 0xd63ac5e6, 0x50aeb748, 0x9bf264ed, 0x86f75455, 0x4dab87f0, 0xcb3ff55e, 0x006326fb,
|
||||
0xf135942e, 0x3a69478b, 0xbcfd3525, 0x77a1e680, 0x6aa4d638, 0xa1f8059d, 0x276c7733, 0xec30a496,
|
||||
0x191c11ee, 0xd240c24b, 0x54d4b0e5, 0x9f886340, 0x828d53f8, 0x49d1805d, 0xcf45f2f3, 0x04192156,
|
||||
0xf54f9383, 0x3e134026, 0xb8873288, 0x73dbe12d, 0x6eded195, 0xa5820230, 0x2316709e, 0xe84aa33b,
|
||||
0x1aca1375, 0xd196c0d0, 0x5702b27e, 0x9c5e61db, 0x815b5163, 0x4a0782c6, 0xcc93f068, 0x07cf23cd,
|
||||
0xf6999118, 0x3dc542bd, 0xbb513013, 0x700de3b6, 0x6d08d30e, 0xa65400ab, 0x20c07205, 0xeb9ca1a0,
|
||||
0x11e81eb4, 0xdab4cd11, 0x5c20bfbf, 0x977c6c1a, 0x8a795ca2, 0x41258f07, 0xc7b1fda9, 0x0ced2e0c,
|
||||
0xfdbb9cd9, 0x36e74f7c, 0xb0733dd2, 0x7b2fee77, 0x662adecf, 0xad760d6a, 0x2be27fc4, 0xe0beac61,
|
||||
0x123e1c2f, 0xd962cf8a, 0x5ff6bd24, 0x94aa6e81, 0x89af5e39, 0x42f38d9c, 0xc467ff32, 0x0f3b2c97,
|
||||
0xfe6d9e42, 0x35314de7, 0xb3a53f49, 0x78f9ecec, 0x65fcdc54, 0xaea00ff1, 0x28347d5f, 0xe368aefa,
|
||||
0x16441b82, 0xdd18c827, 0x5b8cba89, 0x90d0692c, 0x8dd55994, 0x46898a31, 0xc01df89f, 0x0b412b3a,
|
||||
0xfa1799ef, 0x314b4a4a, 0xb7df38e4, 0x7c83eb41, 0x6186dbf9, 0xaada085c, 0x2c4e7af2, 0xe712a957,
|
||||
0x15921919, 0xdececabc, 0x585ab812, 0x93066bb7, 0x8e035b0f, 0x455f88aa, 0xc3cbfa04, 0x089729a1,
|
||||
0xf9c19b74, 0x329d48d1, 0xb4093a7f, 0x7f55e9da, 0x6250d962, 0xa90c0ac7, 0x2f987869, 0xe4c4abcc,
|
||||
},
|
||||
{
|
||||
0x00000000, 0xa6770bb4, 0x979f1129, 0x31e81a9d, 0xf44f2413, 0x52382fa7, 0x63d0353a, 0xc5a73e8e,
|
||||
0x33ef4e67, 0x959845d3, 0xa4705f4e, 0x020754fa, 0xc7a06a74, 0x61d761c0, 0x503f7b5d, 0xf64870e9,
|
||||
0x67de9cce, 0xc1a9977a, 0xf0418de7, 0x56368653, 0x9391b8dd, 0x35e6b369, 0x040ea9f4, 0xa279a240,
|
||||
0x5431d2a9, 0xf246d91d, 0xc3aec380, 0x65d9c834, 0xa07ef6ba, 0x0609fd0e, 0x37e1e793, 0x9196ec27,
|
||||
0xcfbd399c, 0x69ca3228, 0x582228b5, 0xfe552301, 0x3bf21d8f, 0x9d85163b, 0xac6d0ca6, 0x0a1a0712,
|
||||
0xfc5277fb, 0x5a257c4f, 0x6bcd66d2, 0xcdba6d66, 0x081d53e8, 0xae6a585c, 0x9f8242c1, 0x39f54975,
|
||||
0xa863a552, 0x0e14aee6, 0x3ffcb47b, 0x998bbfcf, 0x5c2c8141, 0xfa5b8af5, 0xcbb39068, 0x6dc49bdc,
|
||||
0x9b8ceb35, 0x3dfbe081, 0x0c13fa1c, 0xaa64f1a8, 0x6fc3cf26, 0xc9b4c492, 0xf85cde0f, 0x5e2bd5bb,
|
||||
0x440b7579, 0xe27c7ecd, 0xd3946450, 0x75e36fe4, 0xb044516a, 0x16335ade, 0x27db4043, 0x81ac4bf7,
|
||||
0x77e43b1e, 0xd19330aa, 0xe07b2a37, 0x460c2183, 0x83ab1f0d, 0x25dc14b9, 0x14340e24, 0xb2430590,
|
||||
0x23d5e9b7, 0x85a2e203, 0xb44af89e, 0x123df32a, 0xd79acda4, 0x71edc610, 0x4005dc8d, 0xe672d739,
|
||||
0x103aa7d0, 0xb64dac64, 0x87a5b6f9, 0x21d2bd4d, 0xe47583c3, 0x42028877, 0x73ea92ea, 0xd59d995e,
|
||||
0x8bb64ce5, 0x2dc14751, 0x1c295dcc, 0xba5e5678, 0x7ff968f6, 0xd98e6342, 0xe86679df, 0x4e11726b,
|
||||
0xb8590282, 0x1e2e0936, 0x2fc613ab, 0x89b1181f, 0x4c162691, 0xea612d25, 0xdb8937b8, 0x7dfe3c0c,
|
||||
0xec68d02b, 0x4a1fdb9f, 0x7bf7c102, 0xdd80cab6, 0x1827f438, 0xbe50ff8c, 0x8fb8e511, 0x29cfeea5,
|
||||
0xdf879e4c, 0x79f095f8, 0x48188f65, 0xee6f84d1, 0x2bc8ba5f, 0x8dbfb1eb, 0xbc57ab76, 0x1a20a0c2,
|
||||
0x8816eaf2, 0x2e61e146, 0x1f89fbdb, 0xb9fef06f, 0x7c59cee1, 0xda2ec555, 0xebc6dfc8, 0x4db1d47c,
|
||||
0xbbf9a495, 0x1d8eaf21, 0x2c66b5bc, 0x8a11be08, 0x4fb68086, 0xe9c18b32, 0xd82991af, 0x7e5e9a1b,
|
||||
0xefc8763c, 0x49bf7d88, 0x78576715, 0xde206ca1, 0x1b87522f, 0xbdf0599b, 0x8c184306, 0x2a6f48b2,
|
||||
0xdc27385b, 0x7a5033ef, 0x4bb82972, 0xedcf22c6, 0x28681c48, 0x8e1f17fc, 0xbff70d61, 0x198006d5,
|
||||
0x47abd36e, 0xe1dcd8da, 0xd034c247, 0x7643c9f3, 0xb3e4f77d, 0x1593fcc9, 0x247be654, 0x820cede0,
|
||||
0x74449d09, 0xd23396bd, 0xe3db8c20, 0x45ac8794, 0x800bb91a, 0x267cb2ae, 0x1794a833, 0xb1e3a387,
|
||||
0x20754fa0, 0x86024414, 0xb7ea5e89, 0x119d553d, 0xd43a6bb3, 0x724d6007, 0x43a57a9a, 0xe5d2712e,
|
||||
0x139a01c7, 0xb5ed0a73, 0x840510ee, 0x22721b5a, 0xe7d525d4, 0x41a22e60, 0x704a34fd, 0xd63d3f49,
|
||||
0xcc1d9f8b, 0x6a6a943f, 0x5b828ea2, 0xfdf58516, 0x3852bb98, 0x9e25b02c, 0xafcdaab1, 0x09baa105,
|
||||
0xfff2d1ec, 0x5985da58, 0x686dc0c5, 0xce1acb71, 0x0bbdf5ff, 0xadcafe4b, 0x9c22e4d6, 0x3a55ef62,
|
||||
0xabc30345, 0x0db408f1, 0x3c5c126c, 0x9a2b19d8, 0x5f8c2756, 0xf9fb2ce2, 0xc813367f, 0x6e643dcb,
|
||||
0x982c4d22, 0x3e5b4696, 0x0fb35c0b, 0xa9c457bf, 0x6c636931, 0xca146285, 0xfbfc7818, 0x5d8b73ac,
|
||||
0x03a0a617, 0xa5d7ada3, 0x943fb73e, 0x3248bc8a, 0xf7ef8204, 0x519889b0, 0x6070932d, 0xc6079899,
|
||||
0x304fe870, 0x9638e3c4, 0xa7d0f959, 0x01a7f2ed, 0xc400cc63, 0x6277c7d7, 0x539fdd4a, 0xf5e8d6fe,
|
||||
0x647e3ad9, 0xc209316d, 0xf3e12bf0, 0x55962044, 0x90311eca, 0x3646157e, 0x07ae0fe3, 0xa1d90457,
|
||||
0x579174be, 0xf1e67f0a, 0xc00e6597, 0x66796e23, 0xa3de50ad, 0x05a95b19, 0x34414184, 0x92364a30,
|
||||
},
|
||||
{
|
||||
0x00000000, 0xccaa009e, 0x4225077d, 0x8e8f07e3, 0x844a0efa, 0x48e00e64, 0xc66f0987, 0x0ac50919,
|
||||
0xd3e51bb5, 0x1f4f1b2b, 0x91c01cc8, 0x5d6a1c56, 0x57af154f, 0x9b0515d1, 0x158a1232, 0xd92012ac,
|
||||
0x7cbb312b, 0xb01131b5, 0x3e9e3656, 0xf23436c8, 0xf8f13fd1, 0x345b3f4f, 0xbad438ac, 0x767e3832,
|
||||
0xaf5e2a9e, 0x63f42a00, 0xed7b2de3, 0x21d12d7d, 0x2b142464, 0xe7be24fa, 0x69312319, 0xa59b2387,
|
||||
0xf9766256, 0x35dc62c8, 0xbb53652b, 0x77f965b5, 0x7d3c6cac, 0xb1966c32, 0x3f196bd1, 0xf3b36b4f,
|
||||
0x2a9379e3, 0xe639797d, 0x68b67e9e, 0xa41c7e00, 0xaed97719, 0x62737787, 0xecfc7064, 0x205670fa,
|
||||
0x85cd537d, 0x496753e3, 0xc7e85400, 0x0b42549e, 0x01875d87, 0xcd2d5d19, 0x43a25afa, 0x8f085a64,
|
||||
0x562848c8, 0x9a824856, 0x140d4fb5, 0xd8a74f2b, 0xd2624632, 0x1ec846ac, 0x9047414f, 0x5ced41d1,
|
||||
0x299dc2ed, 0xe537c273, 0x6bb8c590, 0xa712c50e, 0xadd7cc17, 0x617dcc89, 0xeff2cb6a, 0x2358cbf4,
|
||||
0xfa78d958, 0x36d2d9c6, 0xb85dde25, 0x74f7debb, 0x7e32d7a2, 0xb298d73c, 0x3c17d0df, 0xf0bdd041,
|
||||
0x5526f3c6, 0x998cf358, 0x1703f4bb, 0xdba9f425, 0xd16cfd3c, 0x1dc6fda2, 0x9349fa41, 0x5fe3fadf,
|
||||
0x86c3e873, 0x4a69e8ed, 0xc4e6ef0e, 0x084cef90, 0x0289e689, 0xce23e617, 0x40ace1f4, 0x8c06e16a,
|
||||
0xd0eba0bb, 0x1c41a025, 0x92cea7c6, 0x5e64a758, 0x54a1ae41, 0x980baedf, 0x1684a93c, 0xda2ea9a2,
|
||||
0x030ebb0e, 0xcfa4bb90, 0x412bbc73, 0x8d81bced, 0x8744b5f4, 0x4beeb56a, 0xc561b289, 0x09cbb217,
|
||||
0xac509190, 0x60fa910e, 0xee7596ed, 0x22df9673, 0x281a9f6a, 0xe4b09ff4, 0x6a3f9817, 0xa6959889,
|
||||
0x7fb58a25, 0xb31f8abb, 0x3d908d58, 0xf13a8dc6, 0xfbff84df, 0x37558441, 0xb9da83a2, 0x7570833c,
|
||||
0x533b85da, 0x9f918544, 0x111e82a7, 0xddb48239, 0xd7718b20, 0x1bdb8bbe, 0x95548c5d, 0x59fe8cc3,
|
||||
0x80de9e6f, 0x4c749ef1, 0xc2fb9912, 0x0e51998c, 0x04949095, 0xc83e900b, 0x46b197e8, 0x8a1b9776,
|
||||
0x2f80b4f1, 0xe32ab46f, 0x6da5b38c, 0xa10fb312, 0xabcaba0b, 0x6760ba95, 0xe9efbd76, 0x2545bde8,
|
||||
0xfc65af44, 0x30cfafda, 0xbe40a839, 0x72eaa8a7, 0x782fa1be, 0xb485a120, 0x3a0aa6c3, 0xf6a0a65d,
|
||||
0xaa4de78c, 0x66e7e712, 0xe868e0f1, 0x24c2e06f, 0x2e07e976, 0xe2ade9e8, 0x6c22ee0b, 0xa088ee95,
|
||||
0x79a8fc39, 0xb502fca7, 0x3b8dfb44, 0xf727fbda, 0xfde2f2c3, 0x3148f25d, 0xbfc7f5be, 0x736df520,
|
||||
0xd6f6d6a7, 0x1a5cd639, 0x94d3d1da, 0x5879d144, 0x52bcd85d, 0x9e16d8c3, 0x1099df20, 0xdc33dfbe,
|
||||
0x0513cd12, 0xc9b9cd8c, 0x4736ca6f, 0x8b9ccaf1, 0x8159c3e8, 0x4df3c376, 0xc37cc495, 0x0fd6c40b,
|
||||
0x7aa64737, 0xb60c47a9, 0x3883404a, 0xf42940d4, 0xfeec49cd, 0x32464953, 0xbcc94eb0, 0x70634e2e,
|
||||
0xa9435c82, 0x65e95c1c, 0xeb665bff, 0x27cc5b61, 0x2d095278, 0xe1a352e6, 0x6f2c5505, 0xa386559b,
|
||||
0x061d761c, 0xcab77682, 0x44387161, 0x889271ff, 0x825778e6, 0x4efd7878, 0xc0727f9b, 0x0cd87f05,
|
||||
0xd5f86da9, 0x19526d37, 0x97dd6ad4, 0x5b776a4a, 0x51b26353, 0x9d1863cd, 0x1397642e, 0xdf3d64b0,
|
||||
0x83d02561, 0x4f7a25ff, 0xc1f5221c, 0x0d5f2282, 0x079a2b9b, 0xcb302b05, 0x45bf2ce6, 0x89152c78,
|
||||
0x50353ed4, 0x9c9f3e4a, 0x121039a9, 0xdeba3937, 0xd47f302e, 0x18d530b0, 0x965a3753, 0x5af037cd,
|
||||
0xff6b144a, 0x33c114d4, 0xbd4e1337, 0x71e413a9, 0x7b211ab0, 0xb78b1a2e, 0x39041dcd, 0xf5ae1d53,
|
||||
0x2c8e0fff, 0xe0240f61, 0x6eab0882, 0xa201081c, 0xa8c40105, 0x646e019b, 0xeae10678, 0x264b06e6,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@(optimization_mode="speed")
|
||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
result := ~u32(seed);
|
||||
#no_bounds_check for b in data {
|
||||
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
|
||||
|
||||
@private _crc32_table := [256]u32{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
||||
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
|
||||
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
|
||||
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
|
||||
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
|
||||
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
|
||||
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
|
||||
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
||||
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
|
||||
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
|
||||
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||||
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
|
||||
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
|
||||
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
|
||||
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
|
||||
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
|
||||
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
|
||||
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
||||
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
|
||||
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
|
||||
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
};
|
||||
*/
|
||||
+75
-152
@@ -1,32 +1,61 @@
|
||||
package hash
|
||||
|
||||
import "core:mem"
|
||||
import "intrinsics"
|
||||
|
||||
@(optimization_mode="speed")
|
||||
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
|
||||
adler32 :: proc(data: []byte) -> u32 {
|
||||
ADLER_CONST :: 65521;
|
||||
a, b: u32 = 1, 0;
|
||||
for x in data {
|
||||
a = (a + u32(x)) % ADLER_CONST;
|
||||
|
||||
buffer := raw_data(data);
|
||||
a, b: u64 = u64(seed) & 0xFFFF, u64(seed) >> 16;
|
||||
buf := data[:];
|
||||
|
||||
for len(buf) != 0 && uintptr(buffer) & 7 != 0 {
|
||||
a = (a + u64(buf[0]));
|
||||
b = (b + a);
|
||||
buffer = intrinsics.ptr_offset(buffer, 1);
|
||||
buf = buf[1:];
|
||||
}
|
||||
|
||||
for len(buf) > 7 {
|
||||
count := min(len(buf), 5552);
|
||||
for count > 7 {
|
||||
a += u64(buf[0]); b += a;
|
||||
a += u64(buf[1]); b += a;
|
||||
a += u64(buf[2]); b += a;
|
||||
a += u64(buf[3]); b += a;
|
||||
a += u64(buf[4]); b += a;
|
||||
a += u64(buf[5]); b += a;
|
||||
a += u64(buf[6]); b += a;
|
||||
a += u64(buf[7]); b += a;
|
||||
|
||||
buf = buf[8:];
|
||||
count -= 8;
|
||||
}
|
||||
a %= ADLER_CONST;
|
||||
b %= ADLER_CONST;
|
||||
}
|
||||
|
||||
for len(buf) != 0 {
|
||||
a = (a + u64(buf[0])) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
buf = buf[1:];
|
||||
}
|
||||
return (b << 16) | a;
|
||||
return (u32(b) << 16) | u32(a);
|
||||
}
|
||||
|
||||
crc32 :: proc(data: []byte) -> u32 {
|
||||
result := ~u32(0);
|
||||
@(optimization_mode="speed")
|
||||
djb2 :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 5381;
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||
hash = (hash << 5) + hash + u32(b); // hash * 33 + u32(b)
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
crc64 :: proc(data: []byte) -> u64 {
|
||||
result := ~u64(0);
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv32 :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
@@ -35,6 +64,7 @@ fnv32 :: proc(data: []byte) -> u32 {
|
||||
return h;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv64 :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
@@ -43,6 +73,7 @@ fnv64 :: proc(data: []byte) -> u64 {
|
||||
return h;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv32a :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
@@ -51,6 +82,7 @@ fnv32a :: proc(data: []byte) -> u32 {
|
||||
return h;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv64a :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
@@ -59,13 +91,28 @@ fnv64a :: proc(data: []byte) -> u64 {
|
||||
return h;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
jenkins :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 0;
|
||||
for b in data {
|
||||
hash += u32(b);
|
||||
hash += hash << 10;
|
||||
hash ~= hash >> 6;
|
||||
}
|
||||
hash += hash << 3;
|
||||
hash ~= hash >> 11;
|
||||
hash += hash << 15;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
murmur32 :: proc(data: []byte) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51;
|
||||
c2_32: u32 : 0x1b873593;
|
||||
|
||||
h1: u32 = 0;
|
||||
nblocks := len(data)/4;
|
||||
p := &data[0];
|
||||
p := raw_data(data);
|
||||
p1 := mem.ptr_offset(p, 4*nblocks);
|
||||
|
||||
for ; p < p1; p = mem.ptr_offset(p, 4) {
|
||||
@@ -108,6 +155,7 @@ murmur32 :: proc(data: []byte) -> u32 {
|
||||
return h1;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
murmur64 :: proc(data: []byte) -> u64 {
|
||||
SEED :: 0x9747b28c;
|
||||
|
||||
@@ -116,7 +164,7 @@ murmur64 :: proc(data: []byte) -> u64 {
|
||||
r :: 47;
|
||||
|
||||
h: u64 = SEED ~ (u64(len(data)) * m);
|
||||
data64 := mem.slice_ptr(cast(^u64)&data[0], len(data)/size_of(u64));
|
||||
data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64));
|
||||
|
||||
for _, i in data64 {
|
||||
k := data64[i];
|
||||
@@ -152,7 +200,7 @@ murmur64 :: proc(data: []byte) -> u64 {
|
||||
|
||||
h1 := u32(SEED) ~ u32(len(data));
|
||||
h2 := u32(SEED) >> 32;
|
||||
data32 := mem.slice_ptr(cast(^u32)&data[0], len(data)/size_of(u32));
|
||||
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32));
|
||||
len := len(data);
|
||||
i := 0;
|
||||
|
||||
@@ -213,136 +261,11 @@ murmur64 :: proc(data: []byte) -> u64 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_crc32_table := [256]u32{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
||||
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
|
||||
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
|
||||
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
|
||||
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
|
||||
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
|
||||
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
|
||||
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
||||
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
|
||||
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
|
||||
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||||
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
|
||||
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
|
||||
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
|
||||
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
|
||||
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
|
||||
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
|
||||
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
||||
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
|
||||
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
|
||||
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
};
|
||||
_crc64_table := [256]u64{
|
||||
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
||||
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
||||
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
|
||||
0xdb55aacf12c73561, 0x99a54b24bb2d03f2, 0x5eb4691841135847, 0x1c4488f3e8f96ed4,
|
||||
0x663d78ff90e185ef, 0x24cd9914390bb37c, 0xe3dcbb28c335e8c9, 0xa12c5ac36adfde5a,
|
||||
0x2f0e1eba9ea36930, 0x6dfeff5137495fa3, 0xaaefdd6dcd770416, 0xe81f3c86649d3285,
|
||||
0xf45bb4758c645c51, 0xb6ab559e258e6ac2, 0x71ba77a2dfb03177, 0x334a9649765a07e4,
|
||||
0xbd68d2308226b08e, 0xff9833db2bcc861d, 0x388911e7d1f2dda8, 0x7a79f00c7818eb3b,
|
||||
0xcc7af1ff21c30bde, 0x8e8a101488293d4d, 0x499b3228721766f8, 0x0b6bd3c3dbfd506b,
|
||||
0x854997ba2f81e701, 0xc7b97651866bd192, 0x00a8546d7c558a27, 0x4258b586d5bfbcb4,
|
||||
0x5e1c3d753d46d260, 0x1cecdc9e94ace4f3, 0xdbfdfea26e92bf46, 0x990d1f49c77889d5,
|
||||
0x172f5b3033043ebf, 0x55dfbadb9aee082c, 0x92ce98e760d05399, 0xd03e790cc93a650a,
|
||||
0xaa478900b1228e31, 0xe8b768eb18c8b8a2, 0x2fa64ad7e2f6e317, 0x6d56ab3c4b1cd584,
|
||||
0xe374ef45bf6062ee, 0xa1840eae168a547d, 0x66952c92ecb40fc8, 0x2465cd79455e395b,
|
||||
0x3821458aada7578f, 0x7ad1a461044d611c, 0xbdc0865dfe733aa9, 0xff3067b657990c3a,
|
||||
0x711223cfa3e5bb50, 0x33e2c2240a0f8dc3, 0xf4f3e018f031d676, 0xb60301f359dbe0e5,
|
||||
0xda050215ea6c212f, 0x98f5e3fe438617bc, 0x5fe4c1c2b9b84c09, 0x1d14202910527a9a,
|
||||
0x93366450e42ecdf0, 0xd1c685bb4dc4fb63, 0x16d7a787b7faa0d6, 0x5427466c1e109645,
|
||||
0x4863ce9ff6e9f891, 0x0a932f745f03ce02, 0xcd820d48a53d95b7, 0x8f72eca30cd7a324,
|
||||
0x0150a8daf8ab144e, 0x43a04931514122dd, 0x84b16b0dab7f7968, 0xc6418ae602954ffb,
|
||||
0xbc387aea7a8da4c0, 0xfec89b01d3679253, 0x39d9b93d2959c9e6, 0x7b2958d680b3ff75,
|
||||
0xf50b1caf74cf481f, 0xb7fbfd44dd257e8c, 0x70eadf78271b2539, 0x321a3e938ef113aa,
|
||||
0x2e5eb66066087d7e, 0x6cae578bcfe24bed, 0xabbf75b735dc1058, 0xe94f945c9c3626cb,
|
||||
0x676dd025684a91a1, 0x259d31cec1a0a732, 0xe28c13f23b9efc87, 0xa07cf2199274ca14,
|
||||
0x167ff3eacbaf2af1, 0x548f120162451c62, 0x939e303d987b47d7, 0xd16ed1d631917144,
|
||||
0x5f4c95afc5edc62e, 0x1dbc74446c07f0bd, 0xdaad56789639ab08, 0x985db7933fd39d9b,
|
||||
0x84193f60d72af34f, 0xc6e9de8b7ec0c5dc, 0x01f8fcb784fe9e69, 0x43081d5c2d14a8fa,
|
||||
0xcd2a5925d9681f90, 0x8fdab8ce70822903, 0x48cb9af28abc72b6, 0x0a3b7b1923564425,
|
||||
0x70428b155b4eaf1e, 0x32b26afef2a4998d, 0xf5a348c2089ac238, 0xb753a929a170f4ab,
|
||||
0x3971ed50550c43c1, 0x7b810cbbfce67552, 0xbc902e8706d82ee7, 0xfe60cf6caf321874,
|
||||
0xe224479f47cb76a0, 0xa0d4a674ee214033, 0x67c58448141f1b86, 0x253565a3bdf52d15,
|
||||
0xab1721da49899a7f, 0xe9e7c031e063acec, 0x2ef6e20d1a5df759, 0x6c0603e6b3b7c1ca,
|
||||
0xf6fae5c07d3274cd, 0xb40a042bd4d8425e, 0x731b26172ee619eb, 0x31ebc7fc870c2f78,
|
||||
0xbfc9838573709812, 0xfd39626eda9aae81, 0x3a28405220a4f534, 0x78d8a1b9894ec3a7,
|
||||
0x649c294a61b7ad73, 0x266cc8a1c85d9be0, 0xe17dea9d3263c055, 0xa38d0b769b89f6c6,
|
||||
0x2daf4f0f6ff541ac, 0x6f5faee4c61f773f, 0xa84e8cd83c212c8a, 0xeabe6d3395cb1a19,
|
||||
0x90c79d3fedd3f122, 0xd2377cd44439c7b1, 0x15265ee8be079c04, 0x57d6bf0317edaa97,
|
||||
0xd9f4fb7ae3911dfd, 0x9b041a914a7b2b6e, 0x5c1538adb04570db, 0x1ee5d94619af4648,
|
||||
0x02a151b5f156289c, 0x4051b05e58bc1e0f, 0x87409262a28245ba, 0xc5b073890b687329,
|
||||
0x4b9237f0ff14c443, 0x0962d61b56fef2d0, 0xce73f427acc0a965, 0x8c8315cc052a9ff6,
|
||||
0x3a80143f5cf17f13, 0x7870f5d4f51b4980, 0xbf61d7e80f251235, 0xfd913603a6cf24a6,
|
||||
0x73b3727a52b393cc, 0x31439391fb59a55f, 0xf652b1ad0167feea, 0xb4a25046a88dc879,
|
||||
0xa8e6d8b54074a6ad, 0xea16395ee99e903e, 0x2d071b6213a0cb8b, 0x6ff7fa89ba4afd18,
|
||||
0xe1d5bef04e364a72, 0xa3255f1be7dc7ce1, 0x64347d271de22754, 0x26c49cccb40811c7,
|
||||
0x5cbd6cc0cc10fafc, 0x1e4d8d2b65facc6f, 0xd95caf179fc497da, 0x9bac4efc362ea149,
|
||||
0x158e0a85c2521623, 0x577eeb6e6bb820b0, 0x906fc95291867b05, 0xd29f28b9386c4d96,
|
||||
0xcedba04ad0952342, 0x8c2b41a1797f15d1, 0x4b3a639d83414e64, 0x09ca82762aab78f7,
|
||||
0x87e8c60fded7cf9d, 0xc51827e4773df90e, 0x020905d88d03a2bb, 0x40f9e43324e99428,
|
||||
0x2cffe7d5975e55e2, 0x6e0f063e3eb46371, 0xa91e2402c48a38c4, 0xebeec5e96d600e57,
|
||||
0x65cc8190991cb93d, 0x273c607b30f68fae, 0xe02d4247cac8d41b, 0xa2dda3ac6322e288,
|
||||
0xbe992b5f8bdb8c5c, 0xfc69cab42231bacf, 0x3b78e888d80fe17a, 0x7988096371e5d7e9,
|
||||
0xf7aa4d1a85996083, 0xb55aacf12c735610, 0x724b8ecdd64d0da5, 0x30bb6f267fa73b36,
|
||||
0x4ac29f2a07bfd00d, 0x08327ec1ae55e69e, 0xcf235cfd546bbd2b, 0x8dd3bd16fd818bb8,
|
||||
0x03f1f96f09fd3cd2, 0x41011884a0170a41, 0x86103ab85a2951f4, 0xc4e0db53f3c36767,
|
||||
0xd8a453a01b3a09b3, 0x9a54b24bb2d03f20, 0x5d45907748ee6495, 0x1fb5719ce1045206,
|
||||
0x919735e51578e56c, 0xd367d40ebc92d3ff, 0x1476f63246ac884a, 0x568617d9ef46bed9,
|
||||
0xe085162ab69d5e3c, 0xa275f7c11f7768af, 0x6564d5fde549331a, 0x279434164ca30589,
|
||||
0xa9b6706fb8dfb2e3, 0xeb46918411358470, 0x2c57b3b8eb0bdfc5, 0x6ea7525342e1e956,
|
||||
0x72e3daa0aa188782, 0x30133b4b03f2b111, 0xf7021977f9cceaa4, 0xb5f2f89c5026dc37,
|
||||
0x3bd0bce5a45a6b5d, 0x79205d0e0db05dce, 0xbe317f32f78e067b, 0xfcc19ed95e6430e8,
|
||||
0x86b86ed5267cdbd3, 0xc4488f3e8f96ed40, 0x0359ad0275a8b6f5, 0x41a94ce9dc428066,
|
||||
0xcf8b0890283e370c, 0x8d7be97b81d4019f, 0x4a6acb477bea5a2a, 0x089a2aacd2006cb9,
|
||||
0x14dea25f3af9026d, 0x562e43b4931334fe, 0x913f6188692d6f4b, 0xd3cf8063c0c759d8,
|
||||
0x5dedc41a34bbeeb2, 0x1f1d25f19d51d821, 0xd80c07cd676f8394, 0x9afce626ce85b507,
|
||||
};
|
||||
@(optimization_mode="speed")
|
||||
sdbm :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 0;
|
||||
for b in data {
|
||||
hash = u32(b) + (hash<<6) + (hash<<16) - hash;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package hash
|
||||
|
||||
ginger_hash8 :: proc(x: u8) -> u8 {
|
||||
h := x * 251;
|
||||
h += ~(x << 3);
|
||||
h ~= (x >> 1);
|
||||
h += ~(x << 7);
|
||||
h ~= (x >> 6);
|
||||
h += (x << 2);
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
ginger_hash16 :: proc(x: u16) -> u16 {
|
||||
z := (x << 8) | (x >> 8);
|
||||
h := z;
|
||||
h += ~(z << 5);
|
||||
h ~= (z >> 2);
|
||||
h += ~(z << 13);
|
||||
h ~= (z >> 10);
|
||||
h += ~(z << 4);
|
||||
h = (h << 10) | (h >> 10);
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
ginger8 :: proc(data: []byte) -> u8 {
|
||||
h := ginger_hash8(0);
|
||||
for b in data {
|
||||
h ~= ginger_hash8(b);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
ginger16 :: proc(data: []byte) -> u16 {
|
||||
h := ginger_hash16(0);
|
||||
for b in data {
|
||||
h ~= ginger_hash16(u16(b));
|
||||
}
|
||||
return h;
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package image
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
*/
|
||||
|
||||
import "core:bytes"
|
||||
import "core:mem"
|
||||
|
||||
Image :: struct {
|
||||
width: int,
|
||||
height: int,
|
||||
channels: int,
|
||||
depth: int,
|
||||
pixels: bytes.Buffer,
|
||||
/*
|
||||
Some image loaders/writers can return/take an optional background color.
|
||||
For convenience, we return them as u16 so we don't need to switch on the type
|
||||
in our viewer, and can just test against nil.
|
||||
*/
|
||||
background: Maybe([3]u16),
|
||||
|
||||
metadata_ptr: rawptr,
|
||||
metadata_type: typeid,
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT: `.do_not_expand_*` options currently skip handling of the `alpha_*` options,
|
||||
therefore Gray+Alpha will be returned as such even if you add `.alpha_drop_if_present`,
|
||||
and `.alpha_add_if_missing` and keyed transparency will likewise be ignored.
|
||||
|
||||
The same goes for indexed images. This will be remedied in a near future update.
|
||||
*/
|
||||
|
||||
/*
|
||||
Image_Option:
|
||||
`.info`
|
||||
This option behaves as `.return_ihdr` and `.do_not_decompress_image` and can be used
|
||||
to gather an image's dimensions and color information.
|
||||
|
||||
`.return_header`
|
||||
Fill out img.sidecar.header with the image's format-specific header struct.
|
||||
If we only care about the image specs, we can set `.return_header` +
|
||||
`.do_not_decompress_image`, or `.info`, which works as if both of these were set.
|
||||
|
||||
`.return_metadata`
|
||||
Returns all chunks not needed to decode the data.
|
||||
It also returns the header as if `.return_header` was set.
|
||||
|
||||
`.do_not_decompress_image`
|
||||
Skip decompressing IDAT chunk, defiltering and the rest.
|
||||
|
||||
`.do_not_expand_grayscale`
|
||||
Do not turn grayscale (+ Alpha) images into RGB(A).
|
||||
Returns just the 1 or 2 channels present, although 1, 2 and 4 bit are still scaled to 8-bit.
|
||||
|
||||
`.do_not_expand_indexed`
|
||||
Do not turn indexed (+ Alpha) images into RGB(A).
|
||||
Returns just the 1 or 2 (with `tRNS`) channels present.
|
||||
Make sure to use `return_metadata` to also return the palette chunk so you can recolor it yourself.
|
||||
|
||||
`.do_not_expand_channels`
|
||||
Applies both `.do_not_expand_grayscale` and `.do_not_expand_indexed`.
|
||||
|
||||
`.alpha_add_if_missing`
|
||||
If the image has no alpha channel, it'll add one set to max(type).
|
||||
Turns RGB into RGBA and Gray into Gray+Alpha
|
||||
|
||||
`.alpha_drop_if_present`
|
||||
If the image has an alpha channel, drop it.
|
||||
You may want to use `.alpha_premultiply` in this case.
|
||||
|
||||
NOTE: For PNG, this also skips handling of the tRNS chunk, if present,
|
||||
unless you select `alpha_premultiply`.
|
||||
In this case it'll premultiply the specified pixels in question only,
|
||||
as the others are implicitly fully opaque.
|
||||
|
||||
`.alpha_premultiply`
|
||||
If the image has an alpha channel, returns image data as follows:
|
||||
RGB *= A, Gray = Gray *= A
|
||||
|
||||
`.blend_background`
|
||||
If a bKGD chunk is present in a PNG, we normally just set `img.background`
|
||||
with its value and leave it up to the application to decide how to display the image,
|
||||
as per the PNG specification.
|
||||
|
||||
With `.blend_background` selected, we blend the image against the background
|
||||
color. As this negates the use for an alpha channel, we'll drop it _unless_
|
||||
you also specify `.alpha_add_if_missing`.
|
||||
|
||||
Options that don't apply to an image format will be ignored by their loader.
|
||||
*/
|
||||
|
||||
Option :: enum {
|
||||
info = 0,
|
||||
do_not_decompress_image,
|
||||
return_header,
|
||||
return_metadata,
|
||||
alpha_add_if_missing,
|
||||
alpha_drop_if_present,
|
||||
alpha_premultiply,
|
||||
blend_background,
|
||||
// Unimplemented
|
||||
do_not_expand_grayscale,
|
||||
do_not_expand_indexed,
|
||||
do_not_expand_channels,
|
||||
}
|
||||
Options :: distinct bit_set[Option];
|
||||
|
||||
Error :: enum {
|
||||
Invalid_PNG_Signature,
|
||||
IHDR_Not_First_Chunk,
|
||||
IHDR_Corrupt,
|
||||
IDAT_Missing,
|
||||
IDAT_Must_Be_Contiguous,
|
||||
IDAT_Corrupt,
|
||||
PNG_Does_Not_Adhere_to_Spec,
|
||||
PLTE_Encountered_Unexpectedly,
|
||||
PLTE_Invalid_Length,
|
||||
TRNS_Encountered_Unexpectedly,
|
||||
BKGD_Invalid_Length,
|
||||
Invalid_Image_Dimensions,
|
||||
Unknown_Color_Type,
|
||||
Invalid_Color_Bit_Depth_Combo,
|
||||
Unknown_Filter_Method,
|
||||
Unknown_Interlace_Method,
|
||||
Requested_Channel_Not_Present,
|
||||
Post_Processing_Error,
|
||||
}
|
||||
|
||||
/*
|
||||
Functions to help with image buffer calculations
|
||||
*/
|
||||
|
||||
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
|
||||
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
For when you have an RGB(A) image, but want a particular channel.
|
||||
*/
|
||||
|
||||
Channel :: enum u8 {
|
||||
R = 1,
|
||||
G = 2,
|
||||
B = 3,
|
||||
A = 4,
|
||||
}
|
||||
|
||||
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
|
||||
ok = false;
|
||||
t: bytes.Buffer;
|
||||
|
||||
idx := int(channel);
|
||||
|
||||
if img.channels == 2 && idx == 4 {
|
||||
// Alpha requested, which in a two channel image is index 2: G.
|
||||
idx = 2;
|
||||
}
|
||||
|
||||
if idx > img.channels {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
switch img.depth {
|
||||
case 8:
|
||||
buffer_size := compute_buffer_size(img.width, img.height, 1, 8);
|
||||
t = bytes.Buffer{};
|
||||
resize(&t.buf, buffer_size);
|
||||
|
||||
i := bytes.buffer_to_bytes(&img.pixels);
|
||||
o := bytes.buffer_to_bytes(&t);
|
||||
|
||||
for len(i) > 0 {
|
||||
o[0] = i[idx];
|
||||
i = i[img.channels:];
|
||||
o = o[1:];
|
||||
}
|
||||
case 16:
|
||||
buffer_size := compute_buffer_size(img.width, img.height, 2, 8);
|
||||
t = bytes.Buffer{};
|
||||
resize(&t.buf, buffer_size);
|
||||
|
||||
i := mem.slice_data_cast([]u16, img.pixels.buf[:]);
|
||||
o := mem.slice_data_cast([]u16, t.buf[:]);
|
||||
|
||||
for len(i) > 0 {
|
||||
o[0] = i[idx];
|
||||
i = i[img.channels:];
|
||||
o = o[1:];
|
||||
}
|
||||
case 1, 2, 4:
|
||||
// We shouldn't see this case, as the loader already turns these into 8-bit.
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
res = new(Image);
|
||||
res.width = img.width;
|
||||
res.height = img.height;
|
||||
res.channels = 1;
|
||||
res.depth = img.depth;
|
||||
res.pixels = t;
|
||||
res.background = img.background;
|
||||
res.metadata_ptr = img.metadata_ptr;
|
||||
res.metadata_type = img.metadata_type;
|
||||
|
||||
return res, true;
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
//+ignore
|
||||
package png
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
|
||||
An example of how to use `load`.
|
||||
*/
|
||||
|
||||
import "core:compress"
|
||||
import "core:image"
|
||||
// import "core:image/png"
|
||||
import "core:bytes"
|
||||
import "core:fmt"
|
||||
|
||||
// For PPM writer
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
|
||||
main :: proc() {
|
||||
track := mem.Tracking_Allocator{};
|
||||
mem.tracking_allocator_init(&track, context.allocator);
|
||||
|
||||
context.allocator = mem.tracking_allocator(&track);
|
||||
|
||||
demo();
|
||||
|
||||
if len(track.allocation_map) > 0 {
|
||||
fmt.println("Leaks:");
|
||||
for _, v in track.allocation_map {
|
||||
fmt.printf("\t%v\n\n", v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo :: proc() {
|
||||
file: string;
|
||||
|
||||
options := image.Options{}; // {.return_metadata};
|
||||
err: compress.Error;
|
||||
img: ^image.Image;
|
||||
|
||||
file = "../../../misc/logo-slim.png";
|
||||
|
||||
img, err = load(file, options);
|
||||
defer destroy(img);
|
||||
|
||||
if err != nil {
|
||||
fmt.printf("Trying to read PNG file %v returned %v\n", file, err);
|
||||
} else {
|
||||
v: ^Info;
|
||||
|
||||
fmt.printf("Image: %vx%vx%v, %v-bit.\n", img.width, img.height, img.channels, img.depth);
|
||||
if img.metadata_ptr != nil && img.metadata_type == Info {
|
||||
v = (^Info)(img.metadata_ptr);
|
||||
|
||||
// Handle ancillary chunks as you wish.
|
||||
// We provide helper functions for a few types.
|
||||
for c in v.chunks {
|
||||
#partial switch c.header.type {
|
||||
case .tIME:
|
||||
t, _ := core_time(c);
|
||||
fmt.printf("[tIME]: %v\n", t);
|
||||
case .gAMA:
|
||||
fmt.printf("[gAMA]: %v\n", gamma(c));
|
||||
case .pHYs:
|
||||
phys := phys(c);
|
||||
if phys.unit == .Meter {
|
||||
xm := f32(img.width) / f32(phys.ppu_x);
|
||||
ym := f32(img.height) / f32(phys.ppu_y);
|
||||
dpi_x, dpi_y := phys_to_dpi(phys);
|
||||
fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y);
|
||||
fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y);
|
||||
fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym);
|
||||
} else {
|
||||
fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y);
|
||||
}
|
||||
case .iTXt, .zTXt, .tEXt:
|
||||
res, ok_text := text(c);
|
||||
if ok_text {
|
||||
if c.header.type == .iTXt {
|
||||
fmt.printf("[iTXt] %v (%v:%v): %v\n", res.keyword, res.language, res.keyword_localized, res.text);
|
||||
} else {
|
||||
fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text);
|
||||
}
|
||||
}
|
||||
defer text_destroy(res);
|
||||
case .bKGD:
|
||||
fmt.printf("[bKGD] %v\n", img.background);
|
||||
case .eXIf:
|
||||
res, ok_exif := exif(c);
|
||||
if ok_exif {
|
||||
/*
|
||||
Other than checking the signature and byte order, we don't handle Exif data.
|
||||
If you wish to interpret it, pass it to an Exif parser.
|
||||
*/
|
||||
fmt.printf("[eXIf] %v\n", res);
|
||||
}
|
||||
case .PLTE:
|
||||
plte, plte_ok := plte(c);
|
||||
if plte_ok {
|
||||
fmt.printf("[PLTE] %v\n", plte);
|
||||
} else {
|
||||
fmt.printf("[PLTE] Error\n");
|
||||
}
|
||||
case .hIST:
|
||||
res, ok_hist := hist(c);
|
||||
if ok_hist {
|
||||
fmt.printf("[hIST] %v\n", res);
|
||||
}
|
||||
case .cHRM:
|
||||
res, ok_chrm := chrm(c);
|
||||
if ok_chrm {
|
||||
fmt.printf("[cHRM] %v\n", res);
|
||||
}
|
||||
case .sPLT:
|
||||
res, ok_splt := splt(c);
|
||||
if ok_splt {
|
||||
fmt.printf("[sPLT] %v\n", res);
|
||||
}
|
||||
splt_destroy(res);
|
||||
case .sBIT:
|
||||
if res, ok_sbit := sbit(c); ok_sbit {
|
||||
fmt.printf("[sBIT] %v\n", res);
|
||||
}
|
||||
case .iCCP:
|
||||
res, ok_iccp := iccp(c);
|
||||
if ok_iccp {
|
||||
fmt.printf("[iCCP] %v\n", res);
|
||||
}
|
||||
iccp_destroy(res);
|
||||
case .sRGB:
|
||||
if res, ok_srgb := srgb(c); ok_srgb {
|
||||
fmt.printf("[sRGB] Rendering intent: %v\n", res);
|
||||
}
|
||||
case:
|
||||
type := c.header.type;
|
||||
name := chunk_type_to_name(&type);
|
||||
fmt.printf("[%v]: %v\n", name, c.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && .do_not_decompress_image not_in options && .info not_in options {
|
||||
if ok := write_image_as_ppm("out.ppm", img); ok {
|
||||
fmt.println("Saved decoded image.");
|
||||
} else {
|
||||
fmt.println("Error saving out.ppm.");
|
||||
fmt.println(img);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Crappy PPM writer used during testing. Don't use in production.
|
||||
write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: bool) {
|
||||
|
||||
_bg :: proc(bg: Maybe([3]u16), x, y: int, high := true) -> (res: [3]u16) {
|
||||
if v, ok := bg.?; ok {
|
||||
res = v;
|
||||
} else {
|
||||
if high {
|
||||
l := u16(30 * 256 + 30);
|
||||
|
||||
if (x & 4 == 0) ~ (y & 4 == 0) {
|
||||
res = [3]u16{l, 0, l};
|
||||
} else {
|
||||
res = [3]u16{l >> 1, 0, l >> 1};
|
||||
}
|
||||
} else {
|
||||
if (x & 4 == 0) ~ (y & 4 == 0) {
|
||||
res = [3]u16{30, 30, 30};
|
||||
} else {
|
||||
res = [3]u16{15, 15, 15};
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// profiler.timed_proc();
|
||||
using image;
|
||||
using os;
|
||||
|
||||
flags: int = O_WRONLY|O_CREATE|O_TRUNC;
|
||||
|
||||
img := image;
|
||||
|
||||
// PBM 16-bit images are big endian
|
||||
when ODIN_ENDIAN == "little" {
|
||||
if img.depth == 16 {
|
||||
// The pixel components are in Big Endian. Let's byteswap back.
|
||||
input := mem.slice_data_cast([]u16, img.pixels.buf[:]);
|
||||
output := mem.slice_data_cast([]u16be, img.pixels.buf[:]);
|
||||
#no_bounds_check for v, i in input {
|
||||
output[i] = u16be(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pix := bytes.buffer_to_bytes(&img.pixels);
|
||||
|
||||
if len(pix) == 0 || len(pix) < image.width * image.height * int(image.channels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mode: int = 0;
|
||||
when ODIN_OS == "linux" || ODIN_OS == "darwin" {
|
||||
// NOTE(justasd): 644 (owner read, write; group read; others read)
|
||||
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
}
|
||||
|
||||
fd, err := open(filename, flags, mode);
|
||||
if err != 0 {
|
||||
return false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
write_string(fd,
|
||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) - 1)),
|
||||
);
|
||||
|
||||
if channels == 3 {
|
||||
// We don't handle transparency here...
|
||||
write_ptr(fd, raw_data(pix), len(pix));
|
||||
} else {
|
||||
bpp := depth == 16 ? 2 : 1;
|
||||
bytes_needed := width * height * 3 * bpp;
|
||||
|
||||
op := bytes.Buffer{};
|
||||
bytes.buffer_init_allocator(&op, bytes_needed, bytes_needed);
|
||||
defer bytes.buffer_destroy(&op);
|
||||
|
||||
if channels == 1 {
|
||||
if depth == 16 {
|
||||
assert(len(pix) == width * height * 2);
|
||||
p16 := mem.slice_data_cast([]u16, pix);
|
||||
o16 := mem.slice_data_cast([]u16, op.buf[:]);
|
||||
#no_bounds_check for len(p16) != 0 {
|
||||
r := u16(p16[0]);
|
||||
o16[0] = r;
|
||||
o16[1] = r;
|
||||
o16[2] = r;
|
||||
p16 = p16[1:];
|
||||
o16 = o16[3:];
|
||||
}
|
||||
} else {
|
||||
o := 0;
|
||||
for i := 0; i < len(pix); i += 1 {
|
||||
r := pix[i];
|
||||
op.buf[o ] = r;
|
||||
op.buf[o+1] = r;
|
||||
op.buf[o+2] = r;
|
||||
o += 3;
|
||||
}
|
||||
}
|
||||
write_ptr(fd, raw_data(op.buf), len(op.buf));
|
||||
} else if channels == 2 {
|
||||
if depth == 16 {
|
||||
p16 := mem.slice_data_cast([]u16, pix);
|
||||
o16 := mem.slice_data_cast([]u16, op.buf[:]);
|
||||
|
||||
bgcol := img.background;
|
||||
|
||||
#no_bounds_check for len(p16) != 0 {
|
||||
r := f64(u16(p16[0]));
|
||||
bg: f64;
|
||||
if bgcol != nil {
|
||||
v := bgcol.([3]u16)[0];
|
||||
bg = f64(v);
|
||||
}
|
||||
a := f64(u16(p16[1])) / 65535.0;
|
||||
l := (a * r) + (1 - a) * bg;
|
||||
|
||||
o16[0] = u16(l);
|
||||
o16[1] = u16(l);
|
||||
o16[2] = u16(l);
|
||||
|
||||
p16 = p16[2:];
|
||||
o16 = o16[3:];
|
||||
}
|
||||
} else {
|
||||
o := 0;
|
||||
for i := 0; i < len(pix); i += 2 {
|
||||
r := pix[i]; a := pix[i+1]; a1 := f32(a) / 255.0;
|
||||
c := u8(f32(r) * a1);
|
||||
op.buf[o ] = c;
|
||||
op.buf[o+1] = c;
|
||||
op.buf[o+2] = c;
|
||||
o += 3;
|
||||
}
|
||||
}
|
||||
write_ptr(fd, raw_data(op.buf), len(op.buf));
|
||||
} else if channels == 4 {
|
||||
if depth == 16 {
|
||||
p16 := mem.slice_data_cast([]u16be, pix);
|
||||
o16 := mem.slice_data_cast([]u16be, op.buf[:]);
|
||||
|
||||
#no_bounds_check for len(p16) != 0 {
|
||||
|
||||
bg := _bg(img.background, 0, 0);
|
||||
r := f32(p16[0]);
|
||||
g := f32(p16[1]);
|
||||
b := f32(p16[2]);
|
||||
a := f32(p16[3]) / 65535.0;
|
||||
|
||||
lr := (a * r) + (1 - a) * f32(bg[0]);
|
||||
lg := (a * g) + (1 - a) * f32(bg[1]);
|
||||
lb := (a * b) + (1 - a) * f32(bg[2]);
|
||||
|
||||
o16[0] = u16be(lr);
|
||||
o16[1] = u16be(lg);
|
||||
o16[2] = u16be(lb);
|
||||
|
||||
p16 = p16[4:];
|
||||
o16 = o16[3:];
|
||||
}
|
||||
} else {
|
||||
o := 0;
|
||||
|
||||
for i := 0; i < len(pix); i += 4 {
|
||||
|
||||
x := (i / 4) % width;
|
||||
y := i / width / 4;
|
||||
|
||||
_b := _bg(img.background, x, y, false);
|
||||
bgcol := [3]u8{u8(_b[0]), u8(_b[1]), u8(_b[2])};
|
||||
|
||||
r := f32(pix[i]);
|
||||
g := f32(pix[i+1]);
|
||||
b := f32(pix[i+2]);
|
||||
a := f32(pix[i+3]) / 255.0;
|
||||
|
||||
lr := u8(f32(r) * a + (1 - a) * f32(bgcol[0]));
|
||||
lg := u8(f32(g) * a + (1 - a) * f32(bgcol[1]));
|
||||
lb := u8(f32(b) * a + (1 - a) * f32(bgcol[2]));
|
||||
op.buf[o ] = lr;
|
||||
op.buf[o+1] = lg;
|
||||
op.buf[o+2] = lb;
|
||||
o += 3;
|
||||
}
|
||||
}
|
||||
write_ptr(fd, raw_data(op.buf), len(op.buf));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,525 @@
|
||||
package png
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
|
||||
These are a few useful utility functions to work with PNG images.
|
||||
*/
|
||||
|
||||
import "core:image"
|
||||
import "core:compress/zlib"
|
||||
import coretime "core:time"
|
||||
import "core:strings"
|
||||
import "core:bytes"
|
||||
import "core:mem"
|
||||
|
||||
/*
|
||||
Cleanup of image-specific data.
|
||||
There are other helpers for cleanup of PNG-specific data.
|
||||
Those are named *_destroy, where * is the name of the helper.
|
||||
*/
|
||||
|
||||
destroy :: proc(img: ^Image) {
|
||||
if img == nil {
|
||||
/*
|
||||
Nothing to do.
|
||||
Load must've returned with an error.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
bytes.buffer_destroy(&img.pixels);
|
||||
// Clean up Info.
|
||||
free(img.metadata_ptr);
|
||||
|
||||
/*
|
||||
We don't need to do anything for the individual chunks.
|
||||
They're allocated on the temp allocator, as is info.chunks
|
||||
|
||||
See read_chunk.
|
||||
*/
|
||||
free(img);
|
||||
}
|
||||
|
||||
/*
|
||||
Chunk helpers
|
||||
*/
|
||||
|
||||
gamma :: proc(c: Chunk) -> f32 {
|
||||
assert(c.header.type == .gAMA);
|
||||
res := (^gAMA)(raw_data(c.data))^;
|
||||
when true {
|
||||
// Returns the wrong result on old backend
|
||||
// Fixed for -llvm-api
|
||||
return f32(res.gamma_100k) / 100_000.0;
|
||||
} else {
|
||||
return f32(u32(res.gamma_100k)) / 100_000.0;
|
||||
}
|
||||
}
|
||||
|
||||
INCHES_PER_METER :: 1000.0 / 25.4;
|
||||
|
||||
phys :: proc(c: Chunk) -> pHYs {
|
||||
assert(c.header.type == .pHYs);
|
||||
res := (^pHYs)(raw_data(c.data))^;
|
||||
return res;
|
||||
}
|
||||
|
||||
phys_to_dpi :: proc(p: pHYs) -> (x_dpi, y_dpi: f32) {
|
||||
return f32(p.ppu_x) / INCHES_PER_METER, f32(p.ppu_y) / INCHES_PER_METER;
|
||||
}
|
||||
|
||||
time :: proc(c: Chunk) -> tIME {
|
||||
assert(c.header.type == .tIME);
|
||||
res := (^tIME)(raw_data(c.data))^;
|
||||
return res;
|
||||
}
|
||||
|
||||
core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) {
|
||||
png_time := time(c);
|
||||
using png_time;
|
||||
return coretime.datetime_to_time(
|
||||
int(year), int(month), int(day),
|
||||
int(hour), int(minute), int(second),
|
||||
);
|
||||
}
|
||||
|
||||
text :: proc(c: Chunk) -> (res: Text, ok: bool) {
|
||||
#partial switch c.header.type {
|
||||
case .tEXt:
|
||||
ok = true;
|
||||
|
||||
fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator);
|
||||
if len(fields) == 2 {
|
||||
res.keyword = strings.clone(string(fields[0]));
|
||||
res.text = strings.clone(string(fields[1]));
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
case .zTXt:
|
||||
ok = true;
|
||||
|
||||
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator);
|
||||
if len(fields) != 3 || len(fields[1]) != 0 {
|
||||
// Compression method must be 0=Deflate, which thanks to the split above turns
|
||||
// into an empty slice
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
// Set up ZLIB context and decompress text payload.
|
||||
buf: bytes.Buffer;
|
||||
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf);
|
||||
defer bytes.buffer_destroy(&buf);
|
||||
if zlib_error != nil {
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
res.keyword = strings.clone(string(fields[0]));
|
||||
res.text = strings.clone(bytes.buffer_to_string(&buf));
|
||||
return;
|
||||
case .iTXt:
|
||||
ok = true;
|
||||
|
||||
s := string(c.data);
|
||||
null := strings.index_byte(s, 0);
|
||||
if null == -1 {
|
||||
ok = false; return;
|
||||
}
|
||||
if len(c.data) < null + 4 {
|
||||
// At a minimum, including the \0 following the keyword, we require 5 more bytes.
|
||||
ok = false; return;
|
||||
}
|
||||
res.keyword = strings.clone(string(c.data[:null]));
|
||||
rest := c.data[null+1:];
|
||||
|
||||
compression_flag := rest[:1][0];
|
||||
if compression_flag > 1 {
|
||||
ok = false; return;
|
||||
}
|
||||
compression_method := rest[1:2][0];
|
||||
if compression_flag == 1 && compression_method > 0 {
|
||||
// Only Deflate is supported
|
||||
ok = false; return;
|
||||
}
|
||||
rest = rest[2:];
|
||||
|
||||
// We now expect an optional language keyword and translated keyword, both followed by a \0
|
||||
null = strings.index_byte(string(rest), 0);
|
||||
if null == -1 {
|
||||
ok = false; return;
|
||||
}
|
||||
res.language = strings.clone(string(rest[:null]));
|
||||
rest = rest[null+1:];
|
||||
|
||||
null = strings.index_byte(string(rest), 0);
|
||||
if null == -1 {
|
||||
ok = false; return;
|
||||
}
|
||||
res.keyword_localized = strings.clone(string(rest[:null]));
|
||||
rest = rest[null+1:];
|
||||
if compression_flag == 0 {
|
||||
res.text = strings.clone(string(rest));
|
||||
} else {
|
||||
// Set up ZLIB context and decompress text payload.
|
||||
buf: bytes.Buffer;
|
||||
zlib_error := zlib.inflate_from_byte_array(rest, &buf);
|
||||
defer bytes.buffer_destroy(&buf);
|
||||
if zlib_error != nil {
|
||||
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
res.text = strings.clone(bytes.buffer_to_string(&buf));
|
||||
}
|
||||
return;
|
||||
case:
|
||||
// PNG text helper called with an unrecognized chunk type.
|
||||
ok = false; return;
|
||||
}
|
||||
}
|
||||
|
||||
text_destroy :: proc(text: Text) {
|
||||
delete(text.keyword);
|
||||
delete(text.keyword_localized);
|
||||
delete(text.language);
|
||||
delete(text.text);
|
||||
}
|
||||
|
||||
iccp :: proc(c: Chunk) -> (res: iCCP, ok: bool) {
|
||||
ok = true;
|
||||
|
||||
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator);
|
||||
|
||||
if len(fields[0]) < 1 || len(fields[0]) > 79 {
|
||||
// Invalid profile name
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
if len(fields[1]) != 0 {
|
||||
// Compression method should be a zero, which the split turned into an empty slice.
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
// Set up ZLIB context and decompress iCCP payload
|
||||
buf: bytes.Buffer;
|
||||
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf);
|
||||
if zlib_error != nil {
|
||||
bytes.buffer_destroy(&buf);
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
res.name = strings.clone(string(fields[0]));
|
||||
res.profile = bytes.buffer_to_bytes(&buf);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
iccp_destroy :: proc(i: iCCP) {
|
||||
delete(i.name);
|
||||
|
||||
delete(i.profile);
|
||||
|
||||
}
|
||||
|
||||
srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) {
|
||||
ok = true;
|
||||
|
||||
if c.header.type != .sRGB || len(c.data) != 1 {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
res.intent = sRGB_Rendering_Intent(c.data[0]);
|
||||
if res.intent > max(sRGB_Rendering_Intent) {
|
||||
ok = false; return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) {
|
||||
if c.header.type != .PLTE {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
i := 0; j := 0; ok = true;
|
||||
for j < int(c.header.length) {
|
||||
res.entries[i] = {c.data[j], c.data[j+1], c.data[j+2]};
|
||||
i += 1; j += 3;
|
||||
}
|
||||
res.used = u16(i);
|
||||
return;
|
||||
}
|
||||
|
||||
splt :: proc(c: Chunk) -> (res: sPLT, ok: bool) {
|
||||
if c.header.type != .sPLT {
|
||||
return {}, false;
|
||||
}
|
||||
ok = true;
|
||||
|
||||
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator);
|
||||
if len(fields) != 2 {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
res.depth = fields[1][0];
|
||||
if res.depth != 8 && res.depth != 16 {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
data := fields[1][1:];
|
||||
count: int;
|
||||
|
||||
if res.depth == 8 {
|
||||
if len(data) % 6 != 0 {
|
||||
return {}, false;
|
||||
}
|
||||
count = len(data) / 6;
|
||||
if count > 256 {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
res.entries = mem.slice_data_cast([][4]u8, data);
|
||||
} else { // res.depth == 16
|
||||
if len(data) % 10 != 0 {
|
||||
return {}, false;
|
||||
}
|
||||
count = len(data) / 10;
|
||||
if count > 256 {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
res.entries = mem.slice_data_cast([][4]u16, data);
|
||||
}
|
||||
|
||||
res.name = strings.clone(string(fields[0]));
|
||||
res.used = u16(count);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
splt_destroy :: proc(s: sPLT) {
|
||||
delete(s.name);
|
||||
}
|
||||
|
||||
sbit :: proc(c: Chunk) -> (res: [4]u8, ok: bool) {
|
||||
/*
|
||||
Returns [4]u8 with the significant bits in each channel.
|
||||
A channel will contain zero if not applicable to the PNG color type.
|
||||
*/
|
||||
|
||||
if len(c.data) < 1 || len(c.data) > 4 {
|
||||
ok = false; return;
|
||||
}
|
||||
ok = true;
|
||||
|
||||
for i := 0; i < len(c.data); i += 1 {
|
||||
res[i] = c.data[i];
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
hist :: proc(c: Chunk) -> (res: hIST, ok: bool) {
|
||||
if c.header.type != .hIST {
|
||||
return {}, false;
|
||||
}
|
||||
if c.header.length & 1 == 1 || c.header.length > 512 {
|
||||
// The entries are u16be, so the length must be even.
|
||||
// At most 256 entries must be present
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
data := mem.slice_data_cast([]u16be, c.data);
|
||||
i := 0;
|
||||
for len(data) > 0 {
|
||||
// HIST entries are u16be, we unpack them to machine format
|
||||
res.entries[i] = u16(data[0]);
|
||||
i += 1; data = data[1:];
|
||||
}
|
||||
res.used = u16(i);
|
||||
return;
|
||||
}
|
||||
|
||||
chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) {
|
||||
ok = true;
|
||||
if c.header.length != size_of(cHRM_Raw) {
|
||||
return {}, false;
|
||||
}
|
||||
chrm := (^cHRM_Raw)(raw_data(c.data))^;
|
||||
|
||||
res.w.x = f32(chrm.w.x) / 100_000.0;
|
||||
res.w.y = f32(chrm.w.y) / 100_000.0;
|
||||
res.r.x = f32(chrm.r.x) / 100_000.0;
|
||||
res.r.y = f32(chrm.r.y) / 100_000.0;
|
||||
res.g.x = f32(chrm.g.x) / 100_000.0;
|
||||
res.g.y = f32(chrm.g.y) / 100_000.0;
|
||||
res.b.x = f32(chrm.b.x) / 100_000.0;
|
||||
res.b.y = f32(chrm.b.y) / 100_000.0;
|
||||
return;
|
||||
}
|
||||
|
||||
exif :: proc(c: Chunk) -> (res: Exif, ok: bool) {
|
||||
|
||||
ok = true;
|
||||
|
||||
if len(c.data) < 4 {
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
if c.data[0] == 'M' && c.data[1] == 'M' {
|
||||
res.byte_order = .big_endian;
|
||||
if c.data[2] != 0 || c.data[3] != 42 {
|
||||
ok = false; return;
|
||||
}
|
||||
} else if c.data[0] == 'I' && c.data[1] == 'I' {
|
||||
res.byte_order = .little_endian;
|
||||
if c.data[2] != 42 || c.data[3] != 0 {
|
||||
ok = false; return;
|
||||
}
|
||||
} else {
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
res.data = c.data;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
General helper functions
|
||||
*/
|
||||
|
||||
compute_buffer_size :: image.compute_buffer_size;
|
||||
|
||||
/*
|
||||
PNG save helpers
|
||||
*/
|
||||
|
||||
when false {
|
||||
|
||||
make_chunk :: proc(c: any, t: Chunk_Type) -> (res: Chunk) {
|
||||
|
||||
data: []u8;
|
||||
if v, ok := c.([]u8); ok {
|
||||
data = v;
|
||||
} else {
|
||||
data = mem.any_to_bytes(c);
|
||||
}
|
||||
|
||||
res.header.length = u32be(len(data));
|
||||
res.header.type = t;
|
||||
res.data = data;
|
||||
|
||||
// CRC the type
|
||||
crc := hash.crc32(mem.any_to_bytes(res.header.type));
|
||||
// Extend the CRC with the data
|
||||
res.crc = u32be(hash.crc32(data, crc));
|
||||
return;
|
||||
}
|
||||
|
||||
write_chunk :: proc(fd: os.Handle, chunk: Chunk) {
|
||||
c := chunk;
|
||||
// Write length + type
|
||||
os.write_ptr(fd, &c.header, 8);
|
||||
// Write data
|
||||
os.write_ptr(fd, mem.raw_data(c.data), int(c.header.length));
|
||||
// Write CRC32
|
||||
os.write_ptr(fd, &c.crc, 4);
|
||||
}
|
||||
|
||||
write_image_as_png :: proc(filename: string, image: Image) -> (err: Error) {
|
||||
profiler.timed_proc();
|
||||
using image;
|
||||
using os;
|
||||
flags: int = O_WRONLY|O_CREATE|O_TRUNC;
|
||||
|
||||
if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
|
||||
return E_PNG.Invalid_Image_Dimensions;
|
||||
}
|
||||
|
||||
mode: int = 0;
|
||||
when ODIN_OS == "linux" || ODIN_OS == "darwin" {
|
||||
// NOTE(justasd): 644 (owner read, write; group read; others read)
|
||||
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
}
|
||||
|
||||
fd, fderr := open(filename, flags, mode);
|
||||
if fderr != 0 {
|
||||
return E_General.Cannot_Open_File;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
magic := Signature;
|
||||
|
||||
write_ptr(fd, &magic, 8);
|
||||
|
||||
ihdr := IHDR{
|
||||
width = u32be(width),
|
||||
height = u32be(height),
|
||||
bit_depth = depth,
|
||||
compression_method = 0,
|
||||
filter_method = 0,
|
||||
interlace_method = .None,
|
||||
};
|
||||
|
||||
switch channels {
|
||||
case 1: ihdr.color_type = Color_Type{};
|
||||
case 2: ihdr.color_type = Color_Type{.Alpha};
|
||||
case 3: ihdr.color_type = Color_Type{.Color};
|
||||
case 4: ihdr.color_type = Color_Type{.Color, .Alpha};
|
||||
case:// Unhandled
|
||||
return E_PNG.Unknown_Color_Type;
|
||||
}
|
||||
h := make_chunk(ihdr, .IHDR);
|
||||
write_chunk(fd, h);
|
||||
|
||||
bytes_needed := width * height * int(channels) + height;
|
||||
filter_bytes := mem.make_dynamic_array_len_cap([dynamic]u8, bytes_needed, bytes_needed, context.allocator);
|
||||
defer delete(filter_bytes);
|
||||
|
||||
i := 0; j := 0;
|
||||
// Add a filter byte 0 per pixel row
|
||||
for y := 0; y < height; y += 1 {
|
||||
filter_bytes[j] = 0; j += 1;
|
||||
for x := 0; x < width; x += 1 {
|
||||
for z := 0; z < channels; z += 1 {
|
||||
filter_bytes[j+z] = image.pixels[i+z];
|
||||
}
|
||||
i += channels; j += channels;
|
||||
}
|
||||
}
|
||||
assert(j == bytes_needed);
|
||||
|
||||
a: []u8 = filter_bytes[:];
|
||||
|
||||
out_buf: ^[dynamic]u8;
|
||||
defer free(out_buf);
|
||||
|
||||
ctx := zlib.ZLIB_Context{
|
||||
in_buf = &a,
|
||||
out_buf = out_buf,
|
||||
};
|
||||
err = zlib.write_zlib_stream_from_memory(&ctx);
|
||||
|
||||
b: []u8;
|
||||
if err == nil {
|
||||
b = ctx.out_buf[:];
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
|
||||
idat := make_chunk(b, .IDAT);
|
||||
|
||||
write_chunk(fd, idat);
|
||||
|
||||
iend := make_chunk([]u8{}, .IEND);
|
||||
write_chunk(fd, iend);
|
||||
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+122
-67
@@ -1,83 +1,124 @@
|
||||
// This is purely for documentation
|
||||
//+ignore
|
||||
package intrinsics
|
||||
|
||||
|
||||
x86_mmx :: x86_mmx; // Specialized SIMD Vector type
|
||||
|
||||
// Types
|
||||
simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
|
||||
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
||||
|
||||
// Volatile
|
||||
volatile_load :: proc(dst: ^$T) -> T ---
|
||||
volatile_store :: proc(dst: ^$T, val: T) -> T ---
|
||||
|
||||
// Trapping
|
||||
debug_trap :: proc() ---
|
||||
trap :: proc() -> ! ---
|
||||
|
||||
// Instructions
|
||||
|
||||
alloca :: proc(size, align: int) -> ^u8 ---
|
||||
cpu_relax :: proc() ---
|
||||
read_cycle_counter :: proc() -> i64 ---
|
||||
|
||||
count_ones :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||
|
||||
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
|
||||
sqrt :: proc(x: $T) -> T where type_is_float(T) ---
|
||||
|
||||
mem_copy :: proc(dst, src: rawptr, len: int) ---
|
||||
mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
|
||||
mem_zero :: proc(ptr: rawptr, len: int) ---
|
||||
|
||||
|
||||
fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||
fixed_point_div :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||
fixed_point_mul_sat :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||
fixed_point_div_sat :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||
|
||||
// Compiler Hints
|
||||
expect :: proc(val, expected_val: T) -> T ---
|
||||
|
||||
|
||||
// Atomics
|
||||
atomic_fence :: proc() ---
|
||||
atomic_fence_acq :: proc() ---
|
||||
atomic_fence_rel :: proc() ---
|
||||
atomic_fence_acqrel :: proc() ---
|
||||
|
||||
atomic_store :: proc(dst: ^$T, val: $T) ---
|
||||
atomic_store_rel :: proc(dst: ^$T, val: $T) ---
|
||||
atomic_store_relaxed :: proc(dst: ^$T, val: $T) ---
|
||||
atomic_store_unordered :: proc(dst: ^$T, val: $T) ---
|
||||
atomic_store :: proc(dst: ^$T, val: T) ---
|
||||
atomic_store_rel :: proc(dst: ^$T, val: T) ---
|
||||
atomic_store_relaxed :: proc(dst: ^$T, val: T) ---
|
||||
atomic_store_unordered :: proc(dst: ^$T, val: T) ---
|
||||
|
||||
atomic_load :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_acq :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_relaxed :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_unordered :: proc(dst: ^$T) -> T ---
|
||||
|
||||
atomic_add :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
|
||||
atomic_xchg :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
|
||||
atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
|
||||
atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
|
||||
atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
|
||||
// Constant type tests
|
||||
|
||||
@@ -95,6 +136,7 @@ type_is_string :: proc($T: typeid) -> bool ---
|
||||
type_is_typeid :: proc($T: typeid) -> bool ---
|
||||
type_is_any :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_endian_platform :: proc($T: typeid) -> bool ---
|
||||
type_is_endian_little :: proc($T: typeid) -> bool ---
|
||||
type_is_endian_big :: proc($T: typeid) -> bool ---
|
||||
type_is_unsigned :: proc($T: typeid) -> bool ---
|
||||
@@ -103,13 +145,13 @@ type_is_ordered :: proc($T: typeid) -> bool ---
|
||||
type_is_ordered_numeric :: proc($T: typeid) -> bool ---
|
||||
type_is_indexable :: proc($T: typeid) -> bool ---
|
||||
type_is_sliceable :: proc($T: typeid) -> bool ---
|
||||
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp
|
||||
type_is_comparable :: proc($T: typeid) -> bool ---
|
||||
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
|
||||
type_is_dereferenceable :: proc($T: typeid) -> bool ---
|
||||
type_is_valid_map_key :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_named :: proc($T: typeid) -> bool ---
|
||||
type_is_pointer :: proc($T: typeid) -> bool ---
|
||||
type_is_opaque :: proc($T: typeid) -> bool ---
|
||||
type_is_array :: proc($T: typeid) -> bool ---
|
||||
type_is_enumerated_array :: proc($T: typeid) -> bool ---
|
||||
type_is_slice :: proc($T: typeid) -> bool ---
|
||||
@@ -119,14 +161,27 @@ type_is_struct :: proc($T: typeid) -> bool ---
|
||||
type_is_union :: proc($T: typeid) -> bool ---
|
||||
type_is_enum :: proc($T: typeid) -> bool ---
|
||||
type_is_proc :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_field :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_field_value :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_set :: proc($T: typeid) -> bool ---
|
||||
type_is_simd_vector :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
||||
|
||||
type_has_nil :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
||||
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
|
||||
|
||||
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
||||
|
||||
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
||||
type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
||||
|
||||
type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
||||
type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
|
||||
|
||||
type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid ---
|
||||
type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V ---
|
||||
|
||||
|
||||
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
||||
|
||||
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
|
||||
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
package io
|
||||
|
||||
to_reader :: proc(s: Stream) -> (r: Reader, ok: bool = true) {
|
||||
r.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_read == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_writer :: proc(s: Stream) -> (w: Writer, ok: bool = true) {
|
||||
w.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_write == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
to_closer :: proc(s: Stream) -> (c: Closer, ok: bool = true) {
|
||||
c.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_close == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_flusher :: proc(s: Stream) -> (f: Flusher, ok: bool = true) {
|
||||
f.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_flush == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_seeker :: proc(s: Stream) -> (seeker: Seeker, ok: bool = true) {
|
||||
seeker.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_seek == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
to_read_writer :: proc(s: Stream) -> (r: Read_Writer, ok: bool = true) {
|
||||
r.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_read_closer :: proc(s: Stream) -> (r: Read_Closer, ok: bool = true) {
|
||||
r.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_read == nil || s.impl_close == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, ok: bool = true) {
|
||||
r.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, ok: bool = true) {
|
||||
r.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, ok: bool = true) {
|
||||
w.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, ok: bool = true) {
|
||||
w.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
to_reader_at :: proc(s: Stream) -> (r: Reader_At, ok: bool = true) {
|
||||
r.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_read_at == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_writer_at :: proc(s: Stream) -> (w: Writer_At, ok: bool = true) {
|
||||
w.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_write_at == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_reader_from :: proc(s: Stream) -> (r: Reader_From, ok: bool = true) {
|
||||
r.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_read_from == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_writer_to :: proc(s: Stream) -> (w: Writer_To, ok: bool = true) {
|
||||
w.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_write_to == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_write_closer :: proc(s: Stream) -> (w: Write_Closer, ok: bool = true) {
|
||||
w.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, ok: bool = true) {
|
||||
w.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
to_byte_reader :: proc(s: Stream) -> (b: Byte_Reader, ok: bool = true) {
|
||||
b.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_read_byte == nil {
|
||||
ok = false;
|
||||
if s.stream_vtable != nil && s.impl_read != nil {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_byte_scanner :: proc(s: Stream) -> (b: Byte_Scanner, ok: bool = true) {
|
||||
b.stream = s;
|
||||
if s.stream_vtable != nil {
|
||||
if s.impl_unread_byte == nil {
|
||||
ok = false;
|
||||
return;
|
||||
}
|
||||
if s.impl_read_byte != nil {
|
||||
ok = true;
|
||||
} else if s.impl_read != nil {
|
||||
ok = true;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
to_byte_writer :: proc(s: Stream) -> (b: Byte_Writer, ok: bool = true) {
|
||||
b.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_write_byte == nil {
|
||||
ok = false;
|
||||
if s.stream_vtable != nil && s.impl_write != nil {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
to_rune_reader :: proc(s: Stream) -> (r: Rune_Reader, ok: bool = true) {
|
||||
r.stream = s;
|
||||
if s.stream_vtable == nil || s.impl_read_rune == nil {
|
||||
ok = false;
|
||||
if s.stream_vtable != nil && s.impl_read != nil {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
to_rune_scanner :: proc(s: Stream) -> (r: Rune_Scanner, ok: bool = true) {
|
||||
r.stream = s;
|
||||
if s.stream_vtable != nil {
|
||||
if s.impl_unread_rune == nil {
|
||||
ok = false;
|
||||
return;
|
||||
}
|
||||
if s.impl_read_rune != nil {
|
||||
ok = true;
|
||||
} else if s.impl_read != nil {
|
||||
ok = true;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
+514
@@ -0,0 +1,514 @@
|
||||
package io
|
||||
|
||||
import "intrinsics"
|
||||
import "core:runtime"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
Seek_From :: enum {
|
||||
Start = 0, // seek relative to the origin of the file
|
||||
Current = 1, // seek relative to the current offset
|
||||
End = 2, // seek relative to the end
|
||||
}
|
||||
|
||||
Error :: enum i32 {
|
||||
// No Error
|
||||
None = 0,
|
||||
|
||||
// EOF is the error returned by `read` when no more input is available
|
||||
EOF,
|
||||
|
||||
// Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data
|
||||
Unexpected_EOF,
|
||||
|
||||
// Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error
|
||||
Short_Write,
|
||||
|
||||
// Invalid_Write means that a write returned an impossible count
|
||||
Invalid_Write,
|
||||
|
||||
// Short_Buffer means that a read required a longer buffer than was provided
|
||||
Short_Buffer,
|
||||
|
||||
// No_Progress is returned by some implementations of `io.Reader` when many calls
|
||||
// to `read` have failed to return any data or error.
|
||||
// This is usually a signed of a broken `io.Reader` implementation
|
||||
No_Progress,
|
||||
|
||||
Invalid_Whence,
|
||||
Invalid_Offset,
|
||||
Invalid_Unread,
|
||||
|
||||
Negative_Read,
|
||||
Negative_Write,
|
||||
Negative_Count,
|
||||
Buffer_Full,
|
||||
|
||||
// Unknown means that an error has occurred but cannot be categorized
|
||||
Unknown,
|
||||
|
||||
// Empty is returned when a procedure has not been implemented for an io.Stream
|
||||
Empty = -1,
|
||||
}
|
||||
|
||||
Close_Proc :: proc(using s: Stream) -> Error;
|
||||
Flush_Proc :: proc(using s: Stream) -> Error;
|
||||
Seek_Proc :: proc(using s: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error);
|
||||
Size_Proc :: proc(using s: Stream) -> i64;
|
||||
Read_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
|
||||
Read_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
|
||||
Read_From_Proc :: proc(using s: Stream, r: Reader) -> (n: i64, err: Error);
|
||||
Read_Byte_Proc :: proc(using s: Stream) -> (byte, Error);
|
||||
Read_Rune_Proc :: proc(using s: Stream) -> (ch: rune, size: int, err: Error);
|
||||
Unread_Byte_Proc :: proc(using s: Stream) -> Error;
|
||||
Unread_Rune_Proc :: proc(using s: Stream) -> Error;
|
||||
Write_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error);
|
||||
Write_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error);
|
||||
Write_To_Proc :: proc(using s: Stream, w: Writer) -> (n: i64, err: Error);
|
||||
Write_Byte_Proc :: proc(using s: Stream, c: byte) -> Error;
|
||||
Write_Rune_Proc :: proc(using s: Stream, r: rune) -> (size: int, err: Error);
|
||||
Destroy_Proc :: proc(using s: Stream) -> Error;
|
||||
|
||||
|
||||
Stream :: struct {
|
||||
using stream_vtable: ^Stream_VTable,
|
||||
stream_data: rawptr,
|
||||
}
|
||||
Stream_VTable :: struct {
|
||||
impl_close: Close_Proc,
|
||||
impl_flush: Flush_Proc,
|
||||
|
||||
impl_seek: Seek_Proc,
|
||||
impl_size: Size_Proc,
|
||||
|
||||
impl_read: Read_Proc,
|
||||
impl_read_at: Read_At_Proc,
|
||||
impl_read_byte: Read_Byte_Proc,
|
||||
impl_read_rune: Read_Rune_Proc,
|
||||
impl_write_to: Write_To_Proc,
|
||||
|
||||
impl_write: Write_Proc,
|
||||
impl_write_at: Write_At_Proc,
|
||||
impl_write_byte: Write_Byte_Proc,
|
||||
impl_write_rune: Write_Rune_Proc,
|
||||
impl_read_from: Read_From_Proc,
|
||||
|
||||
impl_unread_byte: Unread_Byte_Proc,
|
||||
impl_unread_rune: Unread_Rune_Proc,
|
||||
|
||||
impl_destroy: Destroy_Proc,
|
||||
}
|
||||
|
||||
|
||||
Reader :: struct {using stream: Stream};
|
||||
Writer :: struct {using stream: Stream};
|
||||
Closer :: struct {using stream: Stream};
|
||||
Flusher :: struct {using stream: Stream};
|
||||
Seeker :: struct {using stream: Stream};
|
||||
|
||||
Read_Writer :: struct {using stream: Stream};
|
||||
Read_Closer :: struct {using stream: Stream};
|
||||
Read_Write_Closer :: struct {using stream: Stream};
|
||||
Read_Write_Seeker :: struct {using stream: Stream};
|
||||
|
||||
Write_Closer :: struct {using stream: Stream};
|
||||
Write_Seeker :: struct {using stream: Stream};
|
||||
Write_Flusher :: struct {using stream: Stream};
|
||||
Write_Flush_Closer :: struct {using stream: Stream};
|
||||
|
||||
Reader_At :: struct {using stream: Stream};
|
||||
Writer_At :: struct {using stream: Stream};
|
||||
Reader_From :: struct {using stream: Stream};
|
||||
Writer_To :: struct {using stream: Stream};
|
||||
|
||||
Byte_Reader :: struct {using stream: Stream};
|
||||
Byte_Scanner :: struct {using stream: Stream};
|
||||
Byte_Writer :: struct {using stream: Stream};
|
||||
|
||||
Rune_Reader :: struct {using stream: Stream};
|
||||
Rune_Scanner :: struct {using stream: Stream};
|
||||
|
||||
|
||||
destroy :: proc(s: Stream) -> Error {
|
||||
close_err := close({s});
|
||||
if s.stream_vtable != nil && s.impl_destroy != nil {
|
||||
return s->impl_destroy();
|
||||
}
|
||||
if close_err != .None {
|
||||
return close_err;
|
||||
}
|
||||
return .Empty;
|
||||
}
|
||||
|
||||
read :: proc(s: Reader, p: []byte) -> (n: int, err: Error) {
|
||||
if s.stream_vtable != nil && s.impl_read != nil {
|
||||
return s->impl_read(p);
|
||||
}
|
||||
return 0, .Empty;
|
||||
}
|
||||
|
||||
write :: proc(s: Writer, p: []byte) -> (n: int, err: Error) {
|
||||
if s.stream_vtable != nil && s.impl_write != nil {
|
||||
return s->impl_write(p);
|
||||
}
|
||||
return 0, .Empty;
|
||||
}
|
||||
|
||||
seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
|
||||
if s.stream_vtable != nil && s.impl_seek != nil {
|
||||
return s->impl_seek(offset, whence);
|
||||
}
|
||||
return 0, .Empty;
|
||||
}
|
||||
|
||||
close :: proc(s: Closer) -> Error {
|
||||
if s.stream_vtable != nil && s.impl_close != nil {
|
||||
return s->impl_close();
|
||||
}
|
||||
// Instead of .Empty, .None is fine in this case
|
||||
return .None;
|
||||
}
|
||||
|
||||
flush :: proc(s: Flusher) -> Error {
|
||||
if s.stream_vtable != nil && s.impl_flush != nil {
|
||||
return s->impl_flush();
|
||||
}
|
||||
// Instead of .Empty, .None is fine in this case
|
||||
return .None;
|
||||
}
|
||||
|
||||
size :: proc(s: Stream) -> i64 {
|
||||
if s.stream_vtable == nil {
|
||||
return 0;
|
||||
}
|
||||
if s.impl_size != nil {
|
||||
return s->impl_size();
|
||||
}
|
||||
if s.impl_seek == nil {
|
||||
return 0;
|
||||
}
|
||||
|
||||
curr, end: i64;
|
||||
err: Error;
|
||||
if curr, err = s->impl_seek(0, .Current); err != nil {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if end, err = s->impl_seek(0, .End); err != nil {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if _, err = s->impl_seek(curr, .Start); err != nil {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
read_at :: proc(r: Reader_At, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if r.stream_vtable == nil {
|
||||
return 0, .Empty;
|
||||
}
|
||||
if r.impl_read_at != nil {
|
||||
return r->impl_read_at(p, offset);
|
||||
}
|
||||
if r.impl_seek == nil || r.impl_read == nil {
|
||||
return 0, .Empty;
|
||||
}
|
||||
|
||||
curr_offset: i64;
|
||||
curr_offset, err = r->impl_seek(offset, .Current);
|
||||
if err != nil {
|
||||
return 0, err;
|
||||
}
|
||||
|
||||
n, err = r->impl_read(p);
|
||||
_, err1 := r->impl_seek(curr_offset, .Start);
|
||||
if err1 != nil && err == nil {
|
||||
err = err1;
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
write_at :: proc(w: Writer_At, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if w.stream_vtable == nil {
|
||||
return 0, .Empty;
|
||||
}
|
||||
if w.impl_write_at != nil {
|
||||
return w->impl_write_at(p, offset);
|
||||
}
|
||||
if w.impl_seek == nil || w.impl_write == nil {
|
||||
return 0, .Empty;
|
||||
}
|
||||
|
||||
curr_offset: i64;
|
||||
curr_offset, err = w->impl_seek(offset, .Current);
|
||||
if err != nil {
|
||||
return 0, err;
|
||||
}
|
||||
|
||||
n, err = w->impl_write(p);
|
||||
_, err1 := w->impl_seek(curr_offset, .Start);
|
||||
if err1 != nil && err == nil {
|
||||
err = err1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
write_to :: proc(r: Writer_To, w: Writer) -> (n: i64, err: Error) {
|
||||
if r.stream_vtable == nil || w.stream_vtable == nil {
|
||||
return 0, .Empty;
|
||||
}
|
||||
if r.impl_write_to != nil {
|
||||
return r->impl_write_to(w);
|
||||
}
|
||||
return 0, .Empty;
|
||||
}
|
||||
read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
|
||||
if r.stream_vtable == nil || w.stream_vtable == nil {
|
||||
return 0, .Empty;
|
||||
}
|
||||
if r.impl_read_from != nil {
|
||||
return w->impl_read_from(r);
|
||||
}
|
||||
return 0, .Empty;
|
||||
}
|
||||
|
||||
|
||||
read_byte :: proc(r: Byte_Reader) -> (byte, Error) {
|
||||
if r.stream_vtable == nil {
|
||||
return 0, .Empty;
|
||||
}
|
||||
if r.impl_read_byte != nil {
|
||||
return r->impl_read_byte();
|
||||
}
|
||||
if r.impl_read == nil {
|
||||
return 0, .Empty;
|
||||
}
|
||||
|
||||
b: [1]byte;
|
||||
_, err := r->impl_read(b[:]);
|
||||
return b[0], err;
|
||||
}
|
||||
|
||||
write_byte :: proc{
|
||||
write_byte_to_byte_writer,
|
||||
write_byte_to_writer,
|
||||
};
|
||||
|
||||
write_byte_to_byte_writer :: proc(w: Byte_Writer, c: byte) -> Error {
|
||||
return _write_byte(w, c);
|
||||
}
|
||||
|
||||
write_byte_to_writer :: proc(w: Writer, c: byte) -> Error {
|
||||
return _write_byte(auto_cast w, c);
|
||||
}
|
||||
|
||||
@(private)
|
||||
_write_byte :: proc(w: Byte_Writer, c: byte) -> Error {
|
||||
if w.stream_vtable == nil {
|
||||
return .Empty;
|
||||
}
|
||||
if w.impl_write_byte != nil {
|
||||
return w->impl_write_byte(c);
|
||||
}
|
||||
if w.impl_write == nil {
|
||||
return .Empty;
|
||||
}
|
||||
|
||||
b := [1]byte{c};
|
||||
_, err := w->impl_write(b[:]);
|
||||
return err;
|
||||
}
|
||||
|
||||
read_rune :: proc(br: Rune_Reader) -> (ch: rune, size: int, err: Error) {
|
||||
if br.stream_vtable == nil {
|
||||
return 0, 0, .Empty;
|
||||
}
|
||||
if br.impl_read_rune != nil {
|
||||
return br->impl_read_rune();
|
||||
}
|
||||
if br.impl_read == nil {
|
||||
return 0, 0, .Empty;
|
||||
}
|
||||
|
||||
b: [utf8.UTF_MAX]byte;
|
||||
_, err = br->impl_read(b[:1]);
|
||||
|
||||
s0 := b[0];
|
||||
ch = rune(s0);
|
||||
size = 1;
|
||||
if err != nil {
|
||||
return;
|
||||
}
|
||||
if ch < utf8.RUNE_SELF {
|
||||
return;
|
||||
}
|
||||
x := utf8.accept_sizes[s0];
|
||||
if x >= 0xf0 {
|
||||
mask := rune(x) << 31 >> 31;
|
||||
ch = ch &~ mask | utf8.RUNE_ERROR&mask;
|
||||
return;
|
||||
}
|
||||
sz := int(x&7);
|
||||
n: int;
|
||||
n, err = br->impl_read(b[1:sz]);
|
||||
if err != nil || n+1 < sz {
|
||||
ch = utf8.RUNE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
ch, size = utf8.decode_rune(b[:sz]);
|
||||
return;
|
||||
}
|
||||
|
||||
unread_byte :: proc(s: Byte_Scanner) -> Error {
|
||||
if s.stream_vtable != nil && s.impl_unread_byte != nil {
|
||||
return s->impl_unread_byte();
|
||||
}
|
||||
return .Empty;
|
||||
}
|
||||
unread_rune :: proc(s: Rune_Scanner) -> Error {
|
||||
if s.stream_vtable != nil && s.impl_unread_rune != nil {
|
||||
return s->impl_unread_rune();
|
||||
}
|
||||
return .Empty;
|
||||
}
|
||||
|
||||
|
||||
write_string :: proc(s: Writer, str: string) -> (n: int, err: Error) {
|
||||
return write(s, transmute([]byte)str);
|
||||
}
|
||||
|
||||
write_rune :: proc(s: Writer, r: rune) -> (size: int, err: Error) {
|
||||
if s.stream_vtable != nil && s.impl_write_rune != nil {
|
||||
return s->impl_write_rune(r);
|
||||
}
|
||||
|
||||
if r < utf8.RUNE_SELF {
|
||||
err = write_byte(s, byte(r));
|
||||
if err == nil {
|
||||
size = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
buf, w := utf8.encode_rune(r);
|
||||
return write(s, buf[:w]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) {
|
||||
return read_at_least(r, buf, len(buf));
|
||||
}
|
||||
|
||||
|
||||
read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) {
|
||||
if len(buf) < min {
|
||||
return 0, .Short_Buffer;
|
||||
}
|
||||
for n < min && err == nil {
|
||||
nn: int;
|
||||
nn, err = read(r, buf[n:]);
|
||||
n += n;
|
||||
}
|
||||
|
||||
if n >= min {
|
||||
err = nil;
|
||||
} else if n > 0 && err == .EOF {
|
||||
err = .Unexpected_EOF;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// copy copies from src to dst till either EOF is reached on src or an error occurs
|
||||
// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
|
||||
copy :: proc(dst: Writer, src: Reader) -> (written: i64, err: Error) {
|
||||
return _copy_buffer(dst, src, nil);
|
||||
}
|
||||
|
||||
// copy_buffer is the same as copy except that it stages through the provided buffer (if one is required)
|
||||
// rather than allocating a temporary one on the stack through `intrinsics.alloca`
|
||||
// If buf is `nil`, it is allocate through `intrinsics.alloca`; otherwise if it has zero length, it will panic
|
||||
copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
|
||||
if buf != nil && len(buf) == 0 {
|
||||
panic("empty buffer in io.copy_buffer");
|
||||
}
|
||||
return _copy_buffer(dst, src, buf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// copy_n copies n bytes (or till an error) from src to dst.
|
||||
// It returns the number of bytes copied and the first error that occurred whilst copying, if any.
|
||||
// On return, written == n IFF err == nil
|
||||
copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) {
|
||||
nsrc := limited_reader_init(&Limited_Reader{}, src, n);
|
||||
written, err = copy(dst, nsrc);
|
||||
if written == n {
|
||||
return n, nil;
|
||||
}
|
||||
if written < n && err == nil {
|
||||
// src stopped early and must have been an EOF
|
||||
err = .EOF;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
|
||||
if dst.stream_vtable == nil || src.stream_vtable == nil {
|
||||
return 0, .Empty;
|
||||
}
|
||||
if src.impl_write_to != nil {
|
||||
return src->impl_write_to(dst);
|
||||
}
|
||||
if src.impl_read_from != nil {
|
||||
return dst->impl_read_from(src);
|
||||
}
|
||||
buf := buf;
|
||||
if buf == nil {
|
||||
DEFAULT_SIZE :: 4 * 1024;
|
||||
size := DEFAULT_SIZE;
|
||||
if src.stream_vtable == _limited_reader_vtable {
|
||||
l := (^Limited_Reader)(src.stream_data);
|
||||
if i64(size) > l.n {
|
||||
if l.n < 1 {
|
||||
size = 1;
|
||||
} else {
|
||||
size = int(l.n);
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE(bill): alloca is fine here
|
||||
buf = transmute([]byte)runtime.Raw_Slice{intrinsics.alloca(size, 2*align_of(rawptr)), size};
|
||||
}
|
||||
for {
|
||||
nr, er := read(src, buf);
|
||||
if nr > 0 {
|
||||
nw, ew := write(dst, buf[0:nr]);
|
||||
if nw > 0 {
|
||||
written += i64(nw);
|
||||
}
|
||||
if ew != nil {
|
||||
err = ew;
|
||||
break;
|
||||
}
|
||||
if nr != nw {
|
||||
err = .Short_Write;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if er != nil {
|
||||
if er != .EOF {
|
||||
err = er;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package io
|
||||
|
||||
Multi_Reader :: struct {
|
||||
readers: [dynamic]Reader,
|
||||
}
|
||||
|
||||
@(private)
|
||||
_multi_reader_vtable := &Stream_VTable{
|
||||
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
|
||||
mr := (^Multi_Reader)(s.stream_data);
|
||||
for len(mr.readers) > 0 {
|
||||
r := mr.readers[0];
|
||||
n, err = read(r, p);
|
||||
if err == .EOF {
|
||||
ordered_remove(&mr.readers, 0);
|
||||
}
|
||||
if n > 0 || err != .EOF {
|
||||
if err == .EOF && len(mr.readers) > 0 {
|
||||
// Don't return EOF yet, more readers remain
|
||||
err = nil;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
return 0, .EOF;
|
||||
},
|
||||
};
|
||||
|
||||
multi_reader_init :: proc(mr: ^Multi_Reader, readers: ..Reader, allocator := context.allocator) -> (r: Reader) {
|
||||
all_readers := make([dynamic]Reader, 0, len(readers), allocator);
|
||||
|
||||
for w in readers {
|
||||
if w.stream_vtable == _multi_reader_vtable {
|
||||
other := (^Multi_Reader)(w.stream_data);
|
||||
append(&all_readers, ..other.readers[:]);
|
||||
} else {
|
||||
append(&all_readers, w);
|
||||
}
|
||||
}
|
||||
|
||||
mr.readers = all_readers;
|
||||
|
||||
r.stream_vtable = _multi_reader_vtable;
|
||||
r.stream_data = mr;
|
||||
return;
|
||||
}
|
||||
|
||||
multi_reader_destroy :: proc(mr: ^Multi_Reader) {
|
||||
delete(mr.readers);
|
||||
}
|
||||
|
||||
|
||||
Multi_Writer :: struct {
|
||||
writers: [dynamic]Writer,
|
||||
}
|
||||
|
||||
@(private)
|
||||
_multi_writer_vtable := &Stream_VTable{
|
||||
impl_write = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
|
||||
mw := (^Multi_Writer)(s.stream_data);
|
||||
for w in mw.writers {
|
||||
n, err = write(w, p);
|
||||
if err != nil {
|
||||
return;
|
||||
}
|
||||
if n != len(p) {
|
||||
err = .Short_Write;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return len(p), nil;
|
||||
},
|
||||
};
|
||||
|
||||
multi_writer_init :: proc(mw: ^Multi_Writer, writers: ..Writer, allocator := context.allocator) -> (out: Writer) {
|
||||
mw.writers = make([dynamic]Writer, 0, len(writers), allocator);
|
||||
|
||||
for w in writers {
|
||||
if w.stream_vtable == _multi_writer_vtable {
|
||||
other := (^Multi_Writer)(w.stream_data);
|
||||
append(&mw.writers, ..other.writers[:]);
|
||||
} else {
|
||||
append(&mw.writers, w);
|
||||
}
|
||||
}
|
||||
|
||||
out.stream_vtable = _multi_writer_vtable;
|
||||
out.stream_data = mw;
|
||||
return;
|
||||
}
|
||||
|
||||
multi_writer_destroy :: proc(mw: ^Multi_Writer) {
|
||||
delete(mw.writers);
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package io
|
||||
|
||||
import "core:strconv"
|
||||
|
||||
write_u64 :: proc(w: Writer, i: u64, base: int = 10) -> (n: int, err: Error) {
|
||||
buf: [32]byte;
|
||||
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil);
|
||||
return write_string(w, s);
|
||||
}
|
||||
write_i64 :: proc(w: Writer, i: i64, base: int = 10) -> (n: int, err: Error) {
|
||||
buf: [32]byte;
|
||||
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil);
|
||||
return write_string(w, s);
|
||||
}
|
||||
|
||||
write_uint :: proc(w: Writer, i: uint, base: int = 10) -> (n: int, err: Error) {
|
||||
return write_u64(w, u64(i), base);
|
||||
}
|
||||
write_int :: proc(w: Writer, i: int, base: int = 10) -> (n: int, err: Error) {
|
||||
return write_i64(w, i64(i), base);
|
||||
}
|
||||
|
||||
Tee_Reader :: struct {
|
||||
r: Reader,
|
||||
w: Writer,
|
||||
}
|
||||
|
||||
@(private)
|
||||
_tee_reader_vtable := &Stream_VTable{
|
||||
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
|
||||
t := (^Tee_Reader)(s.stream_data);
|
||||
n, err = read(t.r, p);
|
||||
if n > 0 {
|
||||
if wn, werr := write(t.w, p[:n]); werr != nil {
|
||||
return wn, werr;
|
||||
}
|
||||
}
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
// tee_reader_init returns a Reader that writes to 'w' what it reads from 'r'
|
||||
// All reads from 'r' performed through it are matched with a corresponding write to 'w'
|
||||
// There is no internal buffering done
|
||||
// The write must complete before th read completes
|
||||
// Any error encountered whilst writing is reported as a 'read' error
|
||||
// tee_reader_init must call io.destroy when done with
|
||||
tee_reader_init :: proc(t: ^Tee_Reader, r: Reader, w: Writer, allocator := context.allocator) -> Reader {
|
||||
t.r, t.w = r, w;
|
||||
return tee_reader_to_reader(t);
|
||||
}
|
||||
|
||||
tee_reader_to_reader :: proc(t: ^Tee_Reader) -> (r: Reader) {
|
||||
r.stream_data = t;
|
||||
r.stream_vtable = _tee_reader_vtable;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// A Limited_Reader reads from r but limits the amount of data returned to just n bytes.
|
||||
// Each call to read updates n to reflect the new amount remaining.
|
||||
// read returns EOF when n <= 0 or when the underlying r returns EOF.
|
||||
Limited_Reader :: struct {
|
||||
r: Reader, // underlying reader
|
||||
n: i64, // max_bytes
|
||||
}
|
||||
|
||||
@(private)
|
||||
_limited_reader_vtable := &Stream_VTable{
|
||||
impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
|
||||
l := (^Limited_Reader)(s.stream_data);
|
||||
if l.n <= 0 {
|
||||
return 0, .EOF;
|
||||
}
|
||||
p := p;
|
||||
if i64(len(p)) > l.n {
|
||||
p = p[0:l.n];
|
||||
}
|
||||
n, err = read(l.r, p);
|
||||
l.n -= i64(n);
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
limited_reader_init :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader {
|
||||
l.r = r;
|
||||
l.n = n;
|
||||
return limited_reader_to_reader(l);
|
||||
}
|
||||
|
||||
limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) {
|
||||
r.stream_vtable = _limited_reader_vtable;
|
||||
r.stream_data = l;
|
||||
return;
|
||||
}
|
||||
|
||||
// Section_Reader implements read, seek, and read_at on a section of an underlying Reader_At
|
||||
Section_Reader :: struct {
|
||||
r: Reader_At,
|
||||
base: i64,
|
||||
off: i64,
|
||||
limit: i64,
|
||||
}
|
||||
|
||||
section_reader_init :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) {
|
||||
s.r = r;
|
||||
s.off = off;
|
||||
s.limit = off + n;
|
||||
return;
|
||||
}
|
||||
section_reader_to_stream :: proc(s: ^Section_Reader) -> (out: Stream) {
|
||||
out.stream_data = s;
|
||||
out.stream_vtable = _section_reader_vtable;
|
||||
return;
|
||||
}
|
||||
|
||||
@(private)
|
||||
_section_reader_vtable := &Stream_VTable{
|
||||
impl_read = proc(stream: Stream, p: []byte) -> (n: int, err: Error) {
|
||||
s := (^Section_Reader)(stream.stream_data);
|
||||
if s.off >= s.limit {
|
||||
return 0, .EOF;
|
||||
}
|
||||
p := p;
|
||||
if max := s.limit - s.off; i64(len(p)) > max {
|
||||
p = p[0:max];
|
||||
}
|
||||
n, err = read_at(s.r, p, s.off);
|
||||
s.off += i64(n);
|
||||
return;
|
||||
},
|
||||
impl_read_at = proc(stream: Stream, p: []byte, off: i64) -> (n: int, err: Error) {
|
||||
s := (^Section_Reader)(stream.stream_data);
|
||||
p, off := p, off;
|
||||
|
||||
if off < 0 || off >= s.limit - s.base {
|
||||
return 0, .EOF;
|
||||
}
|
||||
off += s.base;
|
||||
if max := s.limit - off; i64(len(p)) > max {
|
||||
p = p[0:max];
|
||||
n, err = read_at(s.r, p, off);
|
||||
if err == nil {
|
||||
err = .EOF;
|
||||
}
|
||||
return;
|
||||
}
|
||||
return read_at(s.r, p, off);
|
||||
},
|
||||
impl_seek = proc(stream: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
|
||||
s := (^Section_Reader)(stream.stream_data);
|
||||
|
||||
offset := offset;
|
||||
switch whence {
|
||||
case:
|
||||
return 0, .Invalid_Whence;
|
||||
case .Start:
|
||||
offset += s.base;
|
||||
case .Current:
|
||||
offset += s.off;
|
||||
case .End:
|
||||
offset += s.limit;
|
||||
}
|
||||
if offset < s.base {
|
||||
return 0, .Invalid_Offset;
|
||||
}
|
||||
s.off = offset;
|
||||
n = offset - s.base;
|
||||
return;
|
||||
},
|
||||
impl_size = proc(stream: Stream) -> i64 {
|
||||
s := (^Section_Reader)(stream.stream_data);
|
||||
return s.limit - s.base;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -5,138 +5,158 @@ import "core:strings";
|
||||
import "core:os";
|
||||
import "core:time";
|
||||
|
||||
Level_Headers := []string{
|
||||
"[DEBUG] --- ",
|
||||
"[INFO ] --- ",
|
||||
"[WARN ] --- ",
|
||||
"[ERROR] --- ",
|
||||
"[FATAL] --- ",
|
||||
Level_Headers := [?]string{
|
||||
0..<10 = "[DEBUG] --- ",
|
||||
10..<20 = "[INFO ] --- ",
|
||||
20..<30 = "[WARN ] --- ",
|
||||
30..<40 = "[ERROR] --- ",
|
||||
40..<50 = "[FATAL] --- ",
|
||||
};
|
||||
|
||||
Default_Console_Logger_Opts :: Options{
|
||||
.Level,
|
||||
.Terminal_Color,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
.Level,
|
||||
.Terminal_Color,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
} | Full_Timestamp_Opts;
|
||||
|
||||
Default_File_Logger_Opts :: Options{
|
||||
.Level,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
.Level,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
} | Full_Timestamp_Opts;
|
||||
|
||||
|
||||
File_Console_Logger_Data :: struct {
|
||||
lowest_level: Level,
|
||||
file_handle: os.Handle,
|
||||
ident : string,
|
||||
file_handle: os.Handle,
|
||||
ident: string,
|
||||
}
|
||||
|
||||
create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "") -> Logger {
|
||||
data := new(File_Console_Logger_Data);
|
||||
data.lowest_level = lowest;
|
||||
data.file_handle = h;
|
||||
data.ident = ident;
|
||||
return Logger{file_console_logger_proc, data, opt};
|
||||
data := new(File_Console_Logger_Data);
|
||||
data.file_handle = h;
|
||||
data.ident = ident;
|
||||
return Logger{file_console_logger_proc, data, lowest, opt};
|
||||
}
|
||||
|
||||
destroy_file_logger ::proc(log : ^Logger) {
|
||||
data := cast(^File_Console_Logger_Data)log.data;
|
||||
if data.file_handle != os.INVALID_HANDLE do os.close(data.file_handle);
|
||||
free(data);
|
||||
log^ = nil_logger();
|
||||
destroy_file_logger :: proc(log: ^Logger) {
|
||||
data := cast(^File_Console_Logger_Data)log.data;
|
||||
if data.file_handle != os.INVALID_HANDLE {
|
||||
os.close(data.file_handle);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "") -> Logger {
|
||||
data := new(File_Console_Logger_Data);
|
||||
data.lowest_level = lowest;
|
||||
data.file_handle = os.INVALID_HANDLE;
|
||||
data.ident = ident;
|
||||
return Logger{file_console_logger_proc, data, opt};
|
||||
data := new(File_Console_Logger_Data);
|
||||
data.file_handle = os.INVALID_HANDLE;
|
||||
data.ident = ident;
|
||||
return Logger{file_console_logger_proc, data, lowest, opt};
|
||||
}
|
||||
|
||||
destroy_console_logger ::proc(log : ^Logger) {
|
||||
free(log.data);
|
||||
log^ = nil_logger();
|
||||
destroy_console_logger :: proc(log: ^Logger) {
|
||||
free(log.data);
|
||||
}
|
||||
|
||||
file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||
data := cast(^File_Console_Logger_Data)logger_data;
|
||||
if level < data.lowest_level do return;
|
||||
data := cast(^File_Console_Logger_Data)logger_data;
|
||||
h: os.Handle = os.stdout if level <= Level.Error else os.stderr;
|
||||
if data.file_handle != os.INVALID_HANDLE {
|
||||
h = data.file_handle;
|
||||
}
|
||||
backing: [1024]byte; //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
|
||||
buf := strings.builder_from_slice(backing[:]);
|
||||
|
||||
h : os.Handle;
|
||||
if(data.file_handle != os.INVALID_HANDLE) do h = data.file_handle;
|
||||
else do h = level <= Level.Error ? context.stdout : context.stderr;
|
||||
backing: [1024]byte; //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
|
||||
buf := strings.builder_from_slice(backing[:]);
|
||||
do_level_header(options, level, &buf);
|
||||
|
||||
do_level_header(options, level, &buf);
|
||||
when time.IS_SUPPORTED {
|
||||
if Full_Timestamp_Opts & options != nil {
|
||||
fmt.sbprint(&buf, "[");
|
||||
t := time.now();
|
||||
y, m, d := time.date(t);
|
||||
h, min, s := time.clock(t);
|
||||
if .Date in options { fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d); }
|
||||
if .Time in options { fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s); }
|
||||
fmt.sbprint(&buf, "] ");
|
||||
}
|
||||
}
|
||||
|
||||
when time.IS_SUPPORTED {
|
||||
if Full_Timestamp_Opts & options != nil {
|
||||
fmt.sbprint(&buf, "[");
|
||||
t := time.now();
|
||||
y, m, d := time.date(t);
|
||||
h, min, s := time.clock(t);
|
||||
if Option.Date in options do fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d);
|
||||
if Option.Time in options do fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s);
|
||||
fmt.sbprint(&buf, "] ");
|
||||
}
|
||||
}
|
||||
do_location_header(options, &buf, location);
|
||||
|
||||
do_location_header(options, &buf, location);
|
||||
if .Thread_Id in options {
|
||||
// NOTE(Oskar): not using context.thread_id here since that could be
|
||||
// incorrect when replacing context for a thread.
|
||||
fmt.sbprintf(&buf, "[{}] ", os.current_thread_id());
|
||||
}
|
||||
|
||||
if data.ident != "" do fmt.sbprintf(&buf, "[%s] ", data.ident);
|
||||
//TODO(Hoej): When we have better atomics and such, make this thread-safe
|
||||
fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text);
|
||||
if data.ident != "" {
|
||||
fmt.sbprintf(&buf, "[%s] ", data.ident);
|
||||
}
|
||||
//TODO(Hoej): When we have better atomics and such, make this thread-safe
|
||||
fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text);
|
||||
}
|
||||
|
||||
do_level_header :: proc(opts : Options, level : Level, str : ^strings.Builder) {
|
||||
do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
|
||||
|
||||
RESET :: "\x1b[0m";
|
||||
RED :: "\x1b[31m";
|
||||
YELLOW :: "\x1b[33m";
|
||||
DARK_GREY :: "\x1b[90m";
|
||||
RESET :: "\x1b[0m";
|
||||
RED :: "\x1b[31m";
|
||||
YELLOW :: "\x1b[33m";
|
||||
DARK_GREY :: "\x1b[90m";
|
||||
|
||||
col := RESET;
|
||||
switch level {
|
||||
case Level.Debug : col = DARK_GREY;
|
||||
case Level.Info : col = RESET;
|
||||
case Level.Warning : col = YELLOW;
|
||||
case Level.Error, Level.Fatal : col = RED;
|
||||
}
|
||||
col := RESET;
|
||||
switch level {
|
||||
case .Debug: col = DARK_GREY;
|
||||
case .Info: col = RESET;
|
||||
case .Warning: col = YELLOW;
|
||||
case .Error, .Fatal: col = RED;
|
||||
}
|
||||
|
||||
if .Level in opts {
|
||||
if .Terminal_Color in opts do fmt.sbprint(str, col);
|
||||
fmt.sbprint(str, Level_Headers[level]);
|
||||
if .Terminal_Color in opts do fmt.sbprint(str, RESET);
|
||||
}
|
||||
if .Level in opts {
|
||||
if .Terminal_Color in opts {
|
||||
fmt.sbprint(str, col);
|
||||
}
|
||||
fmt.sbprint(str, Level_Headers[level]);
|
||||
if .Terminal_Color in opts {
|
||||
fmt.sbprint(str, RESET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_location_header :: proc(opts : Options, buf : ^strings.Builder, location := #caller_location) {
|
||||
if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return;
|
||||
do_location_header :: proc(opts: Options, buf: ^strings.Builder, location := #caller_location) {
|
||||
if Location_Header_Opts & opts == nil {
|
||||
return;
|
||||
}
|
||||
fmt.sbprint(buf, "[");
|
||||
|
||||
file := location.file_path;
|
||||
if .Short_File_Path in opts {
|
||||
last := 0;
|
||||
for r, i in location.file_path do if r == '/' do last = i+1;
|
||||
file = location.file_path[last:];
|
||||
}
|
||||
file := location.file_path;
|
||||
if .Short_File_Path in opts {
|
||||
last := 0;
|
||||
for r, i in location.file_path {
|
||||
if r == '/' {
|
||||
last = i+1;
|
||||
}
|
||||
}
|
||||
file = location.file_path[last:];
|
||||
}
|
||||
|
||||
if Location_File_Opts & opts != nil do fmt.sbprint(buf, file);
|
||||
if Location_File_Opts & opts != nil {
|
||||
fmt.sbprint(buf, file);
|
||||
}
|
||||
if .Line in opts {
|
||||
if Location_File_Opts & opts != nil {
|
||||
fmt.sbprint(buf, ":");
|
||||
}
|
||||
fmt.sbprint(buf, location.line);
|
||||
}
|
||||
|
||||
if .Procedure in opts {
|
||||
if Location_File_Opts & opts != nil do fmt.sbprint(buf, ".");
|
||||
fmt.sbprintf(buf, "%s()", location.procedure);
|
||||
}
|
||||
if .Procedure in opts {
|
||||
if (Location_File_Opts | {.Line}) & opts != nil {
|
||||
fmt.sbprint(buf, ":");
|
||||
}
|
||||
fmt.sbprintf(buf, "%s()", location.procedure);
|
||||
}
|
||||
|
||||
if .Line in opts {
|
||||
if Location_File_Opts & opts != nil || .Procedure in opts do fmt.sbprint(buf, ":");
|
||||
fmt.sbprint(buf, location.line);
|
||||
}
|
||||
|
||||
fmt.sbprint(buf, "] ");
|
||||
fmt.sbprint(buf, "] ");
|
||||
}
|
||||
|
||||
+76
-46
@@ -8,12 +8,12 @@ import "core:fmt"
|
||||
|
||||
Level :: runtime.Logger_Level;
|
||||
/*
|
||||
Level :: enum {
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
Fatal,
|
||||
Logger_Level :: enum {
|
||||
Debug = 0,
|
||||
Info = 10,
|
||||
Warning = 20,
|
||||
Error = 30,
|
||||
Fatal = 40,
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -38,7 +38,7 @@ Options :: bit_set[Option];
|
||||
|
||||
Full_Timestamp_Opts :: Options{
|
||||
.Date,
|
||||
.Time
|
||||
.Time,
|
||||
};
|
||||
Location_Header_Opts :: Options{
|
||||
.Short_File_Path,
|
||||
@@ -48,7 +48,7 @@ Location_Header_Opts :: Options{
|
||||
};
|
||||
Location_File_Opts :: Options{
|
||||
.Short_File_Path,
|
||||
.Long_File_Path
|
||||
.Long_File_Path,
|
||||
};
|
||||
|
||||
|
||||
@@ -60,56 +60,86 @@ Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Opt
|
||||
Logger :: runtime.Logger;
|
||||
/*
|
||||
Logger :: struct {
|
||||
procedure: Logger_Proc,
|
||||
data: rawptr,
|
||||
options: Options,
|
||||
procedure: Logger_Proc,
|
||||
data: rawptr,
|
||||
lowest_level: Level,
|
||||
options: Logger_Options,
|
||||
}
|
||||
*/
|
||||
|
||||
Multi_Logger_Data :: struct {
|
||||
loggers : []Logger,
|
||||
}
|
||||
|
||||
create_multi_logger :: proc(logs: ..Logger) -> Logger {
|
||||
data := new(Multi_Logger_Data);
|
||||
data.loggers = make([]Logger, len(logs));
|
||||
copy(data.loggers, logs);
|
||||
return Logger{multi_logger_proc, data, nil};
|
||||
}
|
||||
|
||||
destroy_multi_logger :: proc(log : ^Logger) {
|
||||
free(log.data);
|
||||
log^ = nil_logger();
|
||||
}
|
||||
|
||||
multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string,
|
||||
options: Options, location := #caller_location) {
|
||||
data := cast(^Multi_Logger_Data)logger_data;
|
||||
if data.loggers == nil || len(data.loggers) == 0 {
|
||||
return;
|
||||
}
|
||||
for log in data.loggers {
|
||||
log.procedure(log.data, level, text, log.options, location);
|
||||
}
|
||||
}
|
||||
|
||||
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
nil_logger :: proc() -> Logger {
|
||||
return Logger{nil_logger_proc, nil, nil};
|
||||
return Logger{nil_logger_proc, nil, Level.Debug, nil};
|
||||
}
|
||||
|
||||
// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
|
||||
debug :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Debug, fmt_str=fmt_str, args=args, location=location);
|
||||
info :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Info, fmt_str=fmt_str, args=args, location=location);
|
||||
warn :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Warning, fmt_str=fmt_str, args=args, location=location);
|
||||
error :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Error, fmt_str=fmt_str, args=args, location=location);
|
||||
fatal :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Fatal, fmt_str=fmt_str, args=args, location=location);
|
||||
debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Debug, fmt_str=fmt_str, args=args, location=location);
|
||||
}
|
||||
infof :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Info, fmt_str=fmt_str, args=args, location=location);
|
||||
}
|
||||
warnf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Warning, fmt_str=fmt_str, args=args, location=location);
|
||||
}
|
||||
errorf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Error, fmt_str=fmt_str, args=args, location=location);
|
||||
}
|
||||
fatalf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location);
|
||||
}
|
||||
|
||||
logf :: proc(level : Level, fmt_str : string, args : ..any, location := #caller_location) {
|
||||
debug :: proc(args: ..any, sep := " ", location := #caller_location) {
|
||||
log(level=.Debug, args=args, sep=sep, location=location);
|
||||
}
|
||||
info :: proc(args: ..any, sep := " ", location := #caller_location) {
|
||||
log(level=.Info, args=args, sep=sep, location=location);
|
||||
}
|
||||
warn :: proc(args: ..any, sep := " ", location := #caller_location) {
|
||||
log(level=.Warning, args=args, sep=sep, location=location);
|
||||
}
|
||||
error :: proc(args: ..any, sep := " ", location := #caller_location) {
|
||||
log(level=.Error, args=args, sep=sep, location=location);
|
||||
}
|
||||
fatal :: proc(args: ..any, sep := " ", location := #caller_location) {
|
||||
log(level=.Fatal, args=args, sep=sep, location=location);
|
||||
}
|
||||
|
||||
panic :: proc(args: ..any, location := #caller_location) -> ! {
|
||||
log(level=.Fatal, args=args, location=location);
|
||||
runtime.panic("log.panic", location);
|
||||
}
|
||||
panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> ! {
|
||||
logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location);
|
||||
runtime.panic("log.panicf", location);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location) {
|
||||
logger := context.logger;
|
||||
str := len(args) > 0 ? fmt.tprintf(fmt_str, ..args) : fmt.tprint(fmt_str); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
|
||||
if logger.procedure == nil {
|
||||
return;
|
||||
}
|
||||
if level < logger.lowest_level {
|
||||
return;
|
||||
}
|
||||
str := fmt.tprint(args=args, sep=sep); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
|
||||
logger.procedure(logger.data, level, str, logger.options, location);
|
||||
}
|
||||
|
||||
logf :: proc(level: Level, fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logger := context.logger;
|
||||
if logger.procedure == nil {
|
||||
return;
|
||||
}
|
||||
if level < logger.lowest_level {
|
||||
return;
|
||||
}
|
||||
str := fmt.tprintf(fmt_str, ..args);
|
||||
logger.procedure(logger.data, level, str, logger.options, location);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package log
|
||||
|
||||
|
||||
Multi_Logger_Data :: struct {
|
||||
loggers: []Logger,
|
||||
}
|
||||
|
||||
create_multi_logger :: proc(logs: ..Logger) -> Logger {
|
||||
data := new(Multi_Logger_Data);
|
||||
data.loggers = make([]Logger, len(logs));
|
||||
copy(data.loggers, logs);
|
||||
return Logger{multi_logger_proc, data, Level.Debug, nil};
|
||||
}
|
||||
|
||||
destroy_multi_logger :: proc(log : ^Logger) {
|
||||
free(log.data);
|
||||
log^ = nil_logger();
|
||||
}
|
||||
|
||||
multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string,
|
||||
options: Options, location := #caller_location) {
|
||||
data := cast(^Multi_Logger_Data)logger_data;
|
||||
for log in data.loggers {
|
||||
if level < log.lowest_level {
|
||||
return;
|
||||
}
|
||||
log.procedure(log.data, level, text, log.options, location);
|
||||
}
|
||||
}
|
||||
+380
-185
@@ -1,6 +1,6 @@
|
||||
package math_bits
|
||||
|
||||
import "core:os"
|
||||
import "intrinsics"
|
||||
|
||||
U8_MIN :: 0;
|
||||
U16_MIN :: 0;
|
||||
@@ -22,223 +22,277 @@ I16_MAX :: 1 << 15 - 1;
|
||||
I32_MAX :: 1 << 31 - 1;
|
||||
I64_MAX :: 1 << 63 - 1;
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.ctpop.i8") count_ones8 :: proc(i: u8) -> u8 ---
|
||||
@(link_name="llvm.ctpop.i16") count_ones16 :: proc(i: u16) -> u16 ---
|
||||
@(link_name="llvm.ctpop.i32") count_ones32 :: proc(i: u32) -> u32 ---
|
||||
@(link_name="llvm.ctpop.i64") count_ones64 :: proc(i: u64) -> u64 ---
|
||||
|
||||
@(link_name="llvm.ctlz.i8") leading_zeros8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.ctlz.i16") leading_zeros16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.ctlz.i32") leading_zeros32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.ctlz.i64") leading_zeros64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
count_ones :: intrinsics.count_ones;
|
||||
count_zeros :: intrinsics.count_zeros;
|
||||
trailing_zeros :: intrinsics.count_trailing_zeros;
|
||||
leading_zeros :: intrinsics.count_leading_zeros;
|
||||
count_trailing_zeros :: intrinsics.count_trailing_zeros;
|
||||
count_leading_zeros :: intrinsics.count_leading_zeros;
|
||||
reverse_bits :: intrinsics.reverse_bits;
|
||||
byte_swap :: intrinsics.byte_swap;
|
||||
|
||||
@(link_name="llvm.cttz.i8") trailing_zeros8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.cttz.i16") trailing_zeros16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.cttz.i32") trailing_zeros32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.cttz.i64") trailing_zeros64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
overflowing_add :: intrinsics.overflow_add;
|
||||
overflowing_sub :: intrinsics.overflow_sub;
|
||||
overflowing_mul :: intrinsics.overflow_mul;
|
||||
|
||||
@(link_name="llvm.bitreverse.i8") reverse_bits8 :: proc(i: u8) -> u8 ---
|
||||
@(link_name="llvm.bitreverse.i16") reverse_bits16 :: proc(i: u16) -> u16 ---
|
||||
@(link_name="llvm.bitreverse.i32") reverse_bits32 :: proc(i: u32) -> u32 ---
|
||||
@(link_name="llvm.bitreverse.i64") reverse_bits64 :: proc(i: u64) -> u64 ---
|
||||
|
||||
@(link_name="llvm.bswap.i16") byte_swap_u16 :: proc(u16) -> u16 ---
|
||||
@(link_name="llvm.bswap.i32") byte_swap_u32 :: proc(u32) -> u32 ---
|
||||
@(link_name="llvm.bswap.i64") byte_swap_u64 :: proc(u64) -> u64 ---
|
||||
@(link_name="llvm.bswap.i16") byte_swap_i16 :: proc(i16) -> i16 ---
|
||||
@(link_name="llvm.bswap.i32") byte_swap_i32 :: proc(i32) -> i32 ---
|
||||
@(link_name="llvm.bswap.i64") byte_swap_i64 :: proc(i64) -> i64 ---
|
||||
@(link_name="llvm.bswap.i128") byte_swap_u128 :: proc(u128) -> u128 ---
|
||||
@(link_name="llvm.bswap.i128") byte_swap_i128 :: proc(i128) -> i128 ---
|
||||
rotate_left8 :: proc(x: u8, k: int) -> u8 {
|
||||
n :: 8;
|
||||
s := uint(k) & (n-1);
|
||||
return x <<s | x>>(n-s);
|
||||
}
|
||||
rotate_left16 :: proc(x: u16, k: int) -> u16 {
|
||||
n :: 16;
|
||||
s := uint(k) & (n-1);
|
||||
return x <<s | x>>(n-s);
|
||||
}
|
||||
rotate_left32 :: proc(x: u32, k: int) -> u32 {
|
||||
n :: 32;
|
||||
s := uint(k) & (n-1);
|
||||
return x <<s | x>>(n-s);
|
||||
}
|
||||
rotate_left64 :: proc(x: u64, k: int) -> u64 {
|
||||
n :: 64;
|
||||
s := uint(k) & (n-1);
|
||||
return x <<s | x>>(n-s);
|
||||
}
|
||||
|
||||
byte_swap_uint :: proc(i: uint) -> uint {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
return uint(byte_swap_u32(u32(i)));
|
||||
} else {
|
||||
return uint(byte_swap_u64(u64(i)));
|
||||
}
|
||||
rotate_left :: proc(x: uint, k: int) -> uint {
|
||||
n :: 8*size_of(uint);
|
||||
s := uint(k) & (n-1);
|
||||
return x <<s | x>>(n-s);
|
||||
}
|
||||
byte_swap_int :: proc(i: int) -> int {
|
||||
when size_of(int) == size_of(i32) {
|
||||
return int(byte_swap_i32(i32(i)));
|
||||
} else {
|
||||
return int(byte_swap_i64(i64(i)));
|
||||
}
|
||||
}
|
||||
|
||||
byte_swap :: proc{
|
||||
byte_swap_u16,
|
||||
byte_swap_u32,
|
||||
byte_swap_u64,
|
||||
byte_swap_u128,
|
||||
byte_swap_i16,
|
||||
byte_swap_i32,
|
||||
byte_swap_i64,
|
||||
byte_swap_i128,
|
||||
byte_swap_uint,
|
||||
byte_swap_int,
|
||||
};
|
||||
|
||||
count_zeros8 :: proc(i: u8) -> u8 { return 8 - count_ones8(i); }
|
||||
count_zeros16 :: proc(i: u16) -> u16 { return 16 - count_ones16(i); }
|
||||
count_zeros32 :: proc(i: u32) -> u32 { return 32 - count_ones32(i); }
|
||||
count_zeros64 :: proc(i: u64) -> u64 { return 64 - count_ones64(i); }
|
||||
|
||||
|
||||
rotate_left8 :: proc(i: u8, s: uint) -> u8 { return (i << s)|(i >> (8*size_of(u8) - s)); }
|
||||
rotate_left16 :: proc(i: u16, s: uint) -> u16 { return (i << s)|(i >> (8*size_of(u16) - s)); }
|
||||
rotate_left32 :: proc(i: u32, s: uint) -> u32 { return (i << s)|(i >> (8*size_of(u32) - s)); }
|
||||
rotate_left64 :: proc(i: u64, s: uint) -> u64 { return (i << s)|(i >> (8*size_of(u64) - s)); }
|
||||
|
||||
|
||||
rotate_right8 :: proc(i: u8, s: uint) -> u8 { return (i >> s)|(i << (8*size_of(u8) - s)); }
|
||||
rotate_right16 :: proc(i: u16, s: uint) -> u16 { return (i >> s)|(i << (8*size_of(u16) - s)); }
|
||||
rotate_right32 :: proc(i: u32, s: uint) -> u32 { return (i >> s)|(i << (8*size_of(u32) - s)); }
|
||||
rotate_right64 :: proc(i: u64, s: uint) -> u64 { return (i >> s)|(i << (8*size_of(u64) - s)); }
|
||||
|
||||
from_be_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
from_be_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_uint :: proc(i: uint) -> uint { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
from_le_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
from_le_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_uint :: proc(i: uint) -> uint { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
to_be_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
to_be_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_uint :: proc(i: uint) -> uint { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
|
||||
to_le_u8 :: proc(i: u8) -> u8 { return i; }
|
||||
to_le_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_uint :: proc(i: uint) -> uint { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.uadd.with.overflow.i8") overflowing_add_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i8") overflowing_add_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i16") overflowing_add_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i16") overflowing_add_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i32") overflowing_add_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i32") overflowing_add_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i64") overflowing_add_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i64") overflowing_add_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
|
||||
len_u8 :: proc(x: u8) -> int {
|
||||
return int(len_u8_table[x]);
|
||||
}
|
||||
len_u16 :: proc(x: u16) -> (n: int) {
|
||||
x := x;
|
||||
if x >= 1<<8 {
|
||||
x >>= 8;
|
||||
n = 8;
|
||||
}
|
||||
return n + int(len_u8_table[x]);
|
||||
}
|
||||
len_u32 :: proc(x: u32) -> (n: int) {
|
||||
x := x;
|
||||
if x >= 1<<16 {
|
||||
x >>= 16;
|
||||
n = 16;
|
||||
}
|
||||
if x >= 1<<8 {
|
||||
x >>= 8;
|
||||
n += 8;
|
||||
}
|
||||
return n + int(len_u8_table[x]);
|
||||
}
|
||||
len_u64 :: proc(x: u64) -> (n: int) {
|
||||
x := x;
|
||||
if x >= 1<<32 {
|
||||
x >>= 32;
|
||||
n = 32;
|
||||
}
|
||||
if x >= 1<<16 {
|
||||
x >>= 16;
|
||||
n += 16;
|
||||
}
|
||||
if x >= 1<<8 {
|
||||
x >>= 8;
|
||||
n += 8;
|
||||
}
|
||||
return n + int(len_u8_table[x]);
|
||||
}
|
||||
len_uint :: proc(x: uint) -> (n: int) {
|
||||
when size_of(uint) == size_of(u64) {
|
||||
return len_u64(u64(x));
|
||||
} else {
|
||||
return len_u32(u32(x));
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_add_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
// returns the minimum number of bits required to represent x
|
||||
len :: proc{len_u8, len_u16, len_u32, len_u64, len_uint};
|
||||
|
||||
|
||||
add_u32 :: proc(x, y, carry: u32) -> (sum, carry_out: u32) {
|
||||
yc := y + carry;
|
||||
sum = x + yc;
|
||||
if sum < x || yc < y {
|
||||
carry_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
add_u64 :: proc(x, y, carry: u64) -> (sum, carry_out: u64) {
|
||||
yc := y + carry;
|
||||
sum = x + yc;
|
||||
if sum < x || yc < y {
|
||||
carry_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
add_uint :: proc(x, y, carry: uint) -> (sum, carry_out: uint) {
|
||||
yc := y + carry;
|
||||
sum = x + yc;
|
||||
if sum < x || yc < y {
|
||||
carry_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
add :: proc{add_u32, add_u64, add_uint};
|
||||
|
||||
|
||||
sub_u32 :: proc(x, y, borrow: u32) -> (diff, borrow_out: u32) {
|
||||
yb := y + borrow;
|
||||
diff = x - yb;
|
||||
if diff > x || yb < y {
|
||||
borrow_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
sub_u64 :: proc(x, y, borrow: u64) -> (diff, borrow_out: u64) {
|
||||
yb := y + borrow;
|
||||
diff = x - yb;
|
||||
if diff > x || yb < y {
|
||||
borrow_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
sub_uint :: proc(x, y, borrow: uint) -> (diff, borrow_out: uint) {
|
||||
yb := y + borrow;
|
||||
diff = x - yb;
|
||||
if diff > x || yb < y {
|
||||
borrow_out = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
sub :: proc{sub_u32, sub_u64, sub_uint};
|
||||
|
||||
|
||||
mul_u32 :: proc(x, y: u32) -> (hi, lo: u32) {
|
||||
z := u64(x) * u64(y);
|
||||
hi, lo = u32(z>>32), u32(z);
|
||||
return;
|
||||
}
|
||||
mul_u64 :: proc(x, y: u64) -> (hi, lo: u64) {
|
||||
mask :: 1<<32 - 1;
|
||||
|
||||
x0, x1 := x & mask, x >> 32;
|
||||
y0, y1 := y & mask, y >> 32;
|
||||
|
||||
w0 := x0 * y0;
|
||||
t := x1*y0 + w0>>32;
|
||||
|
||||
w1, w2 := t & mask, t >> 32;
|
||||
w1 += x0 * y1;
|
||||
hi = x1*y1 + w2 + w1>>32;
|
||||
lo = x * y;
|
||||
return;
|
||||
}
|
||||
|
||||
mul_uint :: proc(x, y: uint) -> (hi, lo: uint) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_add_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
a, b := mul_u32(u32(x), u32(y));
|
||||
} else {
|
||||
x, ok := overflowing_add_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_add_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_add_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_add_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
#assert(size_of(uint) == size_of(u64));
|
||||
a, b := mul_u64(u64(x), u64(y));
|
||||
}
|
||||
return uint(a), uint(b);
|
||||
}
|
||||
|
||||
overflowing_add :: proc{
|
||||
overflowing_add_u8, overflowing_add_i8,
|
||||
overflowing_add_u16, overflowing_add_i16,
|
||||
overflowing_add_u32, overflowing_add_i32,
|
||||
overflowing_add_u64, overflowing_add_i64,
|
||||
overflowing_add_uint, overflowing_add_int,
|
||||
};
|
||||
mul :: proc{mul_u32, mul_u64, mul_uint};
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.usub.with.overflow.i8") overflowing_sub_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i8") overflowing_sub_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i16") overflowing_sub_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i16") overflowing_sub_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i32") overflowing_sub_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i32") overflowing_sub_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i64") overflowing_sub_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i64") overflowing_sub_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
|
||||
div_u32 :: proc(hi, lo, y: u32) -> (quo, rem: u32) {
|
||||
assert(y != 0 && y <= hi);
|
||||
z := u64(hi)<<32 | u64(lo);
|
||||
quo, rem = u32(z/u64(y)), u32(z%u64(y));
|
||||
return;
|
||||
}
|
||||
overflowing_sub_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
div_u64 :: proc(hi, lo, y: u64) -> (quo, rem: u64) {
|
||||
y := y;
|
||||
two32 :: 1 << 32;
|
||||
mask32 :: two32 - 1;
|
||||
if y == 0 {
|
||||
panic("divide error");
|
||||
}
|
||||
if y <= hi {
|
||||
panic("overflow error");
|
||||
}
|
||||
|
||||
s := uint(count_leading_zeros(y));
|
||||
y <<= s;
|
||||
|
||||
yn1 := y >> 32;
|
||||
yn0 := y & mask32;
|
||||
un32 := hi<<s | lo>>(64-s);
|
||||
un10 := lo << s;
|
||||
un1 := un10 >> 32;
|
||||
un0 := un10 & mask32;
|
||||
q1 := un32 / yn1;
|
||||
rhat := un32 - q1*yn1;
|
||||
|
||||
for q1 >= two32 || q1*yn0 > two32*rhat+un1 {
|
||||
q1 -= 1;
|
||||
rhat += yn1;
|
||||
if rhat >= two32 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
un21 := un32*two32 + un1 - q1*y;
|
||||
q0 := un21 / yn1;
|
||||
rhat = un21 - q0*yn1;
|
||||
|
||||
for q0 >= two32 || q0*yn0 > two32*rhat+un0 {
|
||||
q0 -= 1;
|
||||
rhat += yn1;
|
||||
if rhat >= two32 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s;
|
||||
}
|
||||
div_uint :: proc(hi, lo, y: uint) -> (quo, rem: uint) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_sub_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
a, b := div_u32(u32(hi), u32(lo), u32(y));
|
||||
} else {
|
||||
x, ok := overflowing_sub_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_sub_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_sub_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_sub_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
#assert(size_of(uint) == size_of(u64));
|
||||
a, b := div_u64(u64(hi), u64(lo), u64(y));
|
||||
}
|
||||
return uint(a), uint(b);
|
||||
}
|
||||
div :: proc{div_u32, div_u64, div_uint};
|
||||
|
||||
overflowing_sub :: proc{
|
||||
overflowing_sub_u8, overflowing_sub_i8,
|
||||
overflowing_sub_u16, overflowing_sub_i16,
|
||||
overflowing_sub_u32, overflowing_sub_i32,
|
||||
overflowing_sub_u64, overflowing_sub_i64,
|
||||
overflowing_sub_uint, overflowing_sub_int,
|
||||
};
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.umul.with.overflow.i8") overflowing_mul_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i8") overflowing_mul_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i16") overflowing_mul_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i16") overflowing_mul_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i32") overflowing_mul_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i32") overflowing_mul_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i64") overflowing_mul_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i64") overflowing_mul_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
}
|
||||
overflowing_mul_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_mul_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_mul_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_mul_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_mul :: proc{
|
||||
overflowing_mul_u8, overflowing_mul_i8,
|
||||
overflowing_mul_u16, overflowing_mul_i16,
|
||||
overflowing_mul_u32, overflowing_mul_i32,
|
||||
overflowing_mul_u64, overflowing_mul_i64,
|
||||
overflowing_mul_uint, overflowing_mul_int,
|
||||
};
|
||||
|
||||
is_power_of_two_u8 :: proc(i: u8) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i8 :: proc(i: i8) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
@@ -258,3 +312,144 @@ is_power_of_two :: proc{
|
||||
is_power_of_two_u64, is_power_of_two_i64,
|
||||
is_power_of_two_uint, is_power_of_two_int,
|
||||
};
|
||||
|
||||
|
||||
@private
|
||||
len_u8_table := [256]u8{
|
||||
0 = 0,
|
||||
1 = 1,
|
||||
2..<4 = 2,
|
||||
4..<8 = 3,
|
||||
8..<16 = 4,
|
||||
16..<32 = 5,
|
||||
32..<64 = 6,
|
||||
64..<128 = 7,
|
||||
128..<256 = 8,
|
||||
};
|
||||
|
||||
|
||||
bitfield_extract_u8 :: proc(value: u8, offset, bits: uint) -> u8 { return (value >> offset) & u8(1<<bits - 1); }
|
||||
bitfield_extract_u16 :: proc(value: u16, offset, bits: uint) -> u16 { return (value >> offset) & u16(1<<bits - 1); }
|
||||
bitfield_extract_u32 :: proc(value: u32, offset, bits: uint) -> u32 { return (value >> offset) & u32(1<<bits - 1); }
|
||||
bitfield_extract_u64 :: proc(value: u64, offset, bits: uint) -> u64 { return (value >> offset) & u64(1<<bits - 1); }
|
||||
bitfield_extract_u128 :: proc(value: u128, offset, bits: uint) -> u128 { return (value >> offset) & u128(1<<bits - 1); }
|
||||
bitfield_extract_uint :: proc(value: uint, offset, bits: uint) -> uint { return (value >> offset) & uint(1<<bits - 1); }
|
||||
|
||||
bitfield_extract_i8 :: proc(value: i8, offset, bits: uint) -> i8 {
|
||||
v := (u8(value) >> offset) & u8(1<<bits - 1);
|
||||
m := u8(1<<(bits-1));
|
||||
r := (v~m) - m;
|
||||
return i8(r);
|
||||
}
|
||||
bitfield_extract_i16 :: proc(value: i16, offset, bits: uint) -> i16 {
|
||||
v := (u16(value) >> offset) & u16(1<<bits - 1);
|
||||
m := u16(1<<(bits-1));
|
||||
r := (v~m) - m;
|
||||
return i16(r);
|
||||
}
|
||||
bitfield_extract_i32 :: proc(value: i32, offset, bits: uint) -> i32 {
|
||||
v := (u32(value) >> offset) & u32(1<<bits - 1);
|
||||
m := u32(1<<(bits-1));
|
||||
r := (v~m) - m;
|
||||
return i32(r);
|
||||
}
|
||||
bitfield_extract_i64 :: proc(value: i64, offset, bits: uint) -> i64 {
|
||||
v := (u64(value) >> offset) & u64(1<<bits - 1);
|
||||
m := u64(1<<(bits-1));
|
||||
r := (v~m) - m;
|
||||
return i64(r);
|
||||
}
|
||||
bitfield_extract_i128 :: proc(value: i128, offset, bits: uint) -> i128 {
|
||||
v := (u128(value) >> offset) & u128(1<<bits - 1);
|
||||
m := u128(1<<(bits-1));
|
||||
r := (v~m) - m;
|
||||
return i128(r);
|
||||
}
|
||||
bitfield_extract_int :: proc(value: int, offset, bits: uint) -> int {
|
||||
v := (uint(value) >> offset) & uint(1<<bits - 1);
|
||||
m := uint(1<<(bits-1));
|
||||
r := (v~m) - m;
|
||||
return int(r);
|
||||
}
|
||||
|
||||
|
||||
bitfield_extract :: proc{
|
||||
bitfield_extract_u8,
|
||||
bitfield_extract_u16,
|
||||
bitfield_extract_u32,
|
||||
bitfield_extract_u64,
|
||||
bitfield_extract_u128,
|
||||
bitfield_extract_uint,
|
||||
bitfield_extract_i8,
|
||||
bitfield_extract_i16,
|
||||
bitfield_extract_i32,
|
||||
bitfield_extract_i64,
|
||||
bitfield_extract_i128,
|
||||
bitfield_extract_int,
|
||||
};
|
||||
|
||||
|
||||
bitfield_insert_u8 :: proc(base, insert: u8, offset, bits: uint) -> u8 {
|
||||
mask := u8(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
bitfield_insert_u16 :: proc(base, insert: u16, offset, bits: uint) -> u16 {
|
||||
mask := u16(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
bitfield_insert_u32 :: proc(base, insert: u32, offset, bits: uint) -> u32 {
|
||||
mask := u32(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
bitfield_insert_u64 :: proc(base, insert: u64, offset, bits: uint) -> u64 {
|
||||
mask := u64(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
bitfield_insert_u128 :: proc(base, insert: u128, offset, bits: uint) -> u128 {
|
||||
mask := u128(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
bitfield_insert_uint :: proc(base, insert: uint, offset, bits: uint) -> uint {
|
||||
mask := uint(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
|
||||
bitfield_insert_i8 :: proc(base, insert: i8, offset, bits: uint) -> i8 {
|
||||
mask := i8(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
bitfield_insert_i16 :: proc(base, insert: i16, offset, bits: uint) -> i16 {
|
||||
mask := i16(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
bitfield_insert_i32 :: proc(base, insert: i32, offset, bits: uint) -> i32 {
|
||||
mask := i32(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
bitfield_insert_i64 :: proc(base, insert: i64, offset, bits: uint) -> i64 {
|
||||
mask := i64(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
bitfield_insert_i128 :: proc(base, insert: i128, offset, bits: uint) -> i128 {
|
||||
mask := i128(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
bitfield_insert_int :: proc(base, insert: int, offset, bits: uint) -> int {
|
||||
mask := int(1<<bits - 1);
|
||||
return (base &~ (mask<<offset)) | ((insert&mask) << offset);
|
||||
}
|
||||
|
||||
bitfield_insert :: proc{
|
||||
bitfield_insert_u8,
|
||||
bitfield_insert_u16,
|
||||
bitfield_insert_u32,
|
||||
bitfield_insert_u64,
|
||||
bitfield_insert_u128,
|
||||
bitfield_insert_uint,
|
||||
bitfield_insert_i8,
|
||||
bitfield_insert_i16,
|
||||
bitfield_insert_i32,
|
||||
bitfield_insert_i64,
|
||||
bitfield_insert_i128,
|
||||
bitfield_insert_int,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package math_fixed
|
||||
|
||||
import "core:math"
|
||||
import "core:strconv"
|
||||
|
||||
import "intrinsics"
|
||||
_ :: intrinsics;
|
||||
|
||||
Fixed :: struct($Backing: typeid, $Fraction_Width: uint)
|
||||
where
|
||||
intrinsics.type_is_integer(Backing),
|
||||
0 <= Fraction_Width,
|
||||
Fraction_Width <= 8*size_of(Backing) {
|
||||
i: Backing,
|
||||
}
|
||||
|
||||
Fixed4_4 :: distinct Fixed(i8, 4);
|
||||
Fixed5_3 :: distinct Fixed(i8, 3);
|
||||
Fixed6_2 :: distinct Fixed(i8, 2);
|
||||
Fixed7_1 :: distinct Fixed(i8, 1);
|
||||
|
||||
Fixed8_8 :: distinct Fixed(i16, 8);
|
||||
Fixed13_3 :: distinct Fixed(i16, 3);
|
||||
|
||||
Fixed16_16 :: distinct Fixed(i32, 16);
|
||||
Fixed26_6 :: distinct Fixed(i32, 6);
|
||||
|
||||
Fixed32_32 :: distinct Fixed(i64, 32);
|
||||
Fixed52_12 :: distinct Fixed(i64, 12);
|
||||
|
||||
|
||||
init_from_f64 :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), val: f64) {
|
||||
i, f := math.modf(val);
|
||||
x.i = Backing(f * (1<<Fraction_Width));
|
||||
x.i &= 1<<Fraction_Width - 1;
|
||||
x.i |= Backing(i) << Fraction_Width;
|
||||
}
|
||||
|
||||
|
||||
init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fraction: Backing) {
|
||||
i, f := math.modf(val);
|
||||
x.i = fraction;
|
||||
x.i &= 1<<Fraction_Width - 1;
|
||||
x.i |= integer;
|
||||
}
|
||||
|
||||
to_f64 :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> f64 {
|
||||
res := f64(x.i >> Fraction_Width);
|
||||
res += f64(x.i & (1<<Fraction_Width-1)) / f64(1<<Fraction_Width);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
add :: proc(x, y: $T/Fixed) -> T {
|
||||
return {x.i + y.i};
|
||||
}
|
||||
sub :: proc(x, y: $T/Fixed) -> T {
|
||||
return {x.i - y.i};
|
||||
}
|
||||
|
||||
mul :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
|
||||
z.i = intrinsics.fixed_point_mul(x.i, y.i, Fraction_Width);
|
||||
return;
|
||||
}
|
||||
mul_sat :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
|
||||
z.i = intrinsics.fixed_point_mul_sat(x.i, y.i, Fraction_Width);
|
||||
return;
|
||||
}
|
||||
|
||||
div :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
|
||||
z.i = intrinsics.fixed_point_div(x.i, y.i, Fraction_Width);
|
||||
return;
|
||||
}
|
||||
div_sat :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
|
||||
z.i = intrinsics.fixed_point_div_sat(x.i, y.i, Fraction_Width);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
floor :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
|
||||
return x.i >> Fraction_Width;
|
||||
}
|
||||
ceil :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
|
||||
Integer :: 8*size_of(Backing) - Fraction_Width;
|
||||
return (x.i + (1 << Integer-1)) >> Fraction_Width;
|
||||
}
|
||||
round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
|
||||
Integer :: 8*size_of(Backing) - Fraction_Width;
|
||||
return (x.i + (1 << (Integer - 1))) >> Fraction_Width;
|
||||
}
|
||||
|
||||
|
||||
|
||||
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
|
||||
x := x;
|
||||
buf: [48]byte;
|
||||
i := 0;
|
||||
if x.i < 0 {
|
||||
buf[i] = '-';
|
||||
i += 1;
|
||||
x.i = -x.i;
|
||||
}
|
||||
|
||||
integer := x.i >> Fraction_Width;
|
||||
fraction := x.i & (1<<Fraction_Width - 1);
|
||||
|
||||
s := strconv.append_uint(buf[i:], u64(integer), 10);
|
||||
i += len(s);
|
||||
if fraction != 0 {
|
||||
buf[i] = '.';
|
||||
i += 1;
|
||||
for fraction > 0 {
|
||||
fraction *= 10;
|
||||
buf[i] = byte('0' + (fraction>>Fraction_Width));
|
||||
i += 1;
|
||||
fraction &= 1<<Fraction_Width - 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
n := copy(dst, buf[:i]);
|
||||
return string(dst[:i]);
|
||||
}
|
||||
|
||||
|
||||
to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.allocator) -> string {
|
||||
buf: [48]byte;
|
||||
s := append(buf[:], x);
|
||||
str := make([]byte, len(s), allocator);
|
||||
copy(str, s);
|
||||
return string(str);
|
||||
}
|
||||
@@ -0,0 +1,553 @@
|
||||
package linalg
|
||||
|
||||
import "builtin"
|
||||
import "core:math"
|
||||
|
||||
radians :: proc(degrees: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = degrees * RAD_PER_DEG;
|
||||
}
|
||||
} else {
|
||||
out = degrees * RAD_PER_DEG;
|
||||
}
|
||||
return;
|
||||
}
|
||||
degrees :: proc(radians: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = radians * DEG_PER_RAD;
|
||||
}
|
||||
} else {
|
||||
out = radians * DEG_PER_RAD;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
min_double :: proc(a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = builtin.min(a[i], b[i]);
|
||||
}
|
||||
} else {
|
||||
out = builtin.min(a, b);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
min_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
N :: len(T);
|
||||
|
||||
when N == 1 {
|
||||
out = a[0];
|
||||
} else when N == 2 {
|
||||
out = builtin.min(a[0], a[1]);
|
||||
} else {
|
||||
out = builtin.min(a[0], a[1]);
|
||||
for i in 2..<N {
|
||||
out = builtin.min(out, a[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out = a;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
min_triple :: proc(a, b, c: $T) -> T where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
return min_double(a, min_double(b, c));
|
||||
}
|
||||
|
||||
min :: proc{min_single, min_double, min_triple};
|
||||
|
||||
max_double :: proc(a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = builtin.max(a[i], b[i]);
|
||||
}
|
||||
} else {
|
||||
out = builtin.max(a, b);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
max_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
N :: len(T);
|
||||
|
||||
when N == 1 {
|
||||
out = a[0];
|
||||
} else when N == 2 {
|
||||
out = builtin.max(a[0], a[1]);
|
||||
} else when N == 3 {
|
||||
out = builtin.max(a[0], a[1], a[3]);
|
||||
}else {
|
||||
out = builtin.max(a[0], a[1]);
|
||||
for i in 2..<N {
|
||||
out = builtin.max(out, a[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out = a;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
max_triple :: proc(a, b, c: $T) -> T where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
return max_double(a, max_double(b, c));
|
||||
}
|
||||
|
||||
max :: proc{max_single, max_double, max_triple};
|
||||
|
||||
abs :: proc(a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = builtin.abs(a[i]);
|
||||
}
|
||||
} else {
|
||||
out = builtin.abs(a);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sign :: proc(a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = #force_inline math.sign(a[i]);
|
||||
}
|
||||
} else {
|
||||
out = #force_inline math.sign(a);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
clamp :: proc(x, a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = builtin.clamp(x[i], a[i], b[i]);
|
||||
}
|
||||
} else {
|
||||
out = builtin.clamp(x, a, b);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
saturate :: proc(x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
return clamp(x, 0.0, 1.0);
|
||||
}
|
||||
|
||||
lerp :: proc(a, b, t: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = a[i]*(1-t[i]) + b[i]*t[i];
|
||||
}
|
||||
} else {
|
||||
out = a * (1.0 - t) + b * t;
|
||||
}
|
||||
return;
|
||||
}
|
||||
mix :: proc(a, b, t: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = a[i]*(1-t[i]) + b[i]*t[i];
|
||||
}
|
||||
} else {
|
||||
out = a * (1.0 - t) + b * t;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
unlerp :: proc(a, b, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
return (x - a) / (b - a);
|
||||
}
|
||||
|
||||
step :: proc(e, x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = x[i] < e[i] ? 0.0 : 1.0;
|
||||
}
|
||||
} else {
|
||||
out = x < e ? 0.0 : 1.0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
smoothstep :: proc(e0, e1, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
t := saturate(unlerp(e0, e1, x));
|
||||
return t * t * (3.0 - 2.0 * t);
|
||||
}
|
||||
|
||||
smootherstep :: proc(e0, e1, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
t := saturate(unlerp(e0, e1, x));
|
||||
return t * t * t * (t * (6*t - 15) + 10);
|
||||
}
|
||||
|
||||
|
||||
sqrt :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.sqrt(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.sqrt(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
inverse_sqrt :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = 1.0/math.sqrt(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = 1.0/math.sqrt(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
cos :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.cos(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.cos(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
sin :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.sin(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.sin(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
tan :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.tan(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.tan(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
acos :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.acos(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.acos(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
asin :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.asin(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.asin(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
atan :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.atan(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.atan(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
atan2 :: proc(y, x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.atan2(y[i], x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.atan2(y, x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ln :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.ln(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.ln(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
log2 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = INVLN2 * math.ln(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = INVLN2 * math.ln(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
log10 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = INVLN10 * math.ln(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = INVLN10 * math.ln(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
log :: proc(x, b: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.ln(x[i]) / math.ln(cast(ELEM_TYPE(T))b[i]);
|
||||
}
|
||||
} else {
|
||||
out = INVLN10 * math.ln(x) / math.ln(cast(ELEM_TYPE(T))b);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
exp :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.exp(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.exp(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
exp2 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.exp(LN2 * x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.exp(LN2 * x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
exp10 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.exp(LN10 * x[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.exp(LN10 * x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
pow :: proc(x, e: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = math.pow(x[i], e[i]);
|
||||
}
|
||||
} else {
|
||||
out = math.pow(x, e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ceil :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = #force_inline math.ceil(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = #force_inline math.ceil(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
floor :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = #force_inline math.floor(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = #force_inline math.floor(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
round :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = #force_inline math.round(x[i]);
|
||||
}
|
||||
} else {
|
||||
out = #force_inline math.round(x);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fract :: proc(x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
f := #force_inline floor(x);
|
||||
return x - f;
|
||||
}
|
||||
|
||||
mod :: proc(x, m: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
f := #force_inline floor(x / m);
|
||||
return x - f * m;
|
||||
}
|
||||
|
||||
|
||||
face_forward :: proc(N, I, N_ref: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
|
||||
return dot(N_ref, I) < 0 ? N : -N;
|
||||
}
|
||||
|
||||
distance :: proc(p0, p1: $V/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
return length(p1 - p0);
|
||||
}
|
||||
|
||||
reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
|
||||
b := n * (2 * dot(n, i));
|
||||
return i - b;
|
||||
}
|
||||
refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
|
||||
dv := dot(n, i);
|
||||
k := 1 - eta*eta - (1 - dv*dv);
|
||||
a := i * eta;
|
||||
b := n * eta*dv*math.sqrt(k);
|
||||
return (a - b) * E(int(k >= 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
is_nan_single :: proc(x: $T) -> bool where IS_FLOAT(T) {
|
||||
return #force_inline math.is_nan(x);
|
||||
}
|
||||
|
||||
is_nan_array :: proc(x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T) {
|
||||
for i in 0..<N {
|
||||
out[i] = #force_inline is_nan(x[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
is_inf_single :: proc(x: $T) -> bool where IS_FLOAT(T) {
|
||||
return #force_inline math.is_inf(x);
|
||||
}
|
||||
|
||||
is_inf_array :: proc(x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T) {
|
||||
for i in 0..<N {
|
||||
out[i] = #force_inline is_inf(x[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
classify_single :: proc(x: $T) -> math.Float_Class where IS_FLOAT(T) {
|
||||
return #force_inline math.classify(x);
|
||||
}
|
||||
|
||||
classify_array :: proc(x: $A/[$N]$T) -> (out: [N]math.Float_Class) where IS_FLOAT(T) {
|
||||
for i in 0..<N {
|
||||
out[i] = #force_inline classify_single(x[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
is_nan :: proc{is_nan_single, is_nan_array};
|
||||
is_inf :: proc{is_inf_single, is_inf_array};
|
||||
classify :: proc{classify_single, classify_array};
|
||||
|
||||
|
||||
less_than_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x < y; }
|
||||
less_than_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x <= y; }
|
||||
greater_than_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x > y; }
|
||||
greater_than_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x >= y; }
|
||||
equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x == y; }
|
||||
not_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x != y; }
|
||||
|
||||
less_than_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
|
||||
for i in 0..<N {
|
||||
out[i] = x[i] < y[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
less_than_equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
|
||||
for i in 0..<N {
|
||||
out[i] = x[i] <= y[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
greater_than_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
|
||||
for i in 0..<N {
|
||||
out[i] = x[i] > y[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
greater_than_equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
|
||||
for i in 0..<N {
|
||||
out[i] = x[i] >= y[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
|
||||
for i in 0..<N {
|
||||
out[i] = x[i] == y[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
not_equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
|
||||
for i in 0..<N {
|
||||
out[i] = x[i] != y[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
less_than :: proc{less_than_single, less_than_array};
|
||||
less_than_equal :: proc{less_than_equal_single, less_than_equal_array};
|
||||
greater_than :: proc{greater_than_single, greater_than_array};
|
||||
greater_than_equal :: proc{greater_than_equal_single, greater_than_equal_array};
|
||||
equal :: proc{equal_single, equal_array};
|
||||
not_equal :: proc{not_equal_single, not_equal_array};
|
||||
|
||||
any :: proc(x: $A/[$N]bool) -> (out: bool) {
|
||||
for e in x {
|
||||
if x {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
all :: proc(x: $A/[$N]bool) -> (out: bool) {
|
||||
for e in x {
|
||||
if !e {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
not :: proc(x: $A/[$N]bool) -> (out: A) {
|
||||
for e, i in x {
|
||||
out[i] = !e;
|
||||
}
|
||||
return;
|
||||
}
|
||||
+197
-231
@@ -5,17 +5,51 @@ import "intrinsics"
|
||||
|
||||
// Generic
|
||||
|
||||
TAU :: 6.28318530717958647692528676655900576;
|
||||
PI :: 3.14159265358979323846264338327950288;
|
||||
|
||||
E :: 2.71828182845904523536;
|
||||
|
||||
τ :: TAU;
|
||||
π :: PI;
|
||||
e :: E;
|
||||
|
||||
SQRT_TWO :: 1.41421356237309504880168872420969808;
|
||||
SQRT_THREE :: 1.73205080756887729352744634150587236;
|
||||
SQRT_FIVE :: 2.23606797749978969640917366873127623;
|
||||
|
||||
LN2 :: 0.693147180559945309417232121458176568;
|
||||
LN10 :: 2.30258509299404568401799145468436421;
|
||||
|
||||
MAX_F64_PRECISION :: 16; // Maximum number of meaningful digits after the decimal point for 'f64'
|
||||
MAX_F32_PRECISION :: 8; // Maximum number of meaningful digits after the decimal point for 'f32'
|
||||
|
||||
RAD_PER_DEG :: TAU/360.0;
|
||||
DEG_PER_RAD :: 360.0/TAU;
|
||||
|
||||
|
||||
|
||||
@private IS_NUMERIC :: intrinsics.type_is_numeric;
|
||||
@private IS_QUATERNION :: intrinsics.type_is_quaternion;
|
||||
@private IS_ARRAY :: intrinsics.type_is_array;
|
||||
@private IS_FLOAT :: intrinsics.type_is_float;
|
||||
@private BASE_TYPE :: intrinsics.type_base_type;
|
||||
@private ELEM_TYPE :: intrinsics.type_elem_type;
|
||||
|
||||
|
||||
scalar_dot :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
vector_dot :: proc(a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) {
|
||||
for i in 0..<N {
|
||||
c += a[i] * b[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
quaternion64_dot :: proc(a, b: $T/quaternion64) -> (c: f16) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
quaternion128_dot :: proc(a, b: $T/quaternion128) -> (c: f32) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
@@ -23,13 +57,27 @@ quaternion256_dot :: proc(a, b: $T/quaternion256) -> (c: f64) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
|
||||
dot :: proc{vector_dot, quaternion128_dot, quaternion256_dot};
|
||||
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot};
|
||||
|
||||
inner_product :: dot;
|
||||
outer_product :: proc(a: $A/[$M]$E, b: $B/[$N]E) -> (out: [M][N]E) where IS_NUMERIC(E) {
|
||||
for i in 0..<M {
|
||||
for j in 0..<N {
|
||||
out[i][j] = a[i]*b[j];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
quaternion_inverse :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
scalar_cross :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
vector_cross2 :: proc(a, b: $T/[2]$E) -> E where IS_NUMERIC(E) {
|
||||
return a[0]*b[1] - b[0]*a[1];
|
||||
}
|
||||
@@ -41,8 +89,16 @@ vector_cross3 :: proc(a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_cross :: proc{vector_cross2, vector_cross3};
|
||||
cross :: vector_cross;
|
||||
quaternion_cross :: proc(q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) {
|
||||
q3.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
|
||||
q3.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z;
|
||||
q3.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x;
|
||||
q3.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
|
||||
return;
|
||||
}
|
||||
|
||||
vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3};
|
||||
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross};
|
||||
|
||||
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
return v / length(v);
|
||||
@@ -54,11 +110,11 @@ normalize :: proc{vector_normalize, quaternion_normalize};
|
||||
|
||||
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
m := length(v);
|
||||
return m == 0 ? 0 : v/m;
|
||||
return 0 if m == 0 else v/m;
|
||||
}
|
||||
quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
m := abs(q);
|
||||
return m == 0 ? 0 : q/m;
|
||||
return 0 if m == 0 else q/m;
|
||||
}
|
||||
normalize0 :: proc{vector_normalize0, quaternion_normalize0};
|
||||
|
||||
@@ -79,231 +135,31 @@ quaternion_length2 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return dot(q, q);
|
||||
}
|
||||
|
||||
scalar_triple_product :: proc(a, b, c: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
// a . (b x c)
|
||||
// b . (c x a)
|
||||
// c . (a x b)
|
||||
return dot(a, cross(b, c));
|
||||
}
|
||||
|
||||
vector_triple_product :: proc(a, b, c: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
// a x (b x c)
|
||||
// (a . c)b - (a . b)c
|
||||
return cross(a, cross(b, c));
|
||||
}
|
||||
|
||||
|
||||
length :: proc{vector_length, quaternion_length};
|
||||
length2 :: proc{vector_length2, quaternion_length2};
|
||||
|
||||
|
||||
vector_lerp :: proc(x, y, t: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
ti := t[i];
|
||||
s[i] = x[i]*(1-ti) + y[i]*ti;
|
||||
}
|
||||
return s;
|
||||
projection :: proc(x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
return dot(x, normal) / dot(normal, normal) * normal;
|
||||
}
|
||||
|
||||
vector_unlerp :: proc(a, b, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
ai := a[i];
|
||||
s[i] = (x[i]-ai)/(b[i]-ai);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sin :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sin(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_cos :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.cos(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_tan :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.tan(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
vector_asin :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.asin(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_acos :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.acos(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_atan :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.atan(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_atan2 :: proc(y, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.atan(y[i], x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_pow :: proc(x, y: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.pow(x[i], y[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_expr :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.expr(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sqrt :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sqrt(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_abs :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = abs(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sign :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sign(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_floor :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.floor(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_ceil :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.ceil(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
vector_mod :: proc(x, y: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.mod(x[i], y[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_min :: proc(a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = min(a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_max :: proc(a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = max(a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_clamp :: proc(x, a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = clamp(x[i], a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_mix :: proc(x, y, a: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = x[i]*(1-a[i]) + y[i]*a[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_step :: proc(edge, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = x[i] < edge[i] ? 0 : 1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_smoothstep :: proc(edge0, edge1, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
e0, e1 := edge0[i], edge1[i];
|
||||
t := clamp((x[i] - e0) / (e1 - e0), 0, 1);
|
||||
s[i] = t * t * (3 - 2*t);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_smootherstep :: proc(edge0, edge1, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
e0, e1 := edge0[i], edge1[i];
|
||||
t := clamp((x[i] - e0) / (e1 - e0), 0, 1);
|
||||
s[i] = t * t * t * (t * (6*t - 15) + 10);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_distance :: proc(p0, p1: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
return length(p1 - p0);
|
||||
}
|
||||
|
||||
vector_reflect :: proc(i, n: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
b := n * (2 * dot(n, i));
|
||||
return i - b;
|
||||
}
|
||||
|
||||
vector_refract :: proc(i, n: $V/[$N]$E, eta: E) -> V where IS_NUMERIC(E) {
|
||||
dv := dot(n, i);
|
||||
k := 1 - eta*eta - (1 - dv*dv);
|
||||
a := i * eta;
|
||||
b := n * eta*dv*math.sqrt(k);
|
||||
return (a - b) * E(int(k >= 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
identity :: proc($T: typeid/[$N][N]$E) -> (m: T) {
|
||||
for i in 0..<N do m[i][i] = E(1);
|
||||
for i in 0..<N {
|
||||
m[i][i] = E(1);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -314,8 +170,7 @@ trace :: proc(m: $T/[$N][N]$E) -> (tr: E) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
transpose :: proc(a: $T/[$N][$M]$E) -> (m: T) {
|
||||
transpose :: proc(a: $T/[$N][$M]$E) -> (m: (T when N == M else [M][N]E)) {
|
||||
for j in 0..<M {
|
||||
for i in 0..<N {
|
||||
m[j][i] = a[i][j];
|
||||
@@ -337,6 +192,17 @@ matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
|
||||
return;
|
||||
}
|
||||
|
||||
matrix_comp_mul :: proc(a, b: $M/[$J][$I]$E) -> (c: M)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E) {
|
||||
for j in 0..<J {
|
||||
for i in 0..<I {
|
||||
c[j][i] = a[j][i] * b[j][i];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
matrix_mul_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (c: [K][I]E)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E),
|
||||
@@ -357,12 +223,25 @@ matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
|
||||
IS_NUMERIC(E) {
|
||||
for i in 0..<I {
|
||||
for j in 0..<J {
|
||||
c[i] += a[i][j] * b[i];
|
||||
c[j] += a[i][j] * b[i];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
quaternion_mul_quaternion :: proc(q1, q2: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return q1 * q2;
|
||||
}
|
||||
|
||||
quaternion64_mul_vector3 :: proc(q: $Q/quaternion64, v: $V/[3]$F/f16) -> V {
|
||||
Raw_Quaternion :: struct {xyz: [3]f16, r: f16};
|
||||
|
||||
q := transmute(Raw_Quaternion)q;
|
||||
v := transmute([3]f16)v;
|
||||
|
||||
t := cross(2*q.xyz, v);
|
||||
return V(v + q.r*t + cross(q.xyz, t));
|
||||
}
|
||||
quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
|
||||
Raw_Quaternion :: struct {xyz: [3]f32, r: f32};
|
||||
|
||||
@@ -372,7 +251,6 @@ quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
|
||||
t := cross(2*q.xyz, v);
|
||||
return V(v + q.r*t + cross(q.xyz, t));
|
||||
}
|
||||
|
||||
quaternion256_mul_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
|
||||
Raw_Quaternion :: struct {xyz: [3]f64, r: f64};
|
||||
|
||||
@@ -382,20 +260,108 @@ quaternion256_mul_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
|
||||
t := cross(2*q.xyz, v);
|
||||
return V(v + q.r*t + cross(q.xyz, t));
|
||||
}
|
||||
quaternion_mul_vector3 :: proc{quaternion128_mul_vector3, quaternion256_mul_vector3};
|
||||
quaternion_mul_vector3 :: proc{quaternion64_mul_vector3, quaternion128_mul_vector3, quaternion256_mul_vector3};
|
||||
|
||||
mul :: proc{
|
||||
matrix_mul,
|
||||
matrix_mul_differ,
|
||||
matrix_mul_vector,
|
||||
quaternion64_mul_vector3,
|
||||
quaternion128_mul_vector3,
|
||||
quaternion256_mul_vector3,
|
||||
quaternion_mul_quaternion,
|
||||
};
|
||||
|
||||
vector_to_ptr :: proc(v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E) {
|
||||
vector_to_ptr :: proc(v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E), N > 0 #no_bounds_check {
|
||||
return &v[0];
|
||||
}
|
||||
matrix_to_ptr :: proc(m: ^$A/[$I][$J]$E) -> ^E where IS_NUMERIC(E) {
|
||||
matrix_to_ptr :: proc(m: ^$A/[$I][$J]$E) -> ^E where IS_NUMERIC(E), I > 0, J > 0 #no_bounds_check {
|
||||
return &m[0][0];
|
||||
}
|
||||
|
||||
to_ptr :: proc{vector_to_ptr, matrix_to_ptr};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Splines
|
||||
|
||||
vector_slerp :: proc(x, y: $T/[$N]$E, a: E) -> T {
|
||||
cos_alpha := dot(x, y);
|
||||
alpha := math.acos(cos_alpha);
|
||||
sin_alpha := math.sin(alpha);
|
||||
|
||||
t1 := math.sin((1 - a) * alpha) / sin_alpha;
|
||||
t2 := math.sin(a * alpha) / sin_alpha;
|
||||
|
||||
return x * t1 + y * t2;
|
||||
}
|
||||
|
||||
catmull_rom :: proc(v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
|
||||
s2 := s*s;
|
||||
s3 := s2*s;
|
||||
|
||||
f1 := -s3 + 2 * s2 - s;
|
||||
f2 := 3 * s3 - 5 * s2 + 2;
|
||||
f3 := -3 * s3 + 4 * s2 + s;
|
||||
f4 := s3 - s2;
|
||||
|
||||
return (f1 * v1 + f2 * v2 + f3 * v3 + f4 * v4) * 0.5;
|
||||
}
|
||||
|
||||
hermite :: proc(v1, t1, v2, t2: $T/[$N]$E, s: E) -> T {
|
||||
s2 := s*s;
|
||||
s3 := s2*s;
|
||||
|
||||
f1 := 2 * s3 - 3 * s2 + 1;
|
||||
f2 := -2 * s3 + 3 * s2;
|
||||
f3 := s3 - 2 * s2 + s;
|
||||
f4 := s3 - s2;
|
||||
|
||||
return f1 * v1 + f2 * v2 + f3 * t1 + f4 * t2;
|
||||
}
|
||||
|
||||
cubic :: proc(v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
|
||||
return ((v1 * s + v2) * s + v3) * s + v4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
array_cast :: proc(v: $A/[$N]$T, $Elem_Type: typeid) -> (w: [N]Elem_Type) {
|
||||
for i in 0..<N {
|
||||
w[i] = Elem_Type(v[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
matrix_cast :: proc(v: $A/[$M][$N]$T, $Elem_Type: typeid) -> (w: [M][N]Elem_Type) {
|
||||
for i in 0..<M {
|
||||
for j in 0..<N {
|
||||
w[i][j] = Elem_Type(v[i][j]);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
to_f32 :: #force_inline proc(v: $A/[$N]$T) -> [N]f32 { return array_cast(v, f32); }
|
||||
to_f64 :: #force_inline proc(v: $A/[$N]$T) -> [N]f64 { return array_cast(v, f64); }
|
||||
|
||||
to_i8 :: #force_inline proc(v: $A/[$N]$T) -> [N]i8 { return array_cast(v, i8); }
|
||||
to_i16 :: #force_inline proc(v: $A/[$N]$T) -> [N]i16 { return array_cast(v, i16); }
|
||||
to_i32 :: #force_inline proc(v: $A/[$N]$T) -> [N]i32 { return array_cast(v, i32); }
|
||||
to_i64 :: #force_inline proc(v: $A/[$N]$T) -> [N]i64 { return array_cast(v, i64); }
|
||||
to_int :: #force_inline proc(v: $A/[$N]$T) -> [N]int { return array_cast(v, int); }
|
||||
|
||||
to_u8 :: #force_inline proc(v: $A/[$N]$T) -> [N]u8 { return array_cast(v, u8); }
|
||||
to_u16 :: #force_inline proc(v: $A/[$N]$T) -> [N]u16 { return array_cast(v, u16); }
|
||||
to_u32 :: #force_inline proc(v: $A/[$N]$T) -> [N]u32 { return array_cast(v, u32); }
|
||||
to_u64 :: #force_inline proc(v: $A/[$N]$T) -> [N]u64 { return array_cast(v, u64); }
|
||||
to_uint :: #force_inline proc(v: $A/[$N]$T) -> [N]uint { return array_cast(v, uint); }
|
||||
|
||||
to_complex32 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex32 { return array_cast(v, complex32); }
|
||||
to_complex64 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex64 { return array_cast(v, complex64); }
|
||||
to_complex128 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex128 { return array_cast(v, complex128); }
|
||||
to_quaternion64 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion64 { return array_cast(v, quaternion64); }
|
||||
to_quaternion128 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion128 { return array_cast(v, quaternion128); }
|
||||
to_quaternion256 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion256 { return array_cast(v, quaternion256); }
|
||||
|
||||
+2316
-396
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,127 @@
|
||||
package linalg
|
||||
|
||||
Euler_Angle_Order :: enum {
|
||||
// Tait-Bryan
|
||||
XYZ,
|
||||
XZY,
|
||||
YXZ,
|
||||
YZX,
|
||||
ZXY,
|
||||
ZYX,
|
||||
|
||||
// Proper Euler
|
||||
XYX,
|
||||
XZX,
|
||||
YXY,
|
||||
YZY,
|
||||
ZXZ,
|
||||
ZYZ,
|
||||
}
|
||||
|
||||
|
||||
quaternion_from_euler_angles :: proc{quaternion_from_euler_angles_f16, quaternion_from_euler_angles_f32, quaternion_from_euler_angles_f64};
|
||||
quaternion_from_euler_angle_x :: proc{quaternion_from_euler_angle_x_f16, quaternion_from_euler_angle_x_f32, quaternion_from_euler_angle_x_f64};
|
||||
quaternion_from_euler_angle_y :: proc{quaternion_from_euler_angle_y_f16, quaternion_from_euler_angle_y_f32, quaternion_from_euler_angle_y_f64};
|
||||
quaternion_from_euler_angle_z :: proc{quaternion_from_euler_angle_z_f16, quaternion_from_euler_angle_z_f32, quaternion_from_euler_angle_z_f64};
|
||||
quaternion_from_pitch_yaw_roll :: proc{quaternion_from_pitch_yaw_roll_f16, quaternion_from_pitch_yaw_roll_f32, quaternion_from_pitch_yaw_roll_f64};
|
||||
|
||||
euler_angles_from_quaternion :: proc{euler_angles_from_quaternion_f16, euler_angles_from_quaternion_f32, euler_angles_from_quaternion_f64};
|
||||
euler_angles_xyz_from_quaternion :: proc{euler_angles_xyz_from_quaternion_f16, euler_angles_xyz_from_quaternion_f32, euler_angles_xyz_from_quaternion_f64};
|
||||
euler_angles_yxz_from_quaternion :: proc{euler_angles_yxz_from_quaternion_f16, euler_angles_yxz_from_quaternion_f32, euler_angles_yxz_from_quaternion_f64};
|
||||
euler_angles_xzx_from_quaternion :: proc{euler_angles_xzx_from_quaternion_f16, euler_angles_xzx_from_quaternion_f32, euler_angles_xzx_from_quaternion_f64};
|
||||
euler_angles_xyx_from_quaternion :: proc{euler_angles_xyx_from_quaternion_f16, euler_angles_xyx_from_quaternion_f32, euler_angles_xyx_from_quaternion_f64};
|
||||
euler_angles_yxy_from_quaternion :: proc{euler_angles_yxy_from_quaternion_f16, euler_angles_yxy_from_quaternion_f32, euler_angles_yxy_from_quaternion_f64};
|
||||
euler_angles_yzy_from_quaternion :: proc{euler_angles_yzy_from_quaternion_f16, euler_angles_yzy_from_quaternion_f32, euler_angles_yzy_from_quaternion_f64};
|
||||
euler_angles_zyz_from_quaternion :: proc{euler_angles_zyz_from_quaternion_f16, euler_angles_zyz_from_quaternion_f32, euler_angles_zyz_from_quaternion_f64};
|
||||
euler_angles_zxz_from_quaternion :: proc{euler_angles_zxz_from_quaternion_f16, euler_angles_zxz_from_quaternion_f32, euler_angles_zxz_from_quaternion_f64};
|
||||
euler_angles_xzy_from_quaternion :: proc{euler_angles_xzy_from_quaternion_f16, euler_angles_xzy_from_quaternion_f32, euler_angles_xzy_from_quaternion_f64};
|
||||
euler_angles_yzx_from_quaternion :: proc{euler_angles_yzx_from_quaternion_f16, euler_angles_yzx_from_quaternion_f32, euler_angles_yzx_from_quaternion_f64};
|
||||
euler_angles_zyx_from_quaternion :: proc{euler_angles_zyx_from_quaternion_f16, euler_angles_zyx_from_quaternion_f32, euler_angles_zyx_from_quaternion_f64};
|
||||
euler_angles_zxy_from_quaternion :: proc{euler_angles_zxy_from_quaternion_f16, euler_angles_zxy_from_quaternion_f32, euler_angles_zxy_from_quaternion_f64};
|
||||
|
||||
roll_from_quaternion :: proc{roll_from_quaternion_f16, roll_from_quaternion_f32, roll_from_quaternion_f64};
|
||||
pitch_from_quaternion :: proc{pitch_from_quaternion_f16, pitch_from_quaternion_f32, pitch_from_quaternion_f64};
|
||||
yaw_from_quaternion :: proc{yaw_from_quaternion_f16, yaw_from_quaternion_f32, yaw_from_quaternion_f64};
|
||||
pitch_yaw_roll_from_quaternion :: proc{pitch_yaw_roll_from_quaternion_f16, pitch_yaw_roll_from_quaternion_f32, pitch_yaw_roll_from_quaternion_f64};
|
||||
|
||||
matrix3_from_euler_angles :: proc{matrix3_from_euler_angles_f16, matrix3_from_euler_angles_f32, matrix3_from_euler_angles_f64};
|
||||
matrix3_from_euler_angle_x :: proc{matrix3_from_euler_angle_x_f16, matrix3_from_euler_angle_x_f32, matrix3_from_euler_angle_x_f64};
|
||||
matrix3_from_euler_angle_y :: proc{matrix3_from_euler_angle_y_f16, matrix3_from_euler_angle_y_f32, matrix3_from_euler_angle_y_f64};
|
||||
matrix3_from_euler_angle_z :: proc{matrix3_from_euler_angle_z_f16, matrix3_from_euler_angle_z_f32, matrix3_from_euler_angle_z_f64};
|
||||
matrix3_from_derived_euler_angle_x :: proc{matrix3_from_derived_euler_angle_x_f16, matrix3_from_derived_euler_angle_x_f32, matrix3_from_derived_euler_angle_x_f64};
|
||||
matrix3_from_derived_euler_angle_y :: proc{matrix3_from_derived_euler_angle_y_f16, matrix3_from_derived_euler_angle_y_f32, matrix3_from_derived_euler_angle_y_f64};
|
||||
matrix3_from_derived_euler_angle_z :: proc{matrix3_from_derived_euler_angle_z_f16, matrix3_from_derived_euler_angle_z_f32, matrix3_from_derived_euler_angle_z_f64};
|
||||
matrix3_from_euler_angles_xy :: proc{matrix3_from_euler_angles_xy_f16, matrix3_from_euler_angles_xy_f32, matrix3_from_euler_angles_xy_f64};
|
||||
matrix3_from_euler_angles_yx :: proc{matrix3_from_euler_angles_yx_f16, matrix3_from_euler_angles_yx_f32, matrix3_from_euler_angles_yx_f64};
|
||||
matrix3_from_euler_angles_xz :: proc{matrix3_from_euler_angles_xz_f16, matrix3_from_euler_angles_xz_f32, matrix3_from_euler_angles_xz_f64};
|
||||
matrix3_from_euler_angles_zx :: proc{matrix3_from_euler_angles_zx_f16, matrix3_from_euler_angles_zx_f32, matrix3_from_euler_angles_zx_f64};
|
||||
matrix3_from_euler_angles_yz :: proc{matrix3_from_euler_angles_yz_f16, matrix3_from_euler_angles_yz_f32, matrix3_from_euler_angles_yz_f64};
|
||||
matrix3_from_euler_angles_zy :: proc{matrix3_from_euler_angles_zy_f16, matrix3_from_euler_angles_zy_f32, matrix3_from_euler_angles_zy_f64};
|
||||
matrix3_from_euler_angles_xyz :: proc{matrix3_from_euler_angles_xyz_f16, matrix3_from_euler_angles_xyz_f32, matrix3_from_euler_angles_xyz_f64};
|
||||
matrix3_from_euler_angles_yxz :: proc{matrix3_from_euler_angles_yxz_f16, matrix3_from_euler_angles_yxz_f32, matrix3_from_euler_angles_yxz_f64};
|
||||
matrix3_from_euler_angles_xzx :: proc{matrix3_from_euler_angles_xzx_f16, matrix3_from_euler_angles_xzx_f32, matrix3_from_euler_angles_xzx_f64};
|
||||
matrix3_from_euler_angles_xyx :: proc{matrix3_from_euler_angles_xyx_f16, matrix3_from_euler_angles_xyx_f32, matrix3_from_euler_angles_xyx_f64};
|
||||
matrix3_from_euler_angles_yxy :: proc{matrix3_from_euler_angles_yxy_f16, matrix3_from_euler_angles_yxy_f32, matrix3_from_euler_angles_yxy_f64};
|
||||
matrix3_from_euler_angles_yzy :: proc{matrix3_from_euler_angles_yzy_f16, matrix3_from_euler_angles_yzy_f32, matrix3_from_euler_angles_yzy_f64};
|
||||
matrix3_from_euler_angles_zyz :: proc{matrix3_from_euler_angles_zyz_f16, matrix3_from_euler_angles_zyz_f32, matrix3_from_euler_angles_zyz_f64};
|
||||
matrix3_from_euler_angles_zxz :: proc{matrix3_from_euler_angles_zxz_f16, matrix3_from_euler_angles_zxz_f32, matrix3_from_euler_angles_zxz_f64};
|
||||
matrix3_from_euler_angles_xzy :: proc{matrix3_from_euler_angles_xzy_f16, matrix3_from_euler_angles_xzy_f32, matrix3_from_euler_angles_xzy_f64};
|
||||
matrix3_from_euler_angles_yzx :: proc{matrix3_from_euler_angles_yzx_f16, matrix3_from_euler_angles_yzx_f32, matrix3_from_euler_angles_yzx_f64};
|
||||
matrix3_from_euler_angles_zyx :: proc{matrix3_from_euler_angles_zyx_f16, matrix3_from_euler_angles_zyx_f32, matrix3_from_euler_angles_zyx_f64};
|
||||
matrix3_from_euler_angles_zxy :: proc{matrix3_from_euler_angles_zxy_f16, matrix3_from_euler_angles_zxy_f32, matrix3_from_euler_angles_zxy_f64};
|
||||
matrix3_from_yaw_pitch_roll :: proc{matrix3_from_yaw_pitch_roll_f16, matrix3_from_yaw_pitch_roll_f32, matrix3_from_yaw_pitch_roll_f64};
|
||||
|
||||
euler_angles_from_matrix3 :: proc{euler_angles_from_matrix3_f16, euler_angles_from_matrix3_f32, euler_angles_from_matrix3_f64};
|
||||
euler_angles_xyz_from_matrix3 :: proc{euler_angles_xyz_from_matrix3_f16, euler_angles_xyz_from_matrix3_f32, euler_angles_xyz_from_matrix3_f64};
|
||||
euler_angles_yxz_from_matrix3 :: proc{euler_angles_yxz_from_matrix3_f16, euler_angles_yxz_from_matrix3_f32, euler_angles_yxz_from_matrix3_f64};
|
||||
euler_angles_xzx_from_matrix3 :: proc{euler_angles_xzx_from_matrix3_f16, euler_angles_xzx_from_matrix3_f32, euler_angles_xzx_from_matrix3_f64};
|
||||
euler_angles_xyx_from_matrix3 :: proc{euler_angles_xyx_from_matrix3_f16, euler_angles_xyx_from_matrix3_f32, euler_angles_xyx_from_matrix3_f64};
|
||||
euler_angles_yxy_from_matrix3 :: proc{euler_angles_yxy_from_matrix3_f16, euler_angles_yxy_from_matrix3_f32, euler_angles_yxy_from_matrix3_f64};
|
||||
euler_angles_yzy_from_matrix3 :: proc{euler_angles_yzy_from_matrix3_f16, euler_angles_yzy_from_matrix3_f32, euler_angles_yzy_from_matrix3_f64};
|
||||
euler_angles_zyz_from_matrix3 :: proc{euler_angles_zyz_from_matrix3_f16, euler_angles_zyz_from_matrix3_f32, euler_angles_zyz_from_matrix3_f64};
|
||||
euler_angles_zxz_from_matrix3 :: proc{euler_angles_zxz_from_matrix3_f16, euler_angles_zxz_from_matrix3_f32, euler_angles_zxz_from_matrix3_f64};
|
||||
euler_angles_xzy_from_matrix3 :: proc{euler_angles_xzy_from_matrix3_f16, euler_angles_xzy_from_matrix3_f32, euler_angles_xzy_from_matrix3_f64};
|
||||
euler_angles_yzx_from_matrix3 :: proc{euler_angles_yzx_from_matrix3_f16, euler_angles_yzx_from_matrix3_f32, euler_angles_yzx_from_matrix3_f64};
|
||||
euler_angles_zyx_from_matrix3 :: proc{euler_angles_zyx_from_matrix3_f16, euler_angles_zyx_from_matrix3_f32, euler_angles_zyx_from_matrix3_f64};
|
||||
euler_angles_zxy_from_matrix3 :: proc{euler_angles_zxy_from_matrix3_f16, euler_angles_zxy_from_matrix3_f32, euler_angles_zxy_from_matrix3_f64};
|
||||
|
||||
matrix4_from_euler_angles :: proc{matrix4_from_euler_angles_f16, matrix4_from_euler_angles_f32, matrix4_from_euler_angles_f64};
|
||||
matrix4_from_euler_angle_x :: proc{matrix4_from_euler_angle_x_f16, matrix4_from_euler_angle_x_f32, matrix4_from_euler_angle_x_f64};
|
||||
matrix4_from_euler_angle_y :: proc{matrix4_from_euler_angle_y_f16, matrix4_from_euler_angle_y_f32, matrix4_from_euler_angle_y_f64};
|
||||
matrix4_from_euler_angle_z :: proc{matrix4_from_euler_angle_z_f16, matrix4_from_euler_angle_z_f32, matrix4_from_euler_angle_z_f64};
|
||||
matrix4_from_derived_euler_angle_x :: proc{matrix4_from_derived_euler_angle_x_f16, matrix4_from_derived_euler_angle_x_f32, matrix4_from_derived_euler_angle_x_f64};
|
||||
matrix4_from_derived_euler_angle_y :: proc{matrix4_from_derived_euler_angle_y_f16, matrix4_from_derived_euler_angle_y_f32, matrix4_from_derived_euler_angle_y_f64};
|
||||
matrix4_from_derived_euler_angle_z :: proc{matrix4_from_derived_euler_angle_z_f16, matrix4_from_derived_euler_angle_z_f32, matrix4_from_derived_euler_angle_z_f64};
|
||||
matrix4_from_euler_angles_xy :: proc{matrix4_from_euler_angles_xy_f16, matrix4_from_euler_angles_xy_f32, matrix4_from_euler_angles_xy_f64};
|
||||
matrix4_from_euler_angles_yx :: proc{matrix4_from_euler_angles_yx_f16, matrix4_from_euler_angles_yx_f32, matrix4_from_euler_angles_yx_f64};
|
||||
matrix4_from_euler_angles_xz :: proc{matrix4_from_euler_angles_xz_f16, matrix4_from_euler_angles_xz_f32, matrix4_from_euler_angles_xz_f64};
|
||||
matrix4_from_euler_angles_zx :: proc{matrix4_from_euler_angles_zx_f16, matrix4_from_euler_angles_zx_f32, matrix4_from_euler_angles_zx_f64};
|
||||
matrix4_from_euler_angles_yz :: proc{matrix4_from_euler_angles_yz_f16, matrix4_from_euler_angles_yz_f32, matrix4_from_euler_angles_yz_f64};
|
||||
matrix4_from_euler_angles_zy :: proc{matrix4_from_euler_angles_zy_f16, matrix4_from_euler_angles_zy_f32, matrix4_from_euler_angles_zy_f64};
|
||||
matrix4_from_euler_angles_xyz :: proc{matrix4_from_euler_angles_xyz_f16, matrix4_from_euler_angles_xyz_f32, matrix4_from_euler_angles_xyz_f64};
|
||||
matrix4_from_euler_angles_yxz :: proc{matrix4_from_euler_angles_yxz_f16, matrix4_from_euler_angles_yxz_f32, matrix4_from_euler_angles_yxz_f64};
|
||||
matrix4_from_euler_angles_xzx :: proc{matrix4_from_euler_angles_xzx_f16, matrix4_from_euler_angles_xzx_f32, matrix4_from_euler_angles_xzx_f64};
|
||||
matrix4_from_euler_angles_xyx :: proc{matrix4_from_euler_angles_xyx_f16, matrix4_from_euler_angles_xyx_f32, matrix4_from_euler_angles_xyx_f64};
|
||||
matrix4_from_euler_angles_yxy :: proc{matrix4_from_euler_angles_yxy_f16, matrix4_from_euler_angles_yxy_f32, matrix4_from_euler_angles_yxy_f64};
|
||||
matrix4_from_euler_angles_yzy :: proc{matrix4_from_euler_angles_yzy_f16, matrix4_from_euler_angles_yzy_f32, matrix4_from_euler_angles_yzy_f64};
|
||||
matrix4_from_euler_angles_zyz :: proc{matrix4_from_euler_angles_zyz_f16, matrix4_from_euler_angles_zyz_f32, matrix4_from_euler_angles_zyz_f64};
|
||||
matrix4_from_euler_angles_zxz :: proc{matrix4_from_euler_angles_zxz_f16, matrix4_from_euler_angles_zxz_f32, matrix4_from_euler_angles_zxz_f64};
|
||||
matrix4_from_euler_angles_xzy :: proc{matrix4_from_euler_angles_xzy_f16, matrix4_from_euler_angles_xzy_f32, matrix4_from_euler_angles_xzy_f64};
|
||||
matrix4_from_euler_angles_yzx :: proc{matrix4_from_euler_angles_yzx_f16, matrix4_from_euler_angles_yzx_f32, matrix4_from_euler_angles_yzx_f64};
|
||||
matrix4_from_euler_angles_zyx :: proc{matrix4_from_euler_angles_zyx_f16, matrix4_from_euler_angles_zyx_f32, matrix4_from_euler_angles_zyx_f64};
|
||||
matrix4_from_euler_angles_zxy :: proc{matrix4_from_euler_angles_zxy_f16, matrix4_from_euler_angles_zxy_f32, matrix4_from_euler_angles_zxy_f64};
|
||||
matrix4_from_yaw_pitch_roll :: proc{matrix4_from_yaw_pitch_roll_f16, matrix4_from_yaw_pitch_roll_f32, matrix4_from_yaw_pitch_roll_f64};
|
||||
|
||||
euler_angles_from_matrix4 :: proc{euler_angles_from_matrix4_f16, euler_angles_from_matrix4_f32, euler_angles_from_matrix4_f64};
|
||||
euler_angles_xyz_from_matrix4 :: proc{euler_angles_xyz_from_matrix4_f16, euler_angles_xyz_from_matrix4_f32, euler_angles_xyz_from_matrix4_f64};
|
||||
euler_angles_yxz_from_matrix4 :: proc{euler_angles_yxz_from_matrix4_f16, euler_angles_yxz_from_matrix4_f32, euler_angles_yxz_from_matrix4_f64};
|
||||
euler_angles_xzx_from_matrix4 :: proc{euler_angles_xzx_from_matrix4_f16, euler_angles_xzx_from_matrix4_f32, euler_angles_xzx_from_matrix4_f64};
|
||||
euler_angles_xyx_from_matrix4 :: proc{euler_angles_xyx_from_matrix4_f16, euler_angles_xyx_from_matrix4_f32, euler_angles_xyx_from_matrix4_f64};
|
||||
euler_angles_yxy_from_matrix4 :: proc{euler_angles_yxy_from_matrix4_f16, euler_angles_yxy_from_matrix4_f32, euler_angles_yxy_from_matrix4_f64};
|
||||
euler_angles_yzy_from_matrix4 :: proc{euler_angles_yzy_from_matrix4_f16, euler_angles_yzy_from_matrix4_f32, euler_angles_yzy_from_matrix4_f64};
|
||||
euler_angles_zyz_from_matrix4 :: proc{euler_angles_zyz_from_matrix4_f16, euler_angles_zyz_from_matrix4_f32, euler_angles_zyz_from_matrix4_f64};
|
||||
euler_angles_zxz_from_matrix4 :: proc{euler_angles_zxz_from_matrix4_f16, euler_angles_zxz_from_matrix4_f32, euler_angles_zxz_from_matrix4_f64};
|
||||
euler_angles_xzy_from_matrix4 :: proc{euler_angles_xzy_from_matrix4_f16, euler_angles_xzy_from_matrix4_f32, euler_angles_xzy_from_matrix4_f64};
|
||||
euler_angles_yzx_from_matrix4 :: proc{euler_angles_yzx_from_matrix4_f16, euler_angles_yzx_from_matrix4_f32, euler_angles_yzx_from_matrix4_f64};
|
||||
euler_angles_zyx_from_matrix4 :: proc{euler_angles_zyx_from_matrix4_f16, euler_angles_zyx_from_matrix4_f32, euler_angles_zyx_from_matrix4_f64};
|
||||
euler_angles_zxy_from_matrix4 :: proc{euler_angles_zxy_from_matrix4_f16, euler_angles_zxy_from_matrix4_f32, euler_angles_zxy_from_matrix4_f64};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,222 @@
|
||||
package linalg
|
||||
|
||||
Scalar_Components :: enum u8 {
|
||||
x = 0,
|
||||
r = 0,
|
||||
}
|
||||
|
||||
Vector2_Components :: enum u8 {
|
||||
x = 0,
|
||||
y = 1,
|
||||
r = 0,
|
||||
g = 1,
|
||||
}
|
||||
|
||||
Vector3_Components :: enum u8 {
|
||||
x = 0,
|
||||
y = 1,
|
||||
z = 2,
|
||||
r = 0,
|
||||
g = 1,
|
||||
b = 2,
|
||||
}
|
||||
|
||||
Vector4_Components :: enum u8 {
|
||||
x = 0,
|
||||
y = 1,
|
||||
z = 2,
|
||||
w = 3,
|
||||
r = 0,
|
||||
g = 1,
|
||||
b = 2,
|
||||
a = 3,
|
||||
}
|
||||
|
||||
scalar_f32_swizzle1 :: proc(f: f32, c0: Scalar_Components) -> f32 {
|
||||
return f;
|
||||
}
|
||||
scalar_f32_swizzle2 :: proc(f: f32, c0, c1: Scalar_Components) -> Vector2f32 {
|
||||
return {f, f};
|
||||
}
|
||||
scalar_f32_swizzle3 :: proc(f: f32, c0, c1, c2: Scalar_Components) -> Vector3f32 {
|
||||
return {f, f, f};
|
||||
}
|
||||
scalar_f32_swizzle4 :: proc(f: f32, c0, c1, c2, c3: Scalar_Components) -> Vector4f32 {
|
||||
return {f, f, f, f};
|
||||
}
|
||||
|
||||
vector2f32_swizzle1 :: proc(v: Vector2f32, c0: Vector2_Components) -> f32 {
|
||||
return v[c0];
|
||||
}
|
||||
vector2f32_swizzle2 :: proc(v: Vector2f32, c0, c1: Vector2_Components) -> Vector2f32 {
|
||||
return {v[c0], v[c1]};
|
||||
}
|
||||
vector2f32_swizzle3 :: proc(v: Vector2f32, c0, c1, c2: Vector2_Components) -> Vector3f32 {
|
||||
return {v[c0], v[c1], v[c2]};
|
||||
}
|
||||
vector2f32_swizzle4 :: proc(v: Vector2f32, c0, c1, c2, c3: Vector2_Components) -> Vector4f32 {
|
||||
return {v[c0], v[c1], v[c2], v[c3]};
|
||||
}
|
||||
|
||||
|
||||
vector3f32_swizzle1 :: proc(v: Vector3f32, c0: Vector3_Components) -> f32 {
|
||||
return v[c0];
|
||||
}
|
||||
vector3f32_swizzle2 :: proc(v: Vector3f32, c0, c1: Vector3_Components) -> Vector2f32 {
|
||||
return {v[c0], v[c1]};
|
||||
}
|
||||
vector3f32_swizzle3 :: proc(v: Vector3f32, c0, c1, c2: Vector3_Components) -> Vector3f32 {
|
||||
return {v[c0], v[c1], v[c2]};
|
||||
}
|
||||
vector3f32_swizzle4 :: proc(v: Vector3f32, c0, c1, c2, c3: Vector3_Components) -> Vector4f32 {
|
||||
return {v[c0], v[c1], v[c2], v[c3]};
|
||||
}
|
||||
|
||||
vector4f32_swizzle1 :: proc(v: Vector4f32, c0: Vector4_Components) -> f32 {
|
||||
return v[c0];
|
||||
}
|
||||
vector4f32_swizzle2 :: proc(v: Vector4f32, c0, c1: Vector4_Components) -> Vector2f32 {
|
||||
return {v[c0], v[c1]};
|
||||
}
|
||||
vector4f32_swizzle3 :: proc(v: Vector4f32, c0, c1, c2: Vector4_Components) -> Vector3f32 {
|
||||
return {v[c0], v[c1], v[c2]};
|
||||
}
|
||||
vector4f32_swizzle4 :: proc(v: Vector4f32, c0, c1, c2, c3: Vector4_Components) -> Vector4f32 {
|
||||
return {v[c0], v[c1], v[c2], v[c3]};
|
||||
}
|
||||
|
||||
|
||||
scalar_f64_swizzle1 :: proc(f: f64, c0: Scalar_Components) -> f64 {
|
||||
return f;
|
||||
}
|
||||
scalar_f64_swizzle2 :: proc(f: f64, c0, c1: Scalar_Components) -> Vector2f64 {
|
||||
return {f, f};
|
||||
}
|
||||
scalar_f64_swizzle3 :: proc(f: f64, c0, c1, c2: Scalar_Components) -> Vector3f64 {
|
||||
return {f, f, f};
|
||||
}
|
||||
scalar_f64_swizzle4 :: proc(f: f64, c0, c1, c2, c3: Scalar_Components) -> Vector4f64 {
|
||||
return {f, f, f, f};
|
||||
}
|
||||
|
||||
vector2f64_swizzle1 :: proc(v: Vector2f64, c0: Vector2_Components) -> f64 {
|
||||
return v[c0];
|
||||
}
|
||||
vector2f64_swizzle2 :: proc(v: Vector2f64, c0, c1: Vector2_Components) -> Vector2f64 {
|
||||
return {v[c0], v[c1]};
|
||||
}
|
||||
vector2f64_swizzle3 :: proc(v: Vector2f64, c0, c1, c2: Vector2_Components) -> Vector3f64 {
|
||||
return {v[c0], v[c1], v[c2]};
|
||||
}
|
||||
vector2f64_swizzle4 :: proc(v: Vector2f64, c0, c1, c2, c3: Vector2_Components) -> Vector4f64 {
|
||||
return {v[c0], v[c1], v[c2], v[c3]};
|
||||
}
|
||||
|
||||
|
||||
vector3f64_swizzle1 :: proc(v: Vector3f64, c0: Vector3_Components) -> f64 {
|
||||
return v[c0];
|
||||
}
|
||||
vector3f64_swizzle2 :: proc(v: Vector3f64, c0, c1: Vector3_Components) -> Vector2f64 {
|
||||
return {v[c0], v[c1]};
|
||||
}
|
||||
vector3f64_swizzle3 :: proc(v: Vector3f64, c0, c1, c2: Vector3_Components) -> Vector3f64 {
|
||||
return {v[c0], v[c1], v[c2]};
|
||||
}
|
||||
vector3f64_swizzle4 :: proc(v: Vector3f64, c0, c1, c2, c3: Vector3_Components) -> Vector4f64 {
|
||||
return {v[c0], v[c1], v[c2], v[c3]};
|
||||
}
|
||||
|
||||
vector4f64_swizzle1 :: proc(v: Vector4f64, c0: Vector4_Components) -> f64 {
|
||||
return v[c0];
|
||||
}
|
||||
vector4f64_swizzle2 :: proc(v: Vector4f64, c0, c1: Vector4_Components) -> Vector2f64 {
|
||||
return {v[c0], v[c1]};
|
||||
}
|
||||
vector4f64_swizzle3 :: proc(v: Vector4f64, c0, c1, c2: Vector4_Components) -> Vector3f64 {
|
||||
return {v[c0], v[c1], v[c2]};
|
||||
}
|
||||
vector4f64_swizzle4 :: proc(v: Vector4f64, c0, c1, c2, c3: Vector4_Components) -> Vector4f64 {
|
||||
return {v[c0], v[c1], v[c2], v[c3]};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
scalar_swizzle :: proc{
|
||||
scalar_f32_swizzle1,
|
||||
scalar_f32_swizzle2,
|
||||
scalar_f32_swizzle3,
|
||||
scalar_f32_swizzle4,
|
||||
scalar_f64_swizzle1,
|
||||
scalar_f64_swizzle2,
|
||||
scalar_f64_swizzle3,
|
||||
scalar_f64_swizzle4,
|
||||
};
|
||||
|
||||
vector2_swizzle :: proc{
|
||||
vector2f32_swizzle1,
|
||||
vector2f32_swizzle2,
|
||||
vector2f32_swizzle3,
|
||||
vector2f32_swizzle4,
|
||||
vector2f64_swizzle1,
|
||||
vector2f64_swizzle2,
|
||||
vector2f64_swizzle3,
|
||||
vector2f64_swizzle4,
|
||||
};
|
||||
|
||||
vector3_swizzle :: proc{
|
||||
vector3f32_swizzle1,
|
||||
vector3f32_swizzle2,
|
||||
vector3f32_swizzle3,
|
||||
vector3f32_swizzle4,
|
||||
vector3f64_swizzle1,
|
||||
vector3f64_swizzle2,
|
||||
vector3f64_swizzle3,
|
||||
vector3f64_swizzle4,
|
||||
};
|
||||
|
||||
vector4_swizzle :: proc{
|
||||
vector4f32_swizzle1,
|
||||
vector4f32_swizzle2,
|
||||
vector4f32_swizzle3,
|
||||
vector4f32_swizzle4,
|
||||
vector4f64_swizzle1,
|
||||
vector4f64_swizzle2,
|
||||
vector4f64_swizzle3,
|
||||
vector4f64_swizzle4,
|
||||
};
|
||||
|
||||
swizzle :: proc{
|
||||
scalar_f32_swizzle1,
|
||||
scalar_f32_swizzle2,
|
||||
scalar_f32_swizzle3,
|
||||
scalar_f32_swizzle4,
|
||||
scalar_f64_swizzle1,
|
||||
scalar_f64_swizzle2,
|
||||
scalar_f64_swizzle3,
|
||||
scalar_f64_swizzle4,
|
||||
vector2f32_swizzle1,
|
||||
vector2f32_swizzle2,
|
||||
vector2f32_swizzle3,
|
||||
vector2f32_swizzle4,
|
||||
vector2f64_swizzle1,
|
||||
vector2f64_swizzle2,
|
||||
vector2f64_swizzle3,
|
||||
vector2f64_swizzle4,
|
||||
vector3f32_swizzle1,
|
||||
vector3f32_swizzle2,
|
||||
vector3f32_swizzle3,
|
||||
vector3f32_swizzle4,
|
||||
vector3f64_swizzle1,
|
||||
vector3f64_swizzle2,
|
||||
vector3f64_swizzle3,
|
||||
vector3f64_swizzle4,
|
||||
vector4f32_swizzle1,
|
||||
vector4f32_swizzle2,
|
||||
vector4f32_swizzle3,
|
||||
vector4f32_swizzle4,
|
||||
vector4f64_swizzle1,
|
||||
vector4f64_swizzle2,
|
||||
vector4f64_swizzle3,
|
||||
vector4f64_swizzle4,
|
||||
};
|
||||
+725
-141
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ import "core:math"
|
||||
//
|
||||
// sample = norm_float64() * std_dev + mean
|
||||
//
|
||||
norm_float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 {
|
||||
norm_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
rn :: 3.442619855899;
|
||||
|
||||
@(static)
|
||||
@@ -116,6 +116,12 @@ norm_float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 {
|
||||
0.008624485, 0.005548995, 0.0026696292,
|
||||
};
|
||||
|
||||
r := r;
|
||||
if r == nil {
|
||||
// NOTE(bill, 2020-09-07): Do this so that people can
|
||||
// enforce the global random state if necessary with `nil`
|
||||
r = &global_rand;
|
||||
}
|
||||
|
||||
for {
|
||||
j := i32(uint32(r));
|
||||
|
||||
+42
-28
@@ -6,15 +6,13 @@ Rand :: struct {
|
||||
}
|
||||
|
||||
|
||||
@(private, static)
|
||||
@(private)
|
||||
_GLOBAL_SEED_DATA := 1234567890;
|
||||
@(private, static)
|
||||
@(private)
|
||||
global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)));
|
||||
@(private, static)
|
||||
global_rand_ptr := &global_rand;
|
||||
|
||||
set_global_seed :: proc(seed: u64) {
|
||||
init(global_rand_ptr, seed);
|
||||
init(&global_rand, seed);
|
||||
}
|
||||
|
||||
create :: proc(seed: u64) -> Rand {
|
||||
@@ -32,6 +30,12 @@ init :: proc(r: ^Rand, seed: u64) {
|
||||
}
|
||||
|
||||
_random :: proc(r: ^Rand) -> u32 {
|
||||
r := r;
|
||||
if r == nil {
|
||||
// NOTE(bill, 2020-09-07): Do this so that people can
|
||||
// enforce the global random state if necessary with `nil`
|
||||
r = &global_rand;
|
||||
}
|
||||
old_state := r.state;
|
||||
r.state = old_state * 6364136223846793005 + (r.inc|1);
|
||||
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27);
|
||||
@@ -39,15 +43,15 @@ _random :: proc(r: ^Rand) -> u32 {
|
||||
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31));
|
||||
}
|
||||
|
||||
uint32 :: proc(r: ^Rand = global_rand_ptr) -> u32 { return _random(r); }
|
||||
uint32 :: proc(r: ^Rand = nil) -> u32 { return _random(r); }
|
||||
|
||||
uint64 :: proc(r: ^Rand = global_rand_ptr) -> u64 {
|
||||
uint64 :: proc(r: ^Rand = nil) -> u64 {
|
||||
a := u64(_random(r));
|
||||
b := u64(_random(r));
|
||||
return (a<<32) | b;
|
||||
}
|
||||
|
||||
uint128 :: proc(r: ^Rand = global_rand_ptr) -> u128 {
|
||||
uint128 :: proc(r: ^Rand = nil) -> u128 {
|
||||
a := u128(_random(r));
|
||||
b := u128(_random(r));
|
||||
c := u128(_random(r));
|
||||
@@ -55,12 +59,14 @@ uint128 :: proc(r: ^Rand = global_rand_ptr) -> u128 {
|
||||
return (a<<96) | (b<<64) | (c<<32) | d;
|
||||
}
|
||||
|
||||
int31 :: proc(r: ^Rand = global_rand_ptr) -> i32 { return i32(uint32(r) << 1 >> 1); }
|
||||
int63 :: proc(r: ^Rand = global_rand_ptr) -> i64 { return i64(uint64(r) << 1 >> 1); }
|
||||
int127 :: proc(r: ^Rand = global_rand_ptr) -> i128 { return i128(uint128(r) << 1 >> 1); }
|
||||
int31 :: proc(r: ^Rand = nil) -> i32 { return i32(uint32(r) << 1 >> 1); }
|
||||
int63 :: proc(r: ^Rand = nil) -> i64 { return i64(uint64(r) << 1 >> 1); }
|
||||
int127 :: proc(r: ^Rand = nil) -> i128 { return i128(uint128(r) << 1 >> 1); }
|
||||
|
||||
int31_max :: proc(n: i32, r: ^Rand = global_rand_ptr) -> i32 {
|
||||
if n <= 0 do panic("Invalid argument to int31_max");
|
||||
int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int31_max");
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int31(r) & (n-1);
|
||||
}
|
||||
@@ -72,8 +78,10 @@ int31_max :: proc(n: i32, r: ^Rand = global_rand_ptr) -> i32 {
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int63_max :: proc(n: i64, r: ^Rand = global_rand_ptr) -> i64 {
|
||||
if n <= 0 do panic("Invalid argument to int63_max");
|
||||
int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int63_max");
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int63(r) & (n-1);
|
||||
}
|
||||
@@ -85,8 +93,10 @@ int63_max :: proc(n: i64, r: ^Rand = global_rand_ptr) -> i64 {
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int127_max :: proc(n: i128, r: ^Rand = global_rand_ptr) -> i128 {
|
||||
if n <= 0 do panic("Invalid argument to int63_max");
|
||||
int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int63_max");
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int127(r) & (n-1);
|
||||
}
|
||||
@@ -98,8 +108,10 @@ int127_max :: proc(n: i128, r: ^Rand = global_rand_ptr) -> i128 {
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int_max :: proc(n: int, r: ^Rand = global_rand_ptr) -> int {
|
||||
if n <= 0 do panic("Invalid argument to int_max");
|
||||
int_max :: proc(n: int, r: ^Rand = nil) -> int {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int_max");
|
||||
}
|
||||
when size_of(int) == 4 {
|
||||
return int(int31_max(i32(n), r));
|
||||
} else {
|
||||
@@ -107,14 +119,14 @@ int_max :: proc(n: int, r: ^Rand = global_rand_ptr) -> int {
|
||||
}
|
||||
}
|
||||
|
||||
float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53); }
|
||||
float32 :: proc(r: ^Rand = global_rand_ptr) -> f32 { return f32(float64(r)); }
|
||||
float64 :: proc(r: ^Rand = nil) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53); }
|
||||
float32 :: proc(r: ^Rand = nil) -> f32 { return f32(float64(r)); }
|
||||
|
||||
float64_range :: proc(lo, hi: f64, r: ^Rand = global_rand_ptr) -> f64 { return (hi-lo)*float64(r) + lo; }
|
||||
float32_range :: proc(lo, hi: f32, r: ^Rand = global_rand_ptr) -> f32 { return (hi-lo)*float32(r) + lo; }
|
||||
float64_range :: proc(lo, hi: f64, r: ^Rand = nil) -> f64 { return (hi-lo)*float64(r) + lo; }
|
||||
float32_range :: proc(lo, hi: f32, r: ^Rand = nil) -> f32 { return (hi-lo)*float32(r) + lo; }
|
||||
|
||||
|
||||
read :: proc(p: []byte, r: ^Rand = global_rand_ptr) -> (n: int) {
|
||||
read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
|
||||
pos := i8(0);
|
||||
val := i64(0);
|
||||
for n = 0; n < len(p); n += 1 {
|
||||
@@ -130,10 +142,10 @@ read :: proc(p: []byte, r: ^Rand = global_rand_ptr) -> (n: int) {
|
||||
}
|
||||
|
||||
// perm returns a slice of n ints in a pseudo-random permutation of integers in the range [0, n)
|
||||
perm :: proc(n: int, r: ^Rand = global_rand_ptr) -> []int {
|
||||
perm :: proc(n: int, r: ^Rand = nil) -> []int {
|
||||
m := make([]int, n);
|
||||
for i := 0; i < n; i += 1 {
|
||||
j := int_max(i+1);
|
||||
j := int_max(i+1, r);
|
||||
m[i] = m[j];
|
||||
m[j] = i;
|
||||
}
|
||||
@@ -141,9 +153,11 @@ perm :: proc(n: int, r: ^Rand = global_rand_ptr) -> []int {
|
||||
}
|
||||
|
||||
|
||||
shuffle :: proc(array: $T/[]$E, r: ^Rand = global_rand_ptr) {
|
||||
shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
|
||||
n := i64(len(array));
|
||||
if n < 2 do return;
|
||||
if n < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
for i := i64(0); i < n; i += 1 {
|
||||
j := int63_max(n, r);
|
||||
|
||||
+166
-33
@@ -10,14 +10,38 @@ Allocator_Mode :: enum byte {
|
||||
Free,
|
||||
Free_All,
|
||||
Resize,
|
||||
Query_Features,
|
||||
}
|
||||
*/
|
||||
|
||||
Allocator_Mode_Set :: runtime.Allocator_Mode_Set;
|
||||
/*
|
||||
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode];
|
||||
*/
|
||||
|
||||
Allocator_Query_Info :: runtime.Allocator_Query_Info;
|
||||
/*
|
||||
Allocator_Query_Info :: struct {
|
||||
pointer: rawptr,
|
||||
size: Maybe(int),
|
||||
alignment: Maybe(int),
|
||||
}
|
||||
*/
|
||||
|
||||
Allocator_Error :: runtime.Allocator_Error;
|
||||
/*
|
||||
Allocator_Error :: enum byte {
|
||||
None = 0,
|
||||
Out_Of_Memory = 1,
|
||||
Invalid_Pointer = 2,
|
||||
Invalid_Argument = 3,
|
||||
}
|
||||
*/
|
||||
Allocator_Proc :: runtime.Allocator_Proc;
|
||||
/*
|
||||
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, location := #caller_location) -> rawptr;
|
||||
old_memory: rawptr, old_size: int, location: Source_Code_Location = #caller_location) -> ([]byte, Allocator_Error);
|
||||
*/
|
||||
|
||||
Allocator :: runtime.Allocator;
|
||||
@@ -30,37 +54,112 @@ Allocator :: struct {
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
|
||||
|
||||
alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 do return nil;
|
||||
if allocator.procedure == nil do return nil;
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, 0, loc);
|
||||
}
|
||||
|
||||
free :: inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
|
||||
if ptr == nil do return;
|
||||
if allocator.procedure == nil do return;
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
|
||||
}
|
||||
|
||||
free_all :: inline proc(allocator := context.allocator, loc := #caller_location) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, 0, loc);
|
||||
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 {
|
||||
return nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
|
||||
_ = err;
|
||||
return raw_data(data);
|
||||
}
|
||||
|
||||
resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 {
|
||||
return nil, nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil;
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
|
||||
}
|
||||
|
||||
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil {
|
||||
return nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc);
|
||||
return err;
|
||||
}
|
||||
|
||||
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if bytes == nil {
|
||||
return nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc);
|
||||
return err;
|
||||
}
|
||||
|
||||
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if allocator.procedure != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc);
|
||||
return err;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
if new_size == 0 {
|
||||
free(ptr, allocator, loc);
|
||||
if ptr != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
|
||||
}
|
||||
return nil;
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, 0, loc);
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
|
||||
_ = err;
|
||||
return nil;
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, 0, loc);
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
|
||||
_ = err;
|
||||
return raw_data(data);
|
||||
}
|
||||
|
||||
resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil;
|
||||
}
|
||||
ptr := raw_data(old_data);
|
||||
old_size := len(old_data);
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
|
||||
return nil, err;
|
||||
}
|
||||
return nil, nil;
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
|
||||
}
|
||||
|
||||
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc);
|
||||
return set;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
|
||||
props.pointer = pointer;
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(str), allocator, loc);
|
||||
@@ -76,7 +175,7 @@ delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #cal
|
||||
}
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
|
||||
raw := transmute(Raw_Map)m;
|
||||
delete_slice(raw.hashes);
|
||||
delete_slice(raw.hashes, raw.entries.allocator, loc);
|
||||
free(raw.entries.data, raw.entries.allocator, loc);
|
||||
}
|
||||
|
||||
@@ -90,28 +189,31 @@ delete :: proc{
|
||||
};
|
||||
|
||||
|
||||
new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
return new_aligned(T, align_of(T), allocator, loc);
|
||||
}
|
||||
new_aligned :: inline proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(alloc(size_of(T), alignment, allocator, loc));
|
||||
if ptr != nil do ptr^ = T{};
|
||||
if ptr != nil { ptr^ = T{}; }
|
||||
return ptr;
|
||||
}
|
||||
new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(alloc(size_of(T), align_of(T), allocator, loc));
|
||||
if ptr != nil do ptr^ = data;
|
||||
if ptr != nil { ptr^ = data; }
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
return make_aligned(T, len, align_of(E), allocator, loc);
|
||||
}
|
||||
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_slice_error_loc(loc, len);
|
||||
data := alloc(size_of(E)*len, alignment, allocator, loc);
|
||||
if data == nil do return nil;
|
||||
if data == nil && size_of(E) != 0 {
|
||||
return nil;
|
||||
}
|
||||
zero(data, size_of(E)*len);
|
||||
s := Raw_Slice{data, len};
|
||||
return transmute(T)s;
|
||||
}
|
||||
@@ -125,9 +227,10 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, a
|
||||
runtime.make_dynamic_array_error_loc(loc, len, cap);
|
||||
data := alloc(size_of(E)*cap, align_of(E), allocator, loc);
|
||||
s := Raw_Dynamic_Array{data, len, cap, allocator};
|
||||
if data == nil {
|
||||
if data == nil && size_of(E) != 0 {
|
||||
s.len, s.cap = 0, 0;
|
||||
}
|
||||
zero(data, size_of(E)*len);
|
||||
return transmute(T)s;
|
||||
}
|
||||
make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
@@ -150,20 +253,50 @@ make :: proc{
|
||||
|
||||
|
||||
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if old_memory == nil do return alloc(new_size, alignment, allocator, loc);
|
||||
if old_memory == nil {
|
||||
return alloc(new_size, alignment, allocator, loc);
|
||||
}
|
||||
|
||||
if new_size == 0 {
|
||||
free(old_memory, allocator, loc);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if new_size == old_size do return old_memory;
|
||||
if new_size == old_size {
|
||||
return old_memory;
|
||||
}
|
||||
|
||||
new_memory := alloc(new_size, alignment, allocator, loc);
|
||||
if new_memory == nil do return nil;
|
||||
if new_memory == nil {
|
||||
return nil;
|
||||
}
|
||||
|
||||
copy(new_memory, old_memory, min(old_size, new_size));;
|
||||
copy(new_memory, old_memory, min(old_size, new_size));
|
||||
free(old_memory, allocator, loc);
|
||||
return new_memory;
|
||||
}
|
||||
default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
old_memory := raw_data(old_data);
|
||||
old_size := len(old_data);
|
||||
if old_memory == nil {
|
||||
return alloc_bytes(new_size, alignment, allocator, loc);
|
||||
}
|
||||
|
||||
if new_size == 0 {
|
||||
err := free_bytes(old_data, allocator, loc);
|
||||
return nil, err;
|
||||
}
|
||||
|
||||
if new_size == old_size {
|
||||
return old_data, .None;
|
||||
}
|
||||
|
||||
new_memory, err := alloc_bytes(new_size, alignment, allocator, loc);
|
||||
if new_memory == nil || err != nil {
|
||||
return nil, err;
|
||||
}
|
||||
|
||||
runtime.copy(new_memory, old_data);
|
||||
free_bytes(old_data, allocator, loc);
|
||||
return new_memory, err;
|
||||
}
|
||||
|
||||
+523
-148
@@ -1,9 +1,12 @@
|
||||
package mem
|
||||
|
||||
import "intrinsics"
|
||||
import "core:runtime"
|
||||
|
||||
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
return nil;
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
nil_allocator :: proc() -> Allocator {
|
||||
@@ -44,7 +47,7 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
arena := cast(^Arena)allocator_data;
|
||||
|
||||
switch mode {
|
||||
@@ -52,7 +55,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
total_size := size + alignment;
|
||||
|
||||
if arena.offset + total_size > len(arena.data) {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
|
||||
#no_bounds_check end := &arena.data[arena.offset];
|
||||
@@ -60,7 +63,8 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
ptr := align_forward(end, uintptr(alignment));
|
||||
arena.offset += total_size;
|
||||
arena.peak_used = max(arena.peak_used, arena.offset);
|
||||
return zero(ptr, size);
|
||||
zero(ptr, size);
|
||||
return byte_slice(ptr, size), nil;
|
||||
|
||||
case .Free:
|
||||
// NOTE(bill): Free all at once
|
||||
@@ -70,10 +74,20 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
arena.offset = 0;
|
||||
|
||||
case .Resize:
|
||||
return default_resize_align(old_memory, old_size, size, alignment, arena_allocator(arena));
|
||||
return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena));
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
||||
@@ -94,121 +108,177 @@ end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
|
||||
|
||||
Scratch_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_offset: int,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic]rawptr,
|
||||
default_to_default_allocator: bool,
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_allocation: rawptr,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic][]byte,
|
||||
}
|
||||
|
||||
scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup_allocator := context.allocator) {
|
||||
scratch.data = data;
|
||||
scratch.curr_offset = 0;
|
||||
scratch.prev_offset = 0;
|
||||
scratch.backup_allocator = backup_allocator;
|
||||
scratch_allocator_init :: proc(s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) {
|
||||
s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator);
|
||||
s.curr_offset = 0;
|
||||
s.prev_allocation = nil;
|
||||
s.backup_allocator = backup_allocator;
|
||||
s.leaked_allocations.allocator = backup_allocator;
|
||||
}
|
||||
|
||||
scratch_allocator_destroy :: proc(using scratch: ^Scratch_Allocator) {
|
||||
if scratch == nil {
|
||||
scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) {
|
||||
if s == nil {
|
||||
return;
|
||||
}
|
||||
for ptr in leaked_allocations {
|
||||
free(ptr, backup_allocator);
|
||||
for ptr in s.leaked_allocations {
|
||||
free_bytes(ptr, s.backup_allocator);
|
||||
}
|
||||
delete(leaked_allocations);
|
||||
delete(data, backup_allocator);
|
||||
scratch^ = {};
|
||||
delete(s.leaked_allocations);
|
||||
delete(s.data, s.backup_allocator);
|
||||
s^ = {};
|
||||
}
|
||||
|
||||
scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
|
||||
scratch := (^Scratch_Allocator)(allocator_data);
|
||||
s := (^Scratch_Allocator)(allocator_data);
|
||||
|
||||
if scratch.data == nil {
|
||||
DEFAULT_SCRATCH_BACKING_SIZE :: 1<<22;
|
||||
if s.data == nil {
|
||||
DEFAULT_BACKING_SIZE :: 1<<22;
|
||||
if !(context.allocator.procedure != scratch_allocator_proc &&
|
||||
context.allocator.data != allocator_data) {
|
||||
panic("cyclic initialization of the scratch allocator with itself");
|
||||
}
|
||||
scratch_allocator_init(scratch, make([]byte, 1<<22));
|
||||
scratch_allocator_init(s, DEFAULT_BACKING_SIZE);
|
||||
}
|
||||
|
||||
size := size;
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
size = align_forward_int(size, alignment);
|
||||
|
||||
switch {
|
||||
case scratch.curr_offset+size <= len(scratch.data):
|
||||
offset := align_forward_uintptr(uintptr(scratch.curr_offset), uintptr(alignment));
|
||||
ptr := &scratch.data[offset];
|
||||
zero(ptr, size);
|
||||
scratch.prev_offset = int(offset);
|
||||
scratch.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
case size <= len(scratch.data):
|
||||
offset := align_forward_uintptr(uintptr(0), uintptr(alignment));
|
||||
ptr := &scratch.data[offset];
|
||||
zero(ptr, size);
|
||||
scratch.prev_offset = int(offset);
|
||||
scratch.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
case s.curr_offset+size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data));
|
||||
ptr := start + uintptr(s.curr_offset);
|
||||
ptr = align_forward_uintptr(ptr, uintptr(alignment));
|
||||
zero(rawptr(ptr), size);
|
||||
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return byte_slice(rawptr(ptr), size), nil;
|
||||
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data));
|
||||
ptr := align_forward_uintptr(start, uintptr(alignment));
|
||||
zero(rawptr(ptr), size);
|
||||
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return byte_slice(rawptr(ptr), size), nil;
|
||||
}
|
||||
// TODO(bill): Should leaks be notified about? Should probably use a logging system that is built into the context system
|
||||
a := scratch.backup_allocator;
|
||||
a := s.backup_allocator;
|
||||
if a.procedure == nil {
|
||||
a = context.allocator;
|
||||
scratch.backup_allocator = a;
|
||||
s.backup_allocator = a;
|
||||
}
|
||||
|
||||
ptr := alloc(size, alignment, a, loc);
|
||||
if scratch.leaked_allocations == nil {
|
||||
scratch.leaked_allocations = make([dynamic]rawptr, a);
|
||||
ptr, err := alloc_bytes(size, alignment, a, loc);
|
||||
if err != nil {
|
||||
return ptr, err;
|
||||
}
|
||||
append(&scratch.leaked_allocations, ptr);
|
||||
if s.leaked_allocations == nil {
|
||||
s.leaked_allocations = make([dynamic][]byte, a);
|
||||
}
|
||||
append(&s.leaked_allocations, ptr);
|
||||
|
||||
return ptr;
|
||||
if logger := context.logger; logger.lowest_level <= .Warning {
|
||||
if logger.procedure != nil {
|
||||
logger.procedure(logger.data, .Warning, "mem.Scratch_Allocator resorted to backup_allocator" , logger.options, loc);
|
||||
}
|
||||
}
|
||||
|
||||
return ptr, err;
|
||||
|
||||
case .Free:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr {
|
||||
full_size := scratch.curr_offset - scratch.prev_offset;
|
||||
scratch.curr_offset = scratch.prev_offset;
|
||||
zero(last_ptr, full_size);
|
||||
return nil;
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
old_ptr := uintptr(old_memory);
|
||||
|
||||
if s.prev_allocation == old_memory {
|
||||
s.curr_offset = int(uintptr(s.prev_allocation) - start);
|
||||
s.prev_allocation = nil;
|
||||
return nil, nil;
|
||||
}
|
||||
// NOTE(bill): It's scratch memory, don't worry about freeing
|
||||
|
||||
if start <= old_ptr && old_ptr < end {
|
||||
// NOTE(bill): Cannot free this pointer but it is valid
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
if len(s.leaked_allocations) != 0 {
|
||||
for data, i in s.leaked_allocations {
|
||||
ptr := raw_data(data);
|
||||
if ptr == old_memory {
|
||||
free_bytes(data, s.backup_allocator);
|
||||
ordered_remove(&s.leaked_allocations, i);
|
||||
return nil, nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, .Invalid_Pointer;
|
||||
// panic("invalid pointer passed to default_temp_allocator");
|
||||
|
||||
case .Free_All:
|
||||
scratch.curr_offset = 0;
|
||||
scratch.prev_offset = 0;
|
||||
for ptr in scratch.leaked_allocations {
|
||||
free(ptr, scratch.backup_allocator);
|
||||
s.curr_offset = 0;
|
||||
s.prev_allocation = nil;
|
||||
for ptr in s.leaked_allocations {
|
||||
free_bytes(ptr, s.backup_allocator);
|
||||
}
|
||||
clear(&scratch.leaked_allocations);
|
||||
clear(&s.leaked_allocations);
|
||||
|
||||
case .Resize:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr && len(scratch.data)-scratch.prev_offset >= size {
|
||||
scratch.curr_offset = scratch.prev_offset+size;
|
||||
return old_memory;
|
||||
begin := uintptr(raw_data(s.data));
|
||||
end := begin + uintptr(len(s.data));
|
||||
old_ptr := uintptr(old_memory);
|
||||
if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
|
||||
s.curr_offset = int(old_ptr-begin)+size;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
return scratch_allocator_proc(allocator_data, Allocator_Mode.Alloc, size, alignment, old_memory, old_size, flags, loc);
|
||||
data, err := scratch_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc);
|
||||
if err != nil {
|
||||
return data, err;
|
||||
}
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
_, err = scratch_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, loc);
|
||||
return data, err;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
scratch_allocator :: proc(scratch: ^Scratch_Allocator) -> Allocator {
|
||||
scratch_allocator :: proc(allocator: ^Scratch_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = scratch_allocator_proc,
|
||||
data = scratch,
|
||||
data = allocator,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Stack_Allocation_Header :: struct {
|
||||
prev_offset: int,
|
||||
padding: int,
|
||||
@@ -239,32 +309,33 @@ stack_allocator :: proc(stack: ^Stack) -> Allocator {
|
||||
|
||||
stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
s := cast(^Stack)allocator_data;
|
||||
|
||||
if s.data == nil {
|
||||
return nil;
|
||||
return nil, .Invalid_Argument;
|
||||
}
|
||||
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> rawptr {
|
||||
curr_addr := uintptr(&s.data[0]) + uintptr(s.curr_offset);
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset);
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header));
|
||||
if s.curr_offset + padding + size > len(s.data) {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
s.prev_offset = s.curr_offset;
|
||||
s.curr_offset += padding;
|
||||
|
||||
next_addr := curr_addr + uintptr(padding);
|
||||
header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header));
|
||||
header.padding = auto_cast padding;
|
||||
header.prev_offset = auto_cast s.prev_offset;
|
||||
header.padding = padding;
|
||||
header.prev_offset = s.prev_offset;
|
||||
|
||||
s.curr_offset += size;
|
||||
|
||||
s.peak_used = max(s.peak_used, s.curr_offset);
|
||||
|
||||
return zero(rawptr(next_addr), size);
|
||||
zero(rawptr(next_addr), size);
|
||||
return byte_slice(rawptr(next_addr), size), nil;
|
||||
}
|
||||
|
||||
switch mode {
|
||||
@@ -272,33 +343,31 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, alignment);
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
start := uintptr(&s.data[0]);
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (free)");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.curr_offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(&s.data[0]));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||
|
||||
if old_offset != int(header.prev_offset) {
|
||||
panic("Out of order stack allocator free");
|
||||
return nil;
|
||||
if old_offset != header.prev_offset {
|
||||
// panic("Out of order stack allocator free");
|
||||
return nil, .Invalid_Pointer;
|
||||
}
|
||||
|
||||
s.curr_offset = int(old_offset);
|
||||
s.prev_offset = int(header.prev_offset);
|
||||
|
||||
s.curr_offset = old_offset;
|
||||
s.prev_offset = header.prev_offset;
|
||||
|
||||
case .Free_All:
|
||||
s.prev_offset = 0;
|
||||
@@ -309,33 +378,34 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, alignment);
|
||||
}
|
||||
if size == 0 {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
start := uintptr(&s.data[0]);
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (resize)");
|
||||
return nil;
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.curr_offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
if old_size == size {
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
|
||||
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(&s.data[0]));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||
|
||||
if old_offset != int(header.prev_offset) {
|
||||
ptr := raw_alloc(s, size, alignment);
|
||||
copy(ptr, old_memory, min(old_size, size));
|
||||
return ptr;
|
||||
if old_offset != header.prev_offset {
|
||||
data, err := raw_alloc(s, size, alignment);
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
}
|
||||
return data, err;
|
||||
}
|
||||
|
||||
old_memory_size := uintptr(s.curr_offset) - (curr_addr - start);
|
||||
@@ -347,10 +417,19 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
zero(rawptr(curr_addr + uintptr(diff)), diff);
|
||||
}
|
||||
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return nil, nil;
|
||||
case .Query_Info:
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -385,20 +464,20 @@ small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
|
||||
|
||||
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, ocation := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
s := cast(^Small_Stack)allocator_data;
|
||||
|
||||
if s.data == nil {
|
||||
return nil;
|
||||
return nil, .Invalid_Argument;
|
||||
}
|
||||
|
||||
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
|
||||
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> rawptr {
|
||||
curr_addr := uintptr(&s.data[0]) + uintptr(s.offset);
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset);
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header));
|
||||
if s.offset + padding + size > len(s.data) {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
s.offset += padding;
|
||||
|
||||
@@ -410,7 +489,8 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.offset);
|
||||
|
||||
return zero(rawptr(next_addr), size);
|
||||
zero(rawptr(next_addr), size);
|
||||
return byte_slice(rawptr(next_addr), size), nil;
|
||||
}
|
||||
|
||||
switch mode {
|
||||
@@ -418,26 +498,26 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, align);
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
start := uintptr(&s.data[0]);
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (free)");
|
||||
return nil;
|
||||
// panic("Out of bounds memory address passed to stack allocator (free)");
|
||||
return nil, .Invalid_Pointer;
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(&s.data[0]));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||
|
||||
s.offset = int(old_offset);
|
||||
s.offset = old_offset;
|
||||
|
||||
case .Free_All:
|
||||
s.offset = 0;
|
||||
@@ -447,32 +527,44 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, align);
|
||||
}
|
||||
if size == 0 {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
start := uintptr(&s.data[0]);
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (resize)");
|
||||
return nil;
|
||||
// panic("Out of bounds memory address passed to stack allocator (resize)");
|
||||
return nil, .Invalid_Pointer;
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.offset) {
|
||||
// NOTE(bill): Treat as a double free
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
if old_size == size {
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
|
||||
ptr := raw_alloc(s, size, align);
|
||||
copy(ptr, old_memory, min(old_size, size));
|
||||
return ptr;
|
||||
data, err := raw_alloc(s, size, align);
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
}
|
||||
return data, err;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -503,27 +595,44 @@ DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554;
|
||||
|
||||
dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
pool := (^Dynamic_Pool)(allocator_data);
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return dynamic_pool_alloc(pool, size);
|
||||
return dynamic_pool_alloc_bytes(pool, size);
|
||||
case .Free:
|
||||
panic("Allocator_Mode.Free is not supported for a pool");
|
||||
return nil, nil;
|
||||
case .Free_All:
|
||||
dynamic_pool_free_all(pool);
|
||||
return nil, nil;
|
||||
case .Resize:
|
||||
panic("Allocator_Mode.Resize is not supported for a pool");
|
||||
if old_size >= size {
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
ptr := dynamic_pool_alloc(pool, size);
|
||||
copy(ptr, old_memory, old_size);
|
||||
return ptr;
|
||||
data, err := dynamic_pool_alloc_bytes(pool, size);
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
}
|
||||
return data, err;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info};
|
||||
}
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
info := (^Allocator_Query_Info)(old_memory);
|
||||
if info != nil && info.pointer != nil {
|
||||
info.size = pool.block_size;
|
||||
info.alignment = pool.alignment;
|
||||
return byte_slice(info, size_of(info^)), nil;
|
||||
}
|
||||
return nil, nil;
|
||||
}
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -558,8 +667,14 @@ dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
|
||||
}
|
||||
|
||||
|
||||
dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
cycle_new_block :: proc(using pool: ^Dynamic_Pool) {
|
||||
dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
data, err := dynamic_pool_alloc_bytes(pool, bytes);
|
||||
assert(err == nil);
|
||||
return raw_data(data);
|
||||
}
|
||||
|
||||
dynamic_pool_alloc_bytes :: proc(using pool: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
|
||||
cycle_new_block :: proc(using pool: ^Dynamic_Pool) -> (err: Allocator_Error) {
|
||||
if block_allocator.procedure == nil {
|
||||
panic("You must call pool_init on a Pool before using it");
|
||||
}
|
||||
@@ -572,42 +687,47 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
if len(unused_blocks) > 0 {
|
||||
new_block = pop(&unused_blocks);
|
||||
} else {
|
||||
new_block = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
data: []byte;
|
||||
data, err = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
new_block = raw_data(data);
|
||||
}
|
||||
|
||||
bytes_left = block_size;
|
||||
current_pos = new_block;
|
||||
current_block = new_block;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
n := bytes;
|
||||
extra := alignment - (n % alignment);
|
||||
n += extra;
|
||||
if n >= out_band_size {
|
||||
assert(block_allocator.procedure != nil);
|
||||
memory := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
memory, err := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
if memory != nil {
|
||||
append(&out_band_allocations, (^byte)(memory));
|
||||
append(&out_band_allocations, raw_data(memory));
|
||||
}
|
||||
return memory;
|
||||
return memory, err;
|
||||
}
|
||||
|
||||
if bytes_left < n {
|
||||
cycle_new_block(pool);
|
||||
err := cycle_new_block(pool);
|
||||
if err != nil {
|
||||
return nil, err;
|
||||
}
|
||||
if current_block == nil {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
}
|
||||
|
||||
memory := current_pos;
|
||||
current_pos = ptr_offset((^byte)(current_pos), n);
|
||||
bytes_left -= n;
|
||||
return memory;
|
||||
return byte_slice(memory, bytes), nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -636,3 +756,258 @@ dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
|
||||
}
|
||||
clear(&unused_blocks);
|
||||
}
|
||||
|
||||
|
||||
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Alloc called");
|
||||
}
|
||||
case .Resize:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Resize called");
|
||||
}
|
||||
case .Free:
|
||||
if old_memory != nil {
|
||||
panic("mem: panic allocator, .Free called");
|
||||
}
|
||||
case .Free_All:
|
||||
panic("mem: panic allocator, .Free_All called");
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Query_Features};
|
||||
}
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
panic_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = panic_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Tracking_Allocator_Entry :: struct {
|
||||
memory: rawptr,
|
||||
size: int,
|
||||
alignment: int,
|
||||
err: Allocator_Error,
|
||||
location: runtime.Source_Code_Location,
|
||||
}
|
||||
Tracking_Allocator_Bad_Free_Entry :: struct {
|
||||
memory: rawptr,
|
||||
location: runtime.Source_Code_Location,
|
||||
}
|
||||
Tracking_Allocator :: struct {
|
||||
backing: Allocator,
|
||||
allocation_map: map[rawptr]Tracking_Allocator_Entry,
|
||||
bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
|
||||
clear_on_free_all: bool,
|
||||
}
|
||||
|
||||
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
|
||||
t.backing = backing_allocator;
|
||||
t.allocation_map.allocator = internals_allocator;
|
||||
t.bad_free_array.allocator = internals_allocator;
|
||||
}
|
||||
|
||||
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
delete(t.allocation_map);
|
||||
delete(t.bad_free_array);
|
||||
}
|
||||
|
||||
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
data = data,
|
||||
procedure = tracking_allocator_proc,
|
||||
};
|
||||
}
|
||||
|
||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
data := (^Tracking_Allocator)(allocator_data);
|
||||
if mode == .Query_Info {
|
||||
info := (^Allocator_Query_Info)(old_memory);
|
||||
if info != nil && info.pointer != nil {
|
||||
if entry, ok := data.allocation_map[info.pointer]; ok {
|
||||
info.size = entry.size;
|
||||
info.alignment = entry.alignment;
|
||||
}
|
||||
info.pointer = nil;
|
||||
}
|
||||
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
result: []byte;
|
||||
err: Allocator_Error;
|
||||
if mode == .Free && old_memory not_in data.allocation_map {
|
||||
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
||||
memory = old_memory,
|
||||
location = loc,
|
||||
});
|
||||
} else {
|
||||
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc);
|
||||
if err != nil {
|
||||
return result, err;
|
||||
}
|
||||
}
|
||||
result_ptr := raw_data(result);
|
||||
|
||||
if data.allocation_map.allocator.procedure == nil {
|
||||
data.allocation_map.allocator = context.allocator;
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
};
|
||||
case .Free:
|
||||
delete_key(&data.allocation_map, old_memory);
|
||||
case .Resize:
|
||||
if old_memory != result_ptr {
|
||||
delete_key(&data.allocation_map, old_memory);
|
||||
}
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
};
|
||||
|
||||
case .Free_All:
|
||||
if data.clear_on_free_all {
|
||||
clear_map(&data.allocation_map);
|
||||
}
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info};
|
||||
}
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return result, err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Small_Allocator primary allocates memory from its local buffer of size BUFFER_SIZE
|
||||
// If that buffer's memory is exhausted, it will use the backing allocator (a scratch allocator is recommended)
|
||||
// Memory allocated with Small_Allocator cannot be freed individually using 'free' and must be freed using 'free_all'
|
||||
Small_Allocator :: struct($BUFFER_SIZE: int)
|
||||
where
|
||||
BUFFER_SIZE >= 2*size_of(uintptr),
|
||||
BUFFER_SIZE & (BUFFER_SIZE-1) == 0 {
|
||||
|
||||
buffer: [BUFFER_SIZE]byte,
|
||||
backing: Allocator,
|
||||
start: uintptr,
|
||||
curr: uintptr,
|
||||
end: uintptr,
|
||||
chunk_size: int,
|
||||
}
|
||||
|
||||
small_allocator :: proc(s: ^$S/Small_Allocator, backing := context.allocator) -> (a: Allocator) {
|
||||
if s.backing.procedure == nil {
|
||||
s.backing = backing;
|
||||
}
|
||||
a.data = s;
|
||||
a.procedure = proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
s := (^S)(allocator_data);
|
||||
if s.chunk_size <= 0 {
|
||||
s.chunk_size = 4*1024;
|
||||
}
|
||||
if s.start == 0 {
|
||||
s.start = uintptr(&s.buffer[0]);
|
||||
s.curr = s.start;
|
||||
s.end = s.start + uintptr(S.BUFFER_SIZE);
|
||||
(^rawptr)(s.start)^ = nil;
|
||||
s.curr += size_of(rawptr);
|
||||
}
|
||||
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
s.curr = align_forward_uintptr(s.curr, uintptr(alignment));
|
||||
if size > int(s.end - s.curr) {
|
||||
to_allocate := size_of(rawptr) + size + alignment;
|
||||
if to_allocate < s.chunk_size {
|
||||
to_allocate = s.chunk_size;
|
||||
}
|
||||
s.chunk_size *= 2;
|
||||
|
||||
p := alloc(to_allocate, 16, s.backing, loc);
|
||||
(^rawptr)(s.start)^ = p;
|
||||
s.start = uintptr(p);
|
||||
s.curr = s.start;
|
||||
s.end = s.start + uintptr(to_allocate);
|
||||
|
||||
(^rawptr)(s.start)^ = nil;
|
||||
s.curr += size_of(rawptr);
|
||||
s.curr = align_forward_uintptr(s.curr, uintptr(alignment));
|
||||
}
|
||||
|
||||
p := rawptr(s.curr);
|
||||
s.curr += uintptr(size);
|
||||
return mem_zero(p, size);
|
||||
|
||||
case .Free:
|
||||
// NOP
|
||||
return nil;
|
||||
|
||||
case .Resize:
|
||||
// No need copying the code
|
||||
return default_resize_align(old_memory, old_size, size, alignment, small_allocator(s, s.backing), loc);
|
||||
|
||||
case .Free_All:
|
||||
p := (^rawptr)(&s.buffer[0])^;
|
||||
for p != nil {
|
||||
next := (^rawptr)(p)^;
|
||||
free(next, s.backing, loc);
|
||||
p = next;
|
||||
}
|
||||
// Reset to default
|
||||
s.start = uintptr(&s.buffer[0]);
|
||||
s.curr = s.start;
|
||||
s.end = s.start + uintptr(S.BUFFER_SIZE);
|
||||
|
||||
(^rawptr)(s.start)^ = nil;
|
||||
s.curr += size_of(rawptr);
|
||||
|
||||
|
||||
case .Query_Features:
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil, nil;
|
||||
};
|
||||
return a;
|
||||
}
|
||||
|
||||
+133
-72
@@ -1,53 +1,50 @@
|
||||
package mem
|
||||
|
||||
import "core:runtime"
|
||||
import "core:intrinsics"
|
||||
|
||||
foreign _ {
|
||||
@(link_name = "llvm.bswap.i16") swap16 :: proc(b: u16) -> u16 ---;
|
||||
@(link_name = "llvm.bswap.i32") swap32 :: proc(b: u32) -> u32 ---;
|
||||
@(link_name = "llvm.bswap.i64") swap64 :: proc(b: u64) -> u64 ---;
|
||||
set :: proc(data: rawptr, value: byte, len: int) -> rawptr {
|
||||
return runtime.memset(data, i32(value), len);
|
||||
}
|
||||
swap :: proc{swap16, swap32, swap64};
|
||||
|
||||
|
||||
|
||||
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
|
||||
foreign _ {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memset.p0i8.i64")
|
||||
llvm_memset :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memset.p0i8.i32")
|
||||
llvm_memset :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) ---;
|
||||
}
|
||||
}
|
||||
|
||||
llvm_memset(data, value, len, 1, false);
|
||||
return data;
|
||||
}
|
||||
zero :: inline proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
zero :: proc(data: rawptr, len: int) -> rawptr {
|
||||
return set(data, 0, len);
|
||||
}
|
||||
zero_item :: inline proc "contextless" (item: $P/^$T) {
|
||||
zero_item :: proc(item: $P/^$T) {
|
||||
set(item, 0, size_of(T));
|
||||
}
|
||||
zero_slice :: proc "contextless" (data: $T/[]$E) {
|
||||
if n := len(data); n > 0 {
|
||||
zero(&data[0], size_of(E)*n);
|
||||
}
|
||||
zero_slice :: proc(data: $T/[]$E) {
|
||||
zero(raw_data(data), size_of(E)*len(data));
|
||||
}
|
||||
|
||||
|
||||
copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
copy :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
return runtime.mem_copy(dst, src, len);
|
||||
}
|
||||
copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
return runtime.mem_copy_non_overlapping(dst, src, len);
|
||||
}
|
||||
compare :: inline proc "contextless" (a, b: []byte) -> int {
|
||||
return compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
|
||||
compare :: proc(a, b: []byte) -> int {
|
||||
res := compare_byte_ptrs(raw_data(a), raw_data(b), min(len(a), len(b)));
|
||||
if res == 0 && len(a) != len(b) {
|
||||
return len(a) <= len(b) ? -1 : +1;
|
||||
} else if len(a) == 0 && len(b) == 0 {
|
||||
return 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
|
||||
|
||||
compare_byte_ptrs :: proc(a, b: ^byte, n: int) -> int #no_bounds_check {
|
||||
switch {
|
||||
case a == b:
|
||||
return 0;
|
||||
case a == nil:
|
||||
return -1;
|
||||
case b == nil:
|
||||
return -1;
|
||||
case n == 0:
|
||||
return 0;
|
||||
}
|
||||
|
||||
x := slice_ptr(a, n);
|
||||
y := slice_ptr(b, n);
|
||||
|
||||
@@ -81,32 +78,83 @@ compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_
|
||||
return 0;
|
||||
}
|
||||
|
||||
compare_ptrs :: inline proc "contextless" (a, b: rawptr, n: int) -> int {
|
||||
check_zero :: proc(data: []byte) -> bool {
|
||||
return check_zero_ptr(raw_data(data), len(data));
|
||||
}
|
||||
|
||||
check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool {
|
||||
switch {
|
||||
case len <= 0:
|
||||
return true;
|
||||
case ptr == nil:
|
||||
return true;
|
||||
}
|
||||
|
||||
start := uintptr(ptr);
|
||||
start_aligned := align_forward_uintptr(start, align_of(uintptr));
|
||||
end := start + uintptr(len);
|
||||
end_aligned := align_backward_uintptr(end, align_of(uintptr));
|
||||
|
||||
for b in start..<start_aligned {
|
||||
if (^byte)(b)^ != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for b := start_aligned; b < end_aligned; b += size_of(uintptr) {
|
||||
if (^uintptr)(b)^ != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for b in end_aligned..<end {
|
||||
if (^byte)(b)^ != 0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
simple_equal :: proc(a, b: $T) -> bool where intrinsics.type_is_simple_compare(T) {
|
||||
a, b := a, b;
|
||||
return compare_byte_ptrs((^byte)(&a), (^byte)(&b), size_of(T)) == 0;
|
||||
}
|
||||
|
||||
compare_ptrs :: proc(a, b: rawptr, n: int) -> int {
|
||||
return compare_byte_ptrs((^byte)(a), (^byte)(b), n);
|
||||
}
|
||||
|
||||
ptr_offset :: inline proc "contextless" (ptr: $P/^$T, n: int) -> P {
|
||||
ptr_offset :: proc(ptr: $P/^$T, n: int) -> P {
|
||||
new := int(uintptr(ptr)) + size_of(T)*n;
|
||||
return P(uintptr(new));
|
||||
}
|
||||
|
||||
ptr_sub :: inline proc "contextless" (a, b: $P/^$T) -> int {
|
||||
ptr_sub :: proc(a, b: $P/^$T) -> int {
|
||||
return (int(uintptr(a)) - int(uintptr(b)))/size_of(T);
|
||||
}
|
||||
|
||||
slice_ptr :: inline proc "contextless" (ptr: ^$T, len: int) -> []T {
|
||||
slice_ptr :: proc(ptr: ^$T, len: int) -> []T {
|
||||
assert(len >= 0);
|
||||
slice := Raw_Slice{data = ptr, len = len};
|
||||
return transmute([]T)slice;
|
||||
return transmute([]T)Raw_Slice{data = ptr, len = len};
|
||||
}
|
||||
|
||||
slice_to_bytes :: inline proc "contextless" (slice: $E/[]$T) -> []byte {
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
||||
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
|
||||
}
|
||||
@(deprecated="use byte_slice")
|
||||
slice_ptr_to_bytes :: proc(data: rawptr, len: int) -> []byte {
|
||||
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
|
||||
}
|
||||
|
||||
|
||||
slice_to_bytes :: proc(slice: $E/[]$T) -> []byte {
|
||||
s := transmute(Raw_Slice)slice;
|
||||
s.len *= size_of(T);
|
||||
return transmute([]byte)s;
|
||||
}
|
||||
|
||||
slice_data_cast :: inline proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T {
|
||||
slice_data_cast :: proc($T: typeid/[]$A, slice: $S/[]$B) -> T {
|
||||
when size_of(A) == 0 || size_of(B) == 0 {
|
||||
return nil;
|
||||
} else {
|
||||
@@ -116,41 +164,45 @@ slice_data_cast :: inline proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E {
|
||||
s := transmute(Raw_Slice)backing;
|
||||
d := Raw_Dynamic_Array{
|
||||
data = s.data,
|
||||
len = 0,
|
||||
cap = s.len,
|
||||
allocator = nil_allocator(),
|
||||
};
|
||||
return transmute([dynamic]E)d;
|
||||
slice_to_components :: proc(slice: $E/[]$T) -> (data: ^T, len: int) {
|
||||
s := transmute(Raw_Slice)slice;
|
||||
return s.data, s.len;
|
||||
}
|
||||
|
||||
ptr_to_bytes :: inline proc "contextless" (ptr: ^$T, len := 1) -> []byte {
|
||||
buffer_from_slice :: proc(backing: $T/[]$E) -> [dynamic]E {
|
||||
return transmute([dynamic]E)Raw_Dynamic_Array{
|
||||
data = raw_data(backing),
|
||||
len = 0,
|
||||
cap = len(backing),
|
||||
allocator = nil_allocator(),
|
||||
};
|
||||
}
|
||||
|
||||
ptr_to_bytes :: proc(ptr: ^$T, len := 1) -> []byte {
|
||||
assert(len >= 0);
|
||||
return transmute([]byte)Raw_Slice{ptr, len*size_of(T)};
|
||||
}
|
||||
|
||||
any_to_bytes :: inline proc "contextless" (val: any) -> []byte {
|
||||
any_to_bytes :: proc(val: any) -> []byte {
|
||||
ti := type_info_of(val.id);
|
||||
size := ti != nil ? ti.size : 0;
|
||||
return transmute([]byte)Raw_Slice{val.data, size};
|
||||
}
|
||||
|
||||
|
||||
kilobytes :: inline proc "contextless" (x: int) -> int do return (x) * 1024;
|
||||
megabytes :: inline proc "contextless" (x: int) -> int do return kilobytes(x) * 1024;
|
||||
gigabytes :: inline proc "contextless" (x: int) -> int do return megabytes(x) * 1024;
|
||||
terabytes :: inline proc "contextless" (x: int) -> int do return gigabytes(x) * 1024;
|
||||
kilobytes :: proc(x: int) -> int { return (x) * 1024; }
|
||||
megabytes :: proc(x: int) -> int { return kilobytes(x) * 1024; }
|
||||
gigabytes :: proc(x: int) -> int { return megabytes(x) * 1024; }
|
||||
terabytes :: proc(x: int) -> int { return gigabytes(x) * 1024; }
|
||||
|
||||
is_power_of_two :: inline proc(x: uintptr) -> bool {
|
||||
if x <= 0 do return false;
|
||||
is_power_of_two :: proc(x: uintptr) -> bool {
|
||||
if x <= 0 {
|
||||
return false;
|
||||
}
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
return rawptr(align_forward_uintptr(uintptr(ptr), align));
|
||||
}
|
||||
|
||||
@@ -159,32 +211,32 @@ align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
|
||||
|
||||
p := ptr;
|
||||
modulo := p & (align-1);
|
||||
if modulo != 0 do p += align - modulo;
|
||||
if modulo != 0 {
|
||||
p += align - modulo;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
align_forward_int :: inline proc(ptr, align: int) -> int {
|
||||
align_forward_int :: proc(ptr, align: int) -> int {
|
||||
return int(align_forward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
align_forward_uint :: inline proc(ptr, align: uint) -> uint {
|
||||
align_forward_uint :: proc(ptr, align: uint) -> uint {
|
||||
return uint(align_forward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
|
||||
align_backward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
align_backward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
return rawptr(align_backward_uintptr(uintptr(ptr), align));
|
||||
}
|
||||
|
||||
align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
ptr := rawptr(ptr - align);
|
||||
return uintptr(align_forward(ptr, align));
|
||||
return align_forward_uintptr(ptr - align + 1, align);
|
||||
}
|
||||
|
||||
align_backward_int :: inline proc(ptr, align: int) -> int {
|
||||
align_backward_int :: proc(ptr, align: int) -> int {
|
||||
return int(align_backward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
align_backward_uint :: inline proc(ptr, align: uint) -> uint {
|
||||
align_backward_uint :: proc(ptr, align: uint) -> uint {
|
||||
return uint(align_backward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
|
||||
@@ -215,12 +267,13 @@ align_formula :: proc(size, align: int) -> int {
|
||||
}
|
||||
|
||||
calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int) -> int {
|
||||
p := uintptr(ptr);
|
||||
a := uintptr(align);
|
||||
p, a := ptr, align;
|
||||
modulo := p & (a-1);
|
||||
|
||||
padding := uintptr(0);
|
||||
if modulo != 0 do padding = a - modulo;
|
||||
if modulo != 0 {
|
||||
padding = a - modulo;
|
||||
}
|
||||
|
||||
needed_space := uintptr(header_size);
|
||||
if padding < needed_space {
|
||||
@@ -235,3 +288,11 @@ calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int)
|
||||
|
||||
return int(padding);
|
||||
}
|
||||
|
||||
|
||||
|
||||
clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
new_slice := make(T, len(slice), allocator, loc);
|
||||
runtime.copy(new_slice, slice);
|
||||
return new_slice;
|
||||
}
|
||||
|
||||
+8
-5
@@ -38,20 +38,23 @@ Raw_Quaternion256 :: struct {imag, jmag, kmag: f64, real: f64};
|
||||
Raw_Quaternion128_Vector_Scalar :: struct {vector: [3]f32, scalar: f32};
|
||||
Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64};
|
||||
|
||||
make_any :: inline proc(data: rawptr, id: typeid) -> any {
|
||||
make_any :: proc(data: rawptr, id: typeid) -> any {
|
||||
return transmute(any)Raw_Any{data, id};
|
||||
}
|
||||
|
||||
raw_string_data :: inline proc(s: $T/string) -> ^byte {
|
||||
raw_array_data :: proc(a: $P/^($T/[$N]$E)) -> ^E {
|
||||
return (^E)(a);
|
||||
}
|
||||
raw_string_data :: proc(s: $T/string) -> ^byte {
|
||||
return (transmute(Raw_String)s).data;
|
||||
}
|
||||
raw_slice_data :: inline proc(a: $T/[]$E) -> ^E {
|
||||
raw_slice_data :: proc(a: $T/[]$E) -> ^E {
|
||||
return cast(^E)(transmute(Raw_Slice)a).data;
|
||||
}
|
||||
raw_dynamic_array_data :: inline proc(a: $T/[dynamic]$E) -> ^E {
|
||||
raw_dynamic_array_data :: proc(a: $T/[dynamic]$E) -> ^E {
|
||||
return cast(^E)(transmute(Raw_Dynamic_Array)a).data;
|
||||
}
|
||||
|
||||
raw_data :: proc{raw_string_data, raw_slice_data, raw_dynamic_array_data};
|
||||
raw_data :: proc{raw_array_data, raw_string_data, raw_slice_data, raw_dynamic_array_data};
|
||||
|
||||
|
||||
|
||||
+141
-41
@@ -5,6 +5,8 @@ import "core:odin/tokenizer"
|
||||
Proc_Tag :: enum {
|
||||
Bounds_Check,
|
||||
No_Bounds_Check,
|
||||
Optional_Ok,
|
||||
Optional_Second,
|
||||
}
|
||||
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
|
||||
|
||||
@@ -14,15 +16,12 @@ Proc_Inlining :: enum u32 {
|
||||
No_Inline = 2,
|
||||
}
|
||||
|
||||
Proc_Calling_Convention :: enum i32 {
|
||||
Invalid = 0,
|
||||
Odin,
|
||||
Contextless,
|
||||
C_Decl,
|
||||
Std_Call,
|
||||
Fast_Call,
|
||||
|
||||
Foreign_Block_Default = -1,
|
||||
Proc_Calling_Convention_Extra :: enum i32 {
|
||||
Foreign_Block_Default,
|
||||
}
|
||||
Proc_Calling_Convention :: union {
|
||||
string,
|
||||
Proc_Calling_Convention_Extra,
|
||||
}
|
||||
|
||||
Node_State_Flag :: enum {
|
||||
@@ -31,18 +30,61 @@ Node_State_Flag :: enum {
|
||||
}
|
||||
Node_State_Flags :: distinct bit_set[Node_State_Flag];
|
||||
|
||||
|
||||
Comment_Group :: struct {
|
||||
list: []tokenizer.Token,
|
||||
}
|
||||
|
||||
Node :: struct {
|
||||
pos: tokenizer.Pos,
|
||||
end: tokenizer.Pos,
|
||||
derived: any,
|
||||
state_flags: Node_State_Flags,
|
||||
derived: any,
|
||||
}
|
||||
|
||||
Comment_Group :: struct {
|
||||
using node: Node,
|
||||
list: []tokenizer.Token,
|
||||
}
|
||||
|
||||
Package_Kind :: enum {
|
||||
Normal,
|
||||
Runtime,
|
||||
Init,
|
||||
}
|
||||
|
||||
Package :: struct {
|
||||
using node: Node,
|
||||
kind: Package_Kind,
|
||||
id: int,
|
||||
name: string,
|
||||
fullpath: string,
|
||||
files: map[string]^File,
|
||||
|
||||
user_data: rawptr,
|
||||
}
|
||||
|
||||
File :: struct {
|
||||
using node: Node,
|
||||
id: int,
|
||||
pkg: ^Package,
|
||||
|
||||
fullpath: string,
|
||||
src: string,
|
||||
|
||||
docs: ^Comment_Group,
|
||||
|
||||
pkg_decl: ^Package_Decl,
|
||||
pkg_token: tokenizer.Token,
|
||||
pkg_name: string,
|
||||
|
||||
decls: [dynamic]^Stmt,
|
||||
imports: [dynamic]^Import_Decl,
|
||||
directive_count: int,
|
||||
|
||||
comments: [dynamic]^Comment_Group,
|
||||
|
||||
syntax_warning_count: int,
|
||||
syntax_error_count: int,
|
||||
}
|
||||
|
||||
|
||||
// Base Types
|
||||
|
||||
Expr :: struct {
|
||||
using expr_base: Node,
|
||||
@@ -142,6 +184,7 @@ Paren_Expr :: struct {
|
||||
Selector_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
op: tokenizer.Token,
|
||||
field: ^Ident,
|
||||
}
|
||||
|
||||
@@ -150,6 +193,13 @@ Implicit_Selector_Expr :: struct {
|
||||
field: ^Ident,
|
||||
}
|
||||
|
||||
Selector_Call_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
call: ^Call_Expr,
|
||||
modified_call: bool,
|
||||
}
|
||||
|
||||
Index_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
@@ -200,6 +250,24 @@ Ternary_Expr :: struct {
|
||||
y: ^Expr,
|
||||
}
|
||||
|
||||
Ternary_If_Expr :: struct {
|
||||
using node: Expr,
|
||||
x: ^Expr,
|
||||
op1: tokenizer.Token,
|
||||
cond: ^Expr,
|
||||
op2: tokenizer.Token,
|
||||
y: ^Expr,
|
||||
}
|
||||
|
||||
Ternary_When_Expr :: struct {
|
||||
using node: Expr,
|
||||
x: ^Expr,
|
||||
op1: tokenizer.Token,
|
||||
cond: ^Expr,
|
||||
op2: tokenizer.Token,
|
||||
y: ^Expr,
|
||||
}
|
||||
|
||||
Type_Assertion :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
@@ -224,6 +292,27 @@ Auto_Cast :: struct {
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
Inline_Asm_Dialect :: enum u8 {
|
||||
Default = 0,
|
||||
ATT = 1,
|
||||
Intel = 2,
|
||||
}
|
||||
|
||||
|
||||
Inline_Asm_Expr :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token,
|
||||
param_types: []^Expr,
|
||||
return_type: ^Expr,
|
||||
has_side_effects: bool,
|
||||
is_align_stack: bool,
|
||||
dialect: Inline_Asm_Dialect,
|
||||
open: tokenizer.Pos,
|
||||
constraints_string: ^Expr,
|
||||
asm_string: ^Expr,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -264,6 +353,7 @@ Block_Stmt :: struct {
|
||||
open: tokenizer.Pos,
|
||||
stmts: []^Stmt,
|
||||
close: tokenizer.Pos,
|
||||
uses_do: bool,
|
||||
}
|
||||
|
||||
If_Stmt :: struct {
|
||||
@@ -273,6 +363,7 @@ If_Stmt :: struct {
|
||||
init: ^Stmt,
|
||||
cond: ^Expr,
|
||||
body: ^Stmt,
|
||||
else_pos: tokenizer.Pos,
|
||||
else_stmt: ^Stmt,
|
||||
}
|
||||
|
||||
@@ -308,13 +399,26 @@ Range_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
for_pos: tokenizer.Pos,
|
||||
val0: ^Expr,
|
||||
val1: ^Expr,
|
||||
vals: []^Expr,
|
||||
in_pos: tokenizer.Pos,
|
||||
expr: ^Expr,
|
||||
body: ^Stmt,
|
||||
}
|
||||
|
||||
Inline_Range_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
inline_pos: tokenizer.Pos,
|
||||
for_pos: tokenizer.Pos,
|
||||
val0: ^Expr,
|
||||
val1: ^Expr,
|
||||
in_pos: tokenizer.Pos,
|
||||
expr: ^Expr,
|
||||
body: ^Stmt,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Case_Clause :: struct {
|
||||
using node: Stmt,
|
||||
@@ -405,12 +509,12 @@ Foreign_Block_Decl :: struct {
|
||||
Foreign_Import_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
|
||||
foreign_tok: tokenizer.Token,
|
||||
import_tok: tokenizer.Token,
|
||||
name: ^Ident,
|
||||
collection_name: string,
|
||||
fullpaths: []string,
|
||||
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
|
||||
comment: ^Comment_Group,
|
||||
}
|
||||
|
||||
@@ -424,7 +528,9 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
|
||||
}
|
||||
for {
|
||||
e, ok := val.derived.(Paren_Expr);
|
||||
if !ok do break;
|
||||
if !ok || e.expr == nil {
|
||||
break;
|
||||
}
|
||||
val = e.expr;
|
||||
}
|
||||
return;
|
||||
@@ -452,6 +558,7 @@ Field_Flags_Struct :: Field_Flags{
|
||||
};
|
||||
Field_Flags_Record_Poly_Params :: Field_Flags{
|
||||
.Typeid_Token,
|
||||
.Default_Parameters,
|
||||
};
|
||||
Field_Flags_Signature :: Field_Flags{
|
||||
.Ellipsis,
|
||||
@@ -520,12 +627,6 @@ Distinct_Type :: struct {
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
Opaque_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token_Kind,
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
Poly_Type :: struct {
|
||||
using node: Expr,
|
||||
dollar: tokenizer.Pos,
|
||||
@@ -574,22 +675,23 @@ Struct_Type :: struct {
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
fields: ^Field_List,
|
||||
name_count: int,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
is_packed: bool,
|
||||
is_raw_union: bool,
|
||||
fields: ^Field_List,
|
||||
name_count: int,
|
||||
}
|
||||
|
||||
Union_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
variants: []^Expr,
|
||||
where_token: tokenizer.Token,
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
is_maybe: bool,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
variants: []^Expr,
|
||||
}
|
||||
|
||||
Enum_Type :: struct {
|
||||
@@ -603,15 +705,6 @@ Enum_Type :: struct {
|
||||
is_using: bool,
|
||||
}
|
||||
|
||||
Bit_Field_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
align: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
fields: []^Field_Value, // Field_Value with ':' rather than '='
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Bit_Set_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
@@ -627,3 +720,10 @@ Map_Type :: struct {
|
||||
key: ^Expr,
|
||||
value: ^Expr,
|
||||
}
|
||||
|
||||
|
||||
Relative_Type :: struct {
|
||||
using node: Expr,
|
||||
tag: ^Expr,
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
+30
-63
@@ -67,6 +67,11 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
align = ti.align;
|
||||
}
|
||||
|
||||
switch in node.derived {
|
||||
case Package, File:
|
||||
panic("Cannot clone this node type");
|
||||
}
|
||||
|
||||
res := cast(^Node)mem.alloc(size, align);
|
||||
src: rawptr = node;
|
||||
if node.derived != nil {
|
||||
@@ -74,8 +79,9 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
}
|
||||
mem.copy(res, src, size);
|
||||
res.derived.data = rawptr(res);
|
||||
res.derived.id = node.derived.id;
|
||||
|
||||
switch n in node.derived {
|
||||
switch r in &res.derived {
|
||||
case Bad_Expr:
|
||||
case Ident:
|
||||
case Implicit:
|
||||
@@ -83,145 +89,131 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
case Basic_Lit:
|
||||
|
||||
case Ellipsis:
|
||||
r := cast(^Ellipsis)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Proc_Lit:
|
||||
r := cast(^Proc_Lit)res;
|
||||
r.type = auto_cast clone(r.type);
|
||||
r.body = clone(r.body);
|
||||
case Comp_Lit:
|
||||
r := cast(^Comp_Lit)res;
|
||||
r.type = clone(r.type);
|
||||
r.elems = clone(r.elems);
|
||||
|
||||
case Tag_Expr:
|
||||
r := cast(^Tag_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Unary_Expr:
|
||||
r := cast(^Unary_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Binary_Expr:
|
||||
r := cast(^Binary_Expr)res;
|
||||
r.left = clone(r.left);
|
||||
r.right = clone(r.right);
|
||||
case Paren_Expr:
|
||||
r := cast(^Paren_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Selector_Expr:
|
||||
r := cast(^Selector_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.field = auto_cast clone(r.field);
|
||||
case Implicit_Selector_Expr:
|
||||
r.field = auto_cast clone(r.field);
|
||||
case Selector_Call_Expr:
|
||||
r.expr = clone(r.expr);
|
||||
r.call = auto_cast clone(r.call);
|
||||
case Index_Expr:
|
||||
r := cast(^Index_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.index = clone(r.index);
|
||||
case Deref_Expr:
|
||||
r := cast(^Deref_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Slice_Expr:
|
||||
r := cast(^Slice_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.low = clone(r.low);
|
||||
r.high = clone(r.high);
|
||||
case Call_Expr:
|
||||
r := cast(^Call_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.args = clone(r.args);
|
||||
case Field_Value:
|
||||
r := cast(^Field_Value)res;
|
||||
r.field = clone(r.field);
|
||||
r.value = clone(r.value);
|
||||
case Ternary_Expr:
|
||||
r := cast(^Ternary_Expr)res;
|
||||
r.cond = clone(r.cond);
|
||||
r.x = clone(r.x);
|
||||
r.y = clone(r.y);
|
||||
case Ternary_If_Expr:
|
||||
r.x = clone(r.x);
|
||||
r.cond = clone(r.cond);
|
||||
r.y = clone(r.y);
|
||||
case Ternary_When_Expr:
|
||||
r.x = clone(r.x);
|
||||
r.cond = clone(r.cond);
|
||||
r.y = clone(r.y);
|
||||
case Type_Assertion:
|
||||
r := cast(^Type_Assertion)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.type = clone(r.type);
|
||||
case Type_Cast:
|
||||
r := cast(^Type_Cast)res;
|
||||
r.type = clone(r.type);
|
||||
r.expr = clone(r.expr);
|
||||
case Auto_Cast:
|
||||
r := cast(^Auto_Cast)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Inline_Asm_Expr:
|
||||
r.param_types = clone(r.param_types);
|
||||
r.return_type = clone(r.return_type);
|
||||
r.constraints_string = clone(r.constraints_string);
|
||||
r.asm_string = clone(r.asm_string);
|
||||
|
||||
case Bad_Stmt:
|
||||
// empty
|
||||
case Empty_Stmt:
|
||||
// empty
|
||||
case Expr_Stmt:
|
||||
r := cast(^Expr_Stmt)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Tag_Stmt:
|
||||
r := cast(^Expr_Stmt)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.stmt = clone(r.stmt);
|
||||
|
||||
case Assign_Stmt:
|
||||
r := cast(^Assign_Stmt)res;
|
||||
r.lhs = clone(r.lhs);
|
||||
r.rhs = clone(r.rhs);
|
||||
case Block_Stmt:
|
||||
r := cast(^Block_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.stmts = clone(r.stmts);
|
||||
case If_Stmt:
|
||||
r := cast(^If_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.init = clone(r.init);
|
||||
r.cond = clone(r.cond);
|
||||
r.body = clone(r.body);
|
||||
r.else_stmt = clone(r.else_stmt);
|
||||
case When_Stmt:
|
||||
r := cast(^When_Stmt)res;
|
||||
r.cond = clone(r.cond);
|
||||
r.body = clone(r.body);
|
||||
r.else_stmt = clone(r.else_stmt);
|
||||
case Return_Stmt:
|
||||
r := cast(^Return_Stmt)res;
|
||||
r.results = clone(r.results);
|
||||
case Defer_Stmt:
|
||||
r := cast(^Defer_Stmt)res;
|
||||
r.stmt = clone(r.stmt);
|
||||
case For_Stmt:
|
||||
r := cast(^For_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.init = clone(r.init);
|
||||
r.cond = clone(r.cond);
|
||||
r.post = clone(r.post);
|
||||
r.body = clone(r.body);
|
||||
case Range_Stmt:
|
||||
r := cast(^Range_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.val0 = clone(r.val0);
|
||||
r.val1 = clone(r.val1);
|
||||
r.vals = clone(r.vals);
|
||||
r.expr = clone(r.expr);
|
||||
r.body = clone(r.body);
|
||||
case Case_Clause:
|
||||
r := cast(^Case_Clause)res;
|
||||
r.list = clone(r.list);
|
||||
r.body = clone(r.body);
|
||||
case Switch_Stmt:
|
||||
r := cast(^Switch_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.init = clone(r.init);
|
||||
r.cond = clone(r.cond);
|
||||
r.body = clone(r.body);
|
||||
case Type_Switch_Stmt:
|
||||
r := cast(^Type_Switch_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.tag = clone(r.tag);
|
||||
r.expr = clone(r.expr);
|
||||
r.body = clone(r.body);
|
||||
case Branch_Stmt:
|
||||
r := cast(^Branch_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
case Using_Stmt:
|
||||
r := cast(^Using_Stmt)res;
|
||||
r.list = clone(r.list);
|
||||
case Bad_Decl:
|
||||
case Value_Decl:
|
||||
r := cast(^Value_Decl)res;
|
||||
r.attributes = clone(r.attributes);
|
||||
r.names = clone(r.names);
|
||||
r.type = clone(r.type);
|
||||
@@ -229,85 +221,60 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
case Package_Decl:
|
||||
case Import_Decl:
|
||||
case Foreign_Block_Decl:
|
||||
r := cast(^Foreign_Block_Decl)res;
|
||||
r.attributes = clone(r.attributes);
|
||||
r.foreign_library = clone(r.foreign_library);
|
||||
r.body = clone(r.body);
|
||||
case Foreign_Import_Decl:
|
||||
r := cast(^Foreign_Import_Decl)res;
|
||||
r.name = auto_cast clone(r.name);
|
||||
case Proc_Group:
|
||||
r := cast(^Proc_Group)res;
|
||||
r.args = clone(r.args);
|
||||
case Attribute:
|
||||
r := cast(^Attribute)res;
|
||||
r.elems = clone(r.elems);
|
||||
case Field:
|
||||
r := cast(^Field)res;
|
||||
r.names = clone(r.names);
|
||||
r.type = clone(r.type);
|
||||
r.default_value = clone(r.default_value);
|
||||
case Field_List:
|
||||
r := cast(^Field_List)res;
|
||||
r.list = clone(r.list);
|
||||
case Typeid_Type:
|
||||
r := cast(^Typeid_Type)res;
|
||||
r.specialization = clone(r.specialization);
|
||||
case Helper_Type:
|
||||
r := cast(^Helper_Type)res;
|
||||
r.type = clone(r.type);
|
||||
case Distinct_Type:
|
||||
r := cast(^Distinct_Type)res;
|
||||
r.type = clone(r.type);
|
||||
case Opaque_Type:
|
||||
r := cast(^Opaque_Type)res;
|
||||
r.type = clone(r.type);
|
||||
case Poly_Type:
|
||||
r := cast(^Poly_Type)res;
|
||||
r.type = auto_cast clone(r.type);
|
||||
r.specialization = clone(r.specialization);
|
||||
case Proc_Type:
|
||||
r := cast(^Proc_Type)res;
|
||||
r.params = auto_cast clone(r.params);
|
||||
r.results = auto_cast clone(r.results);
|
||||
case Pointer_Type:
|
||||
r := cast(^Pointer_Type)res;
|
||||
r.elem = clone(r.elem);
|
||||
case Array_Type:
|
||||
r := cast(^Array_Type)res;
|
||||
r.len = clone(r.len);
|
||||
r.elem = clone(r.elem);
|
||||
case Dynamic_Array_Type:
|
||||
r := cast(^Dynamic_Array_Type)res;
|
||||
r.elem = clone(r.elem);
|
||||
case Struct_Type:
|
||||
r := cast(^Struct_Type)res;
|
||||
r.poly_params = auto_cast clone(r.poly_params);
|
||||
r.align = clone(r.align);
|
||||
r.fields = auto_cast clone(r.fields);
|
||||
case Union_Type:
|
||||
r := cast(^Union_Type)res;
|
||||
r.poly_params = auto_cast clone(r.poly_params);
|
||||
r.align = clone(r.align);
|
||||
r.variants = clone(r.variants);
|
||||
case Enum_Type:
|
||||
r := cast(^Enum_Type)res;
|
||||
r.base_type = clone(r.base_type);
|
||||
r.fields = clone(r.fields);
|
||||
case Bit_Field_Type:
|
||||
r := cast(^Bit_Field_Type)res;
|
||||
r.fields = clone(r.fields);
|
||||
case Bit_Set_Type:
|
||||
r := cast(^Bit_Set_Type)res;
|
||||
r.elem = clone(r.elem);
|
||||
r.underlying = clone(r.underlying);
|
||||
case Map_Type:
|
||||
r := cast(^Map_Type)res;
|
||||
r.key = clone(r.key);
|
||||
r.value = clone(r.value);
|
||||
|
||||
case:
|
||||
fmt.panicf("Unhandled node kind: %T", n);
|
||||
fmt.panicf("Unhandled node kind: %T", r);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user