mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 11:11:37 -07:00
Compare commits
3099 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aa846d0ea5 | |||
| 8d0428a8b3 | |||
| 86c1aed20d | |||
| ff620422fa | |||
| 5685a8d885 | |||
| b8327ad00d | |||
| 1bdce19c18 | |||
| 041ff13672 | |||
| 60a2eaf666 | |||
| ec2db568c1 | |||
| 1387fd9047 | |||
| fb6288a54e | |||
| 4d00058858 | |||
| 5c26cf9d73 | |||
| 6048d25d36 | |||
| 748f094e15 | |||
| 184c686c7e | |||
| 240a568eb9 | |||
| ad953c4670 | |||
| 0f9b0c2052 | |||
| cde334ada3 | |||
| 2b4010998d | |||
| 4272fe5e85 | |||
| c29b643a58 | |||
| c9b82a21e9 | |||
| bd31a99bf7 | |||
| 17bbb48d8a | |||
| ac53577e9b | |||
| 896057b5a7 | |||
| 01db195b47 | |||
| d33350e3a5 | |||
| 8a86c4c7cc | |||
| 1c9f48031d | |||
| 7fcd5ecbd5 | |||
| b68b090f13 | |||
| 542098dc6f | |||
| 0a66f8c9a3 | |||
| 158e4c0b6c | |||
| 47c7dc6a9b | |||
| 65551ba8fb | |||
| c7d92562c2 | |||
| 5b3802b8ca | |||
| 2fb0383e82 | |||
| 8077d5f565 | |||
| 6b45856e81 | |||
| 28e5df6e7f | |||
| 22867ec6f0 | |||
| d0a50ff0a3 | |||
| e9b1d4f633 | |||
| ba9f0dd553 | |||
| c3b3194a00 | |||
| 201cad51a9 | |||
| d21e522208 | |||
| f1bdd2e60f | |||
| 0eb75886d1 | |||
| 3612569624 | |||
| c83d13d0cb | |||
| f98c4d6837 | |||
| a4d0092b16 | |||
| eb49b5f84a | |||
| 9d949ef82e | |||
| ae04af4e4e | |||
| 3baddd4116 | |||
| 6ae468828c | |||
| b59e110fec | |||
| 4282688e60 | |||
| 9b3fb25a41 | |||
| 2ce9873464 | |||
| 986844a0f0 | |||
| 7c1f538c02 | |||
| 2f1c896290 | |||
| 8827818b1d | |||
| e19958152a | |||
| 05a181d719 | |||
| d24784074c | |||
| cd2476e084 | |||
| ebbc33fdb5 | |||
| 3a4373641b | |||
| 9adec628c1 | |||
| 3e54cddf64 | |||
| d602709133 | |||
| 8e1120bc09 | |||
| ebed29fc09 | |||
| bee8beb2c9 | |||
| 12296a0dcc | |||
| 2942e45ff5 | |||
| aca5c7c1c6 | |||
| a1d871360c | |||
| 2b36069924 | |||
| 4fb4ada2c7 | |||
| e3ee005404 | |||
| e8bf1f2064 | |||
| 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 | |||
| 14c4fed94c | |||
| c3205316ed | |||
| b542ef273d | |||
| 404132de17 | |||
| cd43f4c94c | |||
| 23ff98dea0 | |||
| c3a8e232a5 | |||
| 7f89f6b582 | |||
| 159150c6d9 | |||
| 527b39ce2b | |||
| 5af3c7b0e8 | |||
| 5db4bd9944 | |||
| 20b410f149 | |||
| ae7cbd5171 | |||
| 04f7225ea5 | |||
| f0c6f29f82 | |||
| 7d9a9a2283 | |||
| ba85e432e7 | |||
| cfba29002a | |||
| 11c7b6a2e4 | |||
| 47f9876b36 | |||
| ff31f9a900 | |||
| ebc4867514 | |||
| e1ccba3de5 | |||
| 28570f8fa4 | |||
| 24bd370e1b | |||
| 3e67ae7339 | |||
| 0e52c37865 | |||
| d520b9a1ba | |||
| 5c7d6fcfd0 | |||
| 5ae924f988 | |||
| cae1e02593 | |||
| b09297da81 | |||
| 9abdfaaf6c | |||
| b32ef9e47b | |||
| b8324b0776 | |||
| d0ca045586 | |||
| 673879d1d2 | |||
| 5d1c9583cb | |||
| d017b5de9d | |||
| 93ead4bcb3 | |||
| bbe9b4dee0 | |||
| 3bd00fd6b7 | |||
| 83fec387d4 | |||
| f6f10d10e8 | |||
| 16a7c55334 | |||
| e9e2ab240d | |||
| 842281ddd3 | |||
| 978d7fcb99 | |||
| b267a5964d | |||
| b288613307 | |||
| 4591353724 | |||
| 13107628f8 | |||
| c407687a4c | |||
| 5a50ab7a99 | |||
| 4578544007 | |||
| bdfef08214 | |||
| 42678848b2 | |||
| ab52f8d795 | |||
| d79ee7d530 | |||
| 7e271310ff | |||
| f24de51c65 | |||
| 2252d992d7 | |||
| 2d70a784d1 | |||
| a8a4dc1eb1 | |||
| 9e9e905431 | |||
| 8ee41c20af | |||
| 11c705508d | |||
| 267ae0b4a2 | |||
| 1bc6e6a7cc | |||
| 33a458c520 | |||
| 6a7ccd8c0a | |||
| 9ba2926e7e | |||
| a50b2d5d04 | |||
| 7f9626e5c7 | |||
| 7140c95c55 | |||
| ceef5db547 | |||
| 5ec8dd166a | |||
| 80a32a8182 | |||
| c2b3056094 | |||
| f99f351e01 | |||
| 880c7f01a8 | |||
| 10f0961184 | |||
| eea403d0ab | |||
| 2cc5c4eed3 | |||
| f308d8d73e | |||
| ff0bc3ccad | |||
| 072979c6d2 | |||
| a3d2c40da0 | |||
| 5b1312342e | |||
| 85e31e1b69 | |||
| fb0fb4767b | |||
| 38a9a2b7fc | |||
| 005c6af302 | |||
| 1d14b3059e | |||
| cc2fa8f756 | |||
| d1c9fd4e01 | |||
| 4593730632 | |||
| f62a0891bd | |||
| 4f2d4716ad | |||
| 022b793a7d | |||
| 7267004a55 | |||
| 786c9dfe07 | |||
| 81b24594ce | |||
| 995ba0df9a | |||
| 08392d885e | |||
| d462dbb5be | |||
| 19c32ecb81 | |||
| 494b1e7eaa | |||
| c43d17bfec | |||
| c9723e2dc0 | |||
| 4ba579bc25 | |||
| 58d4d424c6 | |||
| 89ccb5b99f | |||
| 7f5021c8e9 | |||
| 8bec324779 | |||
| e6f26b9931 | |||
| b8c534eba9 | |||
| 95d3f43e15 | |||
| 2d97e1dee3 | |||
| be2dfd42fd | |||
| 851118faf4 | |||
| 53cd7a3d0c | |||
| f170648629 | |||
| 42def957d5 | |||
| 6433a0d31e | |||
| 359e5d9e15 | |||
| e229885b2b | |||
| ee78374281 | |||
| ebe152a155 | |||
| 46582a45bd | |||
| 6b5ea011e7 | |||
| 9503440eb0 | |||
| 3fa4c5043a | |||
| 9db81498d8 | |||
| 7fbe0a6f23 | |||
| 3fd5c3cd85 | |||
| 99121d6ff2 | |||
| 0c0c83ee29 | |||
| b75d59d6e0 | |||
| 71c8a3456e | |||
| 37e3e081c6 | |||
| 5ea9fc3fb0 | |||
| 53f65224f6 | |||
| 902d313c6a | |||
| 45d844f9d2 | |||
| 9b58781122 | |||
| b74f8f2047 | |||
| 88c90cf99a | |||
| 321dcc60e3 | |||
| 400f12f31f | |||
| 2c5a84bb78 | |||
| e01d8a04a9 | |||
| 69afa33fa5 | |||
| 44e0e96612 | |||
| 0839dccfdc | |||
| 2484f4d04b | |||
| d22e5b697d | |||
| 301ee664e9 | |||
| 9b4d4a2c61 | |||
| d9647174a3 | |||
| 4d29b64196 | |||
| 8be1b2e1b1 | |||
| b85258a9fc | |||
| 07897ed78e | |||
| 8115386217 | |||
| f8dd4816ff | |||
| 928a445a14 | |||
| 42cd78497a | |||
| 813f5d211f | |||
| 71d129a709 | |||
| c35762528c | |||
| aa796a1032 | |||
| 5bf71ba75c | |||
| 967981aacd | |||
| 33d05a07de | |||
| 8da8301b09 | |||
| 536cceeef9 | |||
| 098684a6fe | |||
| 694ee02247 | |||
| 0451c88ab6 | |||
| 2ef7bfc06e | |||
| 7bfdb4f9f4 | |||
| 0a35b13411 | |||
| 803f6a6651 | |||
| 672cfd51c3 | |||
| 80cdf8b6a8 | |||
| dc2d5239c5 | |||
| dacfc9de15 | |||
| 689aa4d734 | |||
| 8a46b493fd | |||
| 86abdc0603 | |||
| a634444f99 | |||
| 04a25b11ad | |||
| 40546fbde2 | |||
| c1176c2bcb | |||
| 57853fe1b1 | |||
| 013b3b9fb3 | |||
| dc0f04e53b | |||
| 40606d9272 | |||
| ebf7926fa4 | |||
| dfb3101ecf | |||
| e3d3a81617 | |||
| dbdbbcd60f | |||
| f9aaff99c6 | |||
| 4b718aae75 | |||
| 20db0e7f09 | |||
| 614ea5c168 | |||
| 57565b78a6 | |||
| b5b085914a | |||
| 044e64beb0 | |||
| adca5b57cd | |||
| 3b898e5224 | |||
| 153e7525b5 | |||
| 44a303e577 | |||
| d63878d0dd | |||
| a20c31d6b5 | |||
| 560bdc339b | |||
| 01dfb1dac8 | |||
| ee8d3e03f8 | |||
| 4aad45e3e7 | |||
| fe5c642d9f | |||
| b5fdb3f855 | |||
| 416ff149bd | |||
| 233c6e2d3a | |||
| a29a6d9285 | |||
| a7a31e4c23 | |||
| 72cad591bd | |||
| 684bf7aa61 | |||
| d760b95e4f | |||
| 5e81fc72b9 | |||
| 0977ac111a | |||
| 2db16d6a32 | |||
| 5aa46d31b2 | |||
| 14e8b299b7 | |||
| c7cb754514 | |||
| a5e42a0465 | |||
| d808f9eccf | |||
| 1734eb949f | |||
| 7fae890ef9 | |||
| 94879ed149 | |||
| 2c75fe2314 | |||
| 1da0668653 | |||
| c60fb10a6a | |||
| 7140f42915 | |||
| ad92fbfd4e | |||
| 1416946757 | |||
| 6a8c4ee04c | |||
| 516df9123d | |||
| 818d6dbbea | |||
| 286c5b7b24 | |||
| c0e8113f6f | |||
| e15dfa8eb6 | |||
| f12ded54f2 | |||
| b4951c9b39 | |||
| 9f0a28017d | |||
| d7c8a3a9dd | |||
| 10b109e25f | |||
| 2afe4bea67 | |||
| 12ae5ed09e | |||
| 0bdc3b4f21 | |||
| eaabb888d4 | |||
| b53fe14c22 | |||
| 45683703ea | |||
| 03053a18ce | |||
| 2a6d9e8927 | |||
| fa81061db0 | |||
| 39b3c8c80f | |||
| 3139151935 | |||
| 672a8f5dbd | |||
| 8672ff1c55 | |||
| abfa894566 | |||
| 5b52fed268 | |||
| 94a638b436 | |||
| 1b8c3ca22a | |||
| 71b32ae117 | |||
| 939459b635 | |||
| 562b518394 | |||
| d62503d031 | |||
| 4e8a801b35 | |||
| e1b711b3b3 | |||
| 6c69e8c043 | |||
| 7fa2d25eea | |||
| dae514a2c9 | |||
| 068993a819 | |||
| 66ae4e5afc | |||
| 218d1131e8 | |||
| f4f6e9ad49 | |||
| 48ab7f876c | |||
| 1e53a6fa96 | |||
| 4cef160c87 | |||
| 68582c5ad1 | |||
| a9a2dafca5 | |||
| da3467c25f | |||
| 5fc42bf9c9 | |||
| 42bbd31df1 | |||
| c7cc38b7d8 | |||
| 4afc78efc6 | |||
| bc34083c9c | |||
| 08dd8414c1 | |||
| d54255505a | |||
| d4914c3546 | |||
| 772c8779fa | |||
| c92b2e9612 | |||
| 1af143b749 | |||
| 1370c10d1b | |||
| 1348d8a8cd | |||
| 495aaacb81 | |||
| 22e982c8fb | |||
| 6d614ef07c | |||
| 723f351a6d | |||
| c93872cc13 | |||
| 97dece15d7 | |||
| 657103c4cf | |||
| b9d3129fb3 | |||
| b311540b16 | |||
| 07ced1cf0e | |||
| a1d4ea7718 | |||
| f921a91fc8 | |||
| 4dade34603 | |||
| d76249d90b | |||
| d118fc569a | |||
| 2dc39a5cbd | |||
| c89fc35e94 | |||
| 614d209824 | |||
| f1a7b31209 | |||
| 6a8b3fee38 | |||
| 4551521b2c | |||
| 97dfcffa76 | |||
| 6d3feb4531 | |||
| c44d25d14f | |||
| 25dd00cd0b | |||
| 01c10aa944 | |||
| 4908d1ebdd | |||
| 7bc146e6fd | |||
| 59ab51acec | |||
| cf23954297 | |||
| d1cc6534cd | |||
| 1b454c7ded | |||
| 150d4e343d | |||
| 28ada801a0 | |||
| a7676bff6e | |||
| 4369298e96 | |||
| a58c29582e | |||
| 3ad20a2d2d | |||
| b86dfa7af7 | |||
| 980890ee8a | |||
| 0a63690b39 | |||
| 0076a4df62 | |||
| 4c065a7e99 | |||
| 04036aba9c | |||
| b08aa857b3 | |||
| 2d26278a65 | |||
| 27a3c5449a | |||
| 9c63212824 | |||
| 65d41d4248 | |||
| b04231dd95 | |||
| 37633c1d2a | |||
| 5877017d30 | |||
| 132fdf14b8 | |||
| e7d3001dd1 | |||
| f163181204 | |||
| 2c5c8192f8 | |||
| 162c87b1b8 | |||
| 77734ea967 | |||
| 912fc2890b | |||
| 14059583cd | |||
| f3bffb9810 | |||
| 540730c0be | |||
| 40f0e74b8c | |||
| 5ca0cd60d0 | |||
| 96f0a08725 | |||
| d85893954d | |||
| d26033eb23 | |||
| c7a70be824 | |||
| 08c490d9ac | |||
| 8ee7ee7120 | |||
| d471a59041 | |||
| f25818e923 | |||
| 3d531be711 | |||
| 56d365a4e7 | |||
| 308300c1fc | |||
| 927d6814f2 | |||
| 7c99f52187 | |||
| 4ab9edeb53 | |||
| c5b3d7a736 | |||
| d7172e168e | |||
| d99ffe604f | |||
| b77c79294c | |||
| 8e722274f0 | |||
| ebe7fc23a5 | |||
| 4d40f564ef | |||
| fd62959bf4 | |||
| 8b8cada33e | |||
| aaa24894b6 | |||
| 2af19c496e | |||
| fea34b32ea | |||
| b891c0feab | |||
| 9f039c323e | |||
| b0dd3ac599 | |||
| b38a8cfb12 | |||
| 4c79b52867 | |||
| 6dba05b00d | |||
| 32a29d627a | |||
| caf9bc6be9 | |||
| 654740d5b1 | |||
| b894e2b378 | |||
| c40acd008e | |||
| 3d2279fba0 | |||
| 2b080dbbc2 | |||
| 9cadd58465 | |||
| 65e9b4d5f0 | |||
| fb3d73cb20 | |||
| 222941727f | |||
| 5697d6df74 | |||
| 426c1ed6f4 | |||
| 458ec5922e | |||
| f5fdd031f9 | |||
| ceb58ae04f | |||
| 868603f617 | |||
| d2faa9bef1 | |||
| b1663a14e9 | |||
| 3fc60930e6 | |||
| 665734f04f | |||
| 16f3bc2c0b | |||
| 71a733e3b5 | |||
| a66612e8ae | |||
| 00c0ce45b3 | |||
| ab0afa548b | |||
| ea1690b7a1 | |||
| a5ff983266 | |||
| a46a1f5f34 | |||
| 40135cbc66 | |||
| c61fd3a70a | |||
| 45fbc4e8c5 | |||
| 9ce8f124bb | |||
| 63bbb9b62f | |||
| 56c4039e72 | |||
| 0755dfbd5d | |||
| 2780f82f30 | |||
| 5dcb5c2ba3 | |||
| 9d552d04ea | |||
| 88e1b93786 | |||
| 62f5eb5bca | |||
| 6ab471ff87 | |||
| 155b138aa4 | |||
| a730b04bf1 | |||
| 957e1e1f07 | |||
| 133f88406f | |||
| ecd2eacd75 | |||
| 2614830c69 | |||
| 381fbd3daf | |||
| dd9113786c | |||
| 1354f53d02 | |||
| 564e85ee29 | |||
| ef04d13337 | |||
| 68d4bde82f | |||
| a019059975 | |||
| 7580ec494b | |||
| a9b20c29b1 | |||
| 76a2807b56 | |||
| 14ff561f6c | |||
| 1fd677b42e | |||
| 6b18b90222 | |||
| 9e6d488063 | |||
| 4a15689776 | |||
| c785c3569f | |||
| e6f9b4fb11 | |||
| b978959fae | |||
| 8b09ab6fe7 | |||
| 2347dca9d9 | |||
| 2ada90e094 | |||
| a137a06b00 | |||
| b1684fe455 | |||
| 886054f0f8 | |||
| 0e1cfa5a0a | |||
| 400558abcd | |||
| d75634ff5e | |||
| 0c04b9398a | |||
| e3d1d1d85c | |||
| b6ea7b7418 | |||
| 7426f3b092 | |||
| 299c299dff | |||
| 290c111206 | |||
| 314d5a778e | |||
| dc706d8a6b | |||
| a6fb2dd587 | |||
| 4e93b70f8a | |||
| 1eaa47ebae | |||
| 3a31444656 | |||
| f7efaf2ba2 | |||
| 14c6f2f258 | |||
| 231f3cc15a | |||
| 332e598357 | |||
| 716373836c | |||
| fdb60b2d51 | |||
| 885c5dc8b7 | |||
| 394baa9ddd | |||
| 3d86fc2f2f | |||
| 61b07335d8 | |||
| 712744ef36 | |||
| dbcd49acfc | |||
| 291bf0c143 | |||
| e398c074db | |||
| eadb66c9ef | |||
| 9d7e1c17cc | |||
| 775b544326 | |||
| da26e14959 | |||
| bdab5e00da | |||
| e781056df1 | |||
| b08d944c33 | |||
| 4f24f1172e | |||
| 4446a1431a | |||
| 090937f8af | |||
| d852b0c948 | |||
| 007a7989b8 | |||
| 5c04800831 | |||
| c634d4a96d | |||
| c67ea97845 | |||
| 15d3f4c190 | |||
| 1b3ec66fa2 | |||
| ad3b6ab718 | |||
| 8c618225bf | |||
| 1f5ab0b5f1 | |||
| 1652d5033b | |||
| 9b4b20e8b1 | |||
| 8fb8b5ed7e | |||
| 7bd86bb3ec | |||
| b6d6eb6ae2 | |||
| a126d2ba16 | |||
| 6faab8e47a | |||
| 76a6757ee9 | |||
| 0c8746ada6 | |||
| a0c81c79ad | |||
| cdfaa643cc | |||
| 989cc893ef | |||
| 2878cd8241 | |||
| a9ab90bd24 | |||
| e551d2b25e | |||
| 38ae2e9efa | |||
| 684945ea57 | |||
| 4c51384ad6 | |||
| 64bd884d94 | |||
| a07232ea63 | |||
| 79b585ada8 | |||
| f917935f9d | |||
| 222e5c8ae9 | |||
| dbd0638853 | |||
| 772dc47f55 | |||
| 0777351482 | |||
| 53d8216311 | |||
| 6a0c3d5599 | |||
| 9647fb2a4b | |||
| 42f936742e | |||
| e2d4667639 | |||
| b74d828af7 | |||
| e16409f88a | |||
| 6571f07c7e | |||
| 4b7a09b92e | |||
| 26fb1fa18c | |||
| 24c43d33bb | |||
| 7343f0c7ff | |||
| 3c8cda514b | |||
| bc954df80e | |||
| e1ae359a77 | |||
| c9602953aa | |||
| 0185b43c2f | |||
| bc5c37ebb1 | |||
| 0133dd9034 | |||
| a194aa5a9e | |||
| 766f3e259f | |||
| 6092cc0497 | |||
| fa5d00521b | |||
| d1e29400d3 | |||
| 2dc7aaec82 | |||
| b7242f9d4b | |||
| 7ea7fc10db | |||
| 141da818ba | |||
| dc3d62d437 | |||
| dee28d998f | |||
| 96ef6aa7f3 | |||
| 238a40321a | |||
| 1aea59a0fc | |||
| c6dee52abe | |||
| 1e180d611d | |||
| e452765d28 | |||
| 2b80683fc7 | |||
| 5f840ea2fc | |||
| c72427fd1e | |||
| 44b959648c | |||
| a96bf08266 | |||
| ebaf48c07d | |||
| c197a27185 | |||
| 5ccccf8816 | |||
| 345e790f52 | |||
| fd529b97be | |||
| be1a3488a4 | |||
| 46c610d6e5 | |||
| db2eff6847 | |||
| e047d9eb5e | |||
| 3113e8c892 | |||
| 19e37c852e | |||
| 8fc24fd6f2 | |||
| 493f11521d | |||
| 3363e2c199 | |||
| cf94d1735d | |||
| d9245a6af3 | |||
| d453b9a5b1 | |||
| 5af20aa467 | |||
| cd2c4c02e1 | |||
| 6c21e99832 | |||
| 08598b9425 | |||
| 6295f6747f | |||
| 64f84ef9a3 | |||
| d1b9f3ac74 | |||
| d732a51587 | |||
| 9487f8c92e | |||
| c5def60224 | |||
| ca2220214e | |||
| 6e6a053823 | |||
| 686e0ef3d1 | |||
| 594238a86c | |||
| c60766f8e6 | |||
| 5acea1bceb | |||
| aac643f476 | |||
| 37edbfeb74 | |||
| 51da3e469b | |||
| 9156af2bab | |||
| 3a18ae3978 | |||
| f294c1adee | |||
| bb93a8b131 | |||
| 5bfe5ad82e | |||
| dd28fe6e82 | |||
| cda0f4d8f3 | |||
| 0546b5c218 | |||
| 61a3e50d1b | |||
| 75aeb02c39 | |||
| a32f024d94 | |||
| 37d993c417 | |||
| bcbb59dc11 | |||
| c1ec45dc0a | |||
| 0778d18bc7 | |||
| d7e9b8d374 | |||
| f647187e53 | |||
| 9dabbc2c95 | |||
| 4167168c63 | |||
| aa156e4bfc | |||
| 1c9656aedb | |||
| 8b2f902f3d | |||
| bbece7e910 | |||
| e5f188241c | |||
| 6d3203c11b | |||
| 5ba3d90893 | |||
| 894f267bbf | |||
| e084799b31 | |||
| 3ba3421f5f | |||
| 2bbad5903f | |||
| a240a3d146 | |||
| 775f1e2c95 | |||
| 7c982b6e10 | |||
| cc14180e9d | |||
| b2d40680c8 | |||
| 8662df2b7f | |||
| 6abbc9f1b5 | |||
| 66a9fde12c | |||
| eb5af2876a | |||
| 1f2fdddc6d | |||
| 0bcf53b513 | |||
| 956dd26aa0 | |||
| b504d6e12a | |||
| b4e83a430a | |||
| e3d7e6f76a | |||
| 5c3dc30dc0 | |||
| c508e46ed9 | |||
| 9d85f236b8 | |||
| 3a05a2e562 | |||
| 68384a452f | |||
| 34b6486361 | |||
| 1ce90b2166 | |||
| 9d6666f333 | |||
| d29335ecec | |||
| 95873e66ab | |||
| b7eebe5d00 | |||
| 57d4333ed3 | |||
| 26f11f12ab | |||
| 0b6fc19fb0 | |||
| f2dae7023f | |||
| f36775ffd8 | |||
| 8702a8a477 | |||
| 47e31c3de8 | |||
| b1d0d82254 | |||
| 542e524a87 | |||
| b54c35639b | |||
| cfcb0514bf | |||
| 1a6b7f9945 | |||
| 03957cee64 | |||
| 1584260886 | |||
| a565d842da | |||
| 411d1450b0 | |||
| 984fa1c672 | |||
| 12c810f85d | |||
| 3bf01c8498 | |||
| d05837ab6d | |||
| 4369a1714e | |||
| 13f084a219 | |||
| 4205f0f0b1 | |||
| bd62bceca6 | |||
| ff6ec860b3 | |||
| 2bf60d3337 | |||
| f288614eaf | |||
| 9761d54c24 | |||
| 3794914478 | |||
| 3e11b4fe1e | |||
| 50c3f4d74e | |||
| 304c7594cd | |||
| d02b050850 | |||
| 17b0e3a1a1 | |||
| 28583bfff8 | |||
| b2df48dadb | |||
| 04a853c6fe | |||
| 84f0c975b5 | |||
| 00161023cd | |||
| 7f063eb5e7 | |||
| 784c48c9e3 | |||
| 008d8f25c8 | |||
| 7ffcf34dca | |||
| f3a4904f21 | |||
| 3aec78b1d4 | |||
| a3e6e8d304 | |||
| a747c03f29 | |||
| 2301ae157c | |||
| d3c7d6d485 | |||
| 9b063ad9a3 | |||
| c2f9bf489e | |||
| e496b95881 | |||
| 444f4f446a | |||
| 41ad896f3f | |||
| 0a4b88f9a6 | |||
| 4c2f03b1f2 | |||
| 52dcaeb1e9 | |||
| f96fbc94c8 | |||
| bb62bed981 | |||
| bc6b8c5332 | |||
| 6ab6447791 | |||
| f61c4715c1 | |||
| 3061bc8478 | |||
| d035d48c8e | |||
| b55b1ffe14 | |||
| 620d5d34f7 | |||
| f9654b6c36 | |||
| 6659ceb551 | |||
| 5aa591d884 | |||
| efe91b1f91 | |||
| 7c99884afb | |||
| dfd7a194ed | |||
| 2ddb27869b | |||
| 5c608b01ba | |||
| 2bd85e764e | |||
| 822e4894f2 | |||
| ce2e23849e | |||
| 099995e7dd | |||
| 72f4186b21 | |||
| 3742d9e7e9 | |||
| 4ac1218bf8 | |||
| b171cc41e6 | |||
| efc3a9e69d | |||
| 307c58d908 | |||
| ae02e9c34a | |||
| 139fa55c27 | |||
| 562bb6e4c4 | |||
| ef2931d4a5 | |||
| 2d4aa2be6d | |||
| 42b42db675 | |||
| 73e9dbbf8c | |||
| 0971a59493 | |||
| 627c91124a | |||
| 4eba717281 | |||
| 9623e5e032 | |||
| 805cc48f03 | |||
| d894fb3708 | |||
| b6ca913cff | |||
| 39db428603 | |||
| 992502f03b | |||
| edc3a9392a | |||
| f881ebd007 | |||
| 11ea03d2e8 | |||
| 99b4d59f44 | |||
| dfeefc5179 | |||
| ab46406f4d | |||
| 48ad147818 | |||
| 79d49f1955 | |||
| 1ccc8700e4 | |||
| f38d70a235 | |||
| b37b7a0f72 | |||
| f8d7f42208 | |||
| db0756a119 | |||
| 1a4e25f141 | |||
| 79ade6ac7b | |||
| ecce1d9974 | |||
| 834308d8ce | |||
| 9e73189d63 | |||
| 11bddf270c | |||
| 0818a272e2 | |||
| 9750b1162a | |||
| 3106aaaa3d | |||
| d31d4c9bd6 | |||
| 6993777d36 | |||
| 54c044ee09 | |||
| 2e5cecf9e6 | |||
| 7acb49eefb | |||
| 0f6c1f3482 | |||
| 1ee0fe7457 | |||
| 1a18481d8b | |||
| 28c61c0f5d | |||
| 597fb452b1 | |||
| 5961a63880 | |||
| cce5e595e5 | |||
| e7d72f6848 | |||
| 7dcad45e0d | |||
| 3772ea6ae1 | |||
| 2cc2eb1ec0 | |||
| 8a789e33b0 | |||
| 2f86f8f8e0 | |||
| 02f9a27f46 | |||
| 6cb605a025 | |||
| 9f3e42e4ef | |||
| 71d987bd2e | |||
| 637899467c | |||
| 5bdb424c6b | |||
| c62cfddb9c | |||
| 14a4c28f8f | |||
| f1e1814ff9 | |||
| b468cf141b | |||
| 787ea1feba | |||
| 91477e9e69 | |||
| cfd0dfd2bf | |||
| 46b1868185 | |||
| 4c4de1d6c4 | |||
| c8b30de771 | |||
| 4f3837f0e6 | |||
| 76848e8807 | |||
| 12902821d6 | |||
| f5549f6bde | |||
| 3825eab989 | |||
| 3cd6ae311d | |||
| 26cfc0257d | |||
| 1d31eabb6e | |||
| 8cd2797b2e | |||
| 11f5236434 | |||
| 220485a2d2 | |||
| eb274cf316 | |||
| aa542980ce | |||
| e0240c186f | |||
| ae58502a21 | |||
| 6a3697279c | |||
| c19ec5d65d | |||
| 15dca449c9 | |||
| dda985f49d | |||
| 12256beeb2 | |||
| 0858ae2024 | |||
| 6c18864291 | |||
| ae57284912 | |||
| 001837e6bb | |||
| 28523f17e2 | |||
| ae2af8315e | |||
| adbb3bb75f | |||
| 6181c4edb3 | |||
| 830c194da5 | |||
| 1830c1e57c | |||
| e5735af6d6 | |||
| a6b0ae71b2 | |||
| 3365baee8f | |||
| cc88dd0b71 | |||
| f050bfe872 | |||
| ab71acc3a5 | |||
| 0a85d1af6b | |||
| 68adadb01a | |||
| d56f458d11 | |||
| a65eadee63 | |||
| 16dfae62bc | |||
| fe680a8b1f | |||
| 54fe9f3eb1 | |||
| cbc6c2666b | |||
| a4d0ac1802 | |||
| 0dc29a7208 | |||
| a9321bc73f | |||
| e3f0ab7c3d | |||
| 5643ea1ba2 | |||
| 3b6523fbd9 | |||
| ffc4f01470 | |||
| e326f41d16 | |||
| 1d0ac72e4a | |||
| b216e44870 | |||
| 7d39b26cf4 | |||
| 884d5fed9f | |||
| ec84188597 | |||
| 85ac95f81b | |||
| 042550cf87 | |||
| b3ebff715a | |||
| 1ee60663bb | |||
| 59da98d3f0 | |||
| 2d41a42f61 | |||
| e1e4a916a5 | |||
| 71f94bff76 | |||
| c7d6467cfa | |||
| 79a3c0b36c | |||
| 966249c10a | |||
| acc010cba5 | |||
| 89f4e7a8db | |||
| 55f4eabecd | |||
| d0fc9aa069 | |||
| 8be9b5082c | |||
| 708907df31 | |||
| 70586b1cf8 | |||
| 877a78d6ba | |||
| 3928614326 | |||
| 5e5f5bfa8d | |||
| 3a1a7b40f9 | |||
| 835d7dcab2 | |||
| 28816dc491 | |||
| ccdc3438be | |||
| 60711dd355 | |||
| fad3947e26 | |||
| d8e5b2d1a4 | |||
| 2d26ad0226 | |||
| 45d3c6c0d3 | |||
| c6bffd7c35 | |||
| 462d81430c | |||
| d3cada5bd6 | |||
| cdbf831a7a | |||
| 0718f14774 | |||
| a6fe656f21 | |||
| dc5da7933a | |||
| 96fc9138d4 | |||
| 6512a3e5f2 | |||
| 49f2124df0 | |||
| a11d6e696a | |||
| 1705ba8069 | |||
| 8d2c4a78a1 | |||
| 8504ff920b | |||
| e34a9e6185 | |||
| c3c7834246 | |||
| 1ab40d8600 | |||
| 92ce02dab0 | |||
| 8abe9ef507 | |||
| d0e04bf569 | |||
| b92599879a | |||
| 0e91298fd1 | |||
| e515220694 | |||
| a55683d287 | |||
| fa4e95105f | |||
| 1e01085ef7 | |||
| 04a1f869b5 | |||
| e04ba7530d | |||
| ea055f1465 | |||
| 3b2c867817 | |||
| 3de23eb0bf | |||
| 5de3b07e2b | |||
| c0ca4d4635 | |||
| efe4b71bae | |||
| bc37bd5429 | |||
| 5f20e04259 | |||
| 9bef5ec01a | |||
| cdf873542b | |||
| 4742690dec | |||
| 3a16f1e854 | |||
| 877400dd12 | |||
| a4e3201113 | |||
| a99cc2fd70 | |||
| 5fe4c33d0e | |||
| 4d9d38cc28 | |||
| 5b71ffd4f9 | |||
| c2ca24a486 | |||
| e5aff6fd6d | |||
| 3eb8aa8268 | |||
| 6d1c32eb77 | |||
| ba776a3c9f | |||
| cd7e260f4e | |||
| ba67e474d3 | |||
| b92a8c513e | |||
| 13572aeef0 | |||
| 5081ea1a0c | |||
| e9e7ce2606 | |||
| 915dcb0c28 | |||
| 8236c6d4b7 | |||
| 555fe37ad8 | |||
| 881f667558 | |||
| 0a99595efe | |||
| 268491b224 | |||
| 49ea9ed722 | |||
| d7108416c9 | |||
| b136630856 | |||
| fa6f31186a | |||
| b027b1d60f | |||
| 7ed1d931cb | |||
| 2570296b01 | |||
| f0a4526250 | |||
| c39332c7e7 | |||
| 3f4b6b22dc | |||
| e0549df03e | |||
| e46662a546 | |||
| 360a74e2fe | |||
| 597c4591bc | |||
| 80833ed703 | |||
| 106302189c | |||
| 05c5f98e8e | |||
| d556fa2cd8 | |||
| 9bd7f023b2 | |||
| 398109ac84 | |||
| 12b870ba66 | |||
| 6202fb8373 | |||
| ced818ad54 | |||
| ccbb6df749 | |||
| 6eb505a677 | |||
| 619783ca1b | |||
| 642aa0bc4b | |||
| 45b3067068 | |||
| b7858a66b9 | |||
| 4e203feaf4 | |||
| a513b47780 | |||
| 547a2831c7 | |||
| 5c52ffe24e | |||
| a5763d6fee | |||
| 95482c554d | |||
| 10758710d4 | |||
| 86cf9383ea | |||
| 307977d4cf | |||
| 1beff539d7 | |||
| df578d6ec5 | |||
| 6aae381e83 | |||
| 7ee9051a56 | |||
| eb11edabe0 | |||
| c067b90403 | |||
| 5b6770f3d2 | |||
| 718b80ba39 | |||
| 4d052d5119 | |||
| 7e4c643401 | |||
| e920338f21 | |||
| af2048570c | |||
| 1ee4f849cb | |||
| 703393fc63 | |||
| 81420ab246 | |||
| c94d19718b | |||
| e25c72ecdd | |||
| 780b81a59f | |||
| 9f1dda701d | |||
| e597a8d72e | |||
| de9a4b5164 | |||
| 319aca3101 | |||
| 9dc2c01aaa | |||
| 6164672421 | |||
| 61906613b0 | |||
| 3b48fa8e7d | |||
| 324b7d65e7 | |||
| 373a60b9ef | |||
| 2ef22e86e0 | |||
| 830f4f540f | |||
| 56ff5496bc | |||
| 20fbece14c | |||
| 9fbfd86cde | |||
| 7547bc66cf | |||
| 18a9fa7355 | |||
| b32af841c5 | |||
| 66b4252931 | |||
| 2c95eaa418 | |||
| 7382f52dc9 | |||
| 49dd299999 | |||
| e391b05513 | |||
| 2de62910fc | |||
| fc77b5b4ac | |||
| a83d916fad | |||
| e71a641379 | |||
| e2eca45188 | |||
| 4d78540658 | |||
| b83c3f265b | |||
| 30f5a3bb93 | |||
| 2e1e1e6034 | |||
| 991479fbf9 | |||
| 5660f98cc3 | |||
| 5bf0f9d630 | |||
| 15b72119eb | |||
| dc30e7a200 | |||
| db2293144a | |||
| 5016f45429 | |||
| 9fa4aa40b7 | |||
| 52f60c706a | |||
| fff4ead96a | |||
| 3574341b6b | |||
| cbabc80d92 | |||
| f4cf88c2ca | |||
| 6db95b554f | |||
| 105de7705a | |||
| 584dffea14 | |||
| 41b6d215bb | |||
| 9274f29ca9 | |||
| 08c87e57f8 | |||
| b21cdd5037 | |||
| 63ab8b2418 | |||
| cb7a343caf | |||
| 40542e6e26 | |||
| 9da05dd4cb | |||
| ae9da0abfb | |||
| d3ea334e7a | |||
| d76132a3fb | |||
| 223c473cf6 | |||
| fd57cfa1ae | |||
| f23bd2dc27 | |||
| 69062ba3ab | |||
| e75563cb32 | |||
| d63885a495 | |||
| f28a34fa99 | |||
| a1e8de4e00 | |||
| d247ba4751 | |||
| 27b7dc336a | |||
| 60a7c68aa6 | |||
| 78c103e62c | |||
| ffec1c77f2 | |||
| 5357181484 | |||
| 33ddb3ad4d | |||
| 1cd453db14 | |||
| 3b5932699c | |||
| bada81159d | |||
| 652da98c70 | |||
| e14e2c3b4d | |||
| f96a897821 | |||
| b74ae77745 | |||
| 564226be02 | |||
| f6c45fc68a | |||
| 35ba5771a5 | |||
| b2461f7192 | |||
| 60a54f404b | |||
| 921f261377 | |||
| d70a555c1c | |||
| 4c339360e9 | |||
| 731dad480d | |||
| a0f2357cb3 | |||
| e86ac75e9c | |||
| f51de2e488 | |||
| 5efefdcf16 | |||
| cabb2bb992 | |||
| d560f6c920 | |||
| 21432ba96e | |||
| c341597657 | |||
| 2a1420d4e7 | |||
| 28d88f6af4 | |||
| c4d2d287fc | |||
| 6a85546b76 | |||
| 2e92d0c821 | |||
| a499a3aa5e | |||
| 23ab3c4713 | |||
| da300aa9c3 | |||
| e225158a6f | |||
| 2ce55783d2 | |||
| 14eeee40b2 | |||
| 038dea9202 | |||
| 0ae3484171 | |||
| 54976c3249 | |||
| 4c06b44315 | |||
| 678b58e0b1 | |||
| 8f913c656c | |||
| 001b48a5c6 | |||
| 54929a1b92 | |||
| 92780e2683 | |||
| 2891988d3b | |||
| c1728914c6 | |||
| ed2f49e8d2 | |||
| 8a76a370a9 | |||
| 1160fd4331 | |||
| 0134c38759 | |||
| d079095517 | |||
| 028d628e9f | |||
| 5e4b62acfe | |||
| 9366fa8e95 | |||
| 369db3a8e3 | |||
| 8c360b2a3c | |||
| b66e7bed45 | |||
| e919482aa8 | |||
| dce45e7d58 | |||
| 1a0877e965 | |||
| 0361a18551 | |||
| 83d90f1463 | |||
| f661ae9d09 | |||
| bee4cb57f2 | |||
| 53b670b889 | |||
| e2600a3e44 | |||
| 25101b2ae0 | |||
| 4e7867fcc1 | |||
| 101ee64165 | |||
| 4c3e65791e | |||
| a9c8031b61 | |||
| afb3033913 | |||
| 2ad26640a2 | |||
| 2c0b08145f | |||
| aa9c9eda9e | |||
| 1353d61894 | |||
| 88ba6d8015 | |||
| 8b288a2072 | |||
| 4e90644527 | |||
| 6651b65373 | |||
| 705352099f | |||
| 2e28c9d793 | |||
| 2fe660a1d7 | |||
| b03ce0e9b4 | |||
| 386f5f596d | |||
| add53228b2 | |||
| d90008cc52 | |||
| dbf8f9ab38 | |||
| 81a99cf67b | |||
| 876af6fb02 | |||
| b3734a5f77 | |||
| 419ab6f00c | |||
| 5558b55e9f | |||
| 4b14d608f4 | |||
| 9428d86f2b | |||
| ddebf0daf2 | |||
| 3a44c62ecf | |||
| 184efd4f49 | |||
| 6b3c4cc379 | |||
| 0b137e087c | |||
| 37790c13a0 | |||
| 82057f08ce | |||
| 1553421e1a | |||
| f3ea109e6f | |||
| 90dbfe7660 | |||
| 125bad3154 | |||
| 30c83d6c81 | |||
| 4f12c118a5 | |||
| 423775d50e | |||
| 860a5c3e86 | |||
| 649e02f209 | |||
| b449305cc1 | |||
| 49bee6bad0 | |||
| ac277a1cce | |||
| a17310a83c | |||
| b509946b13 | |||
| a69ea58388 | |||
| 30530d058c | |||
| 436928d06a | |||
| 32a502d14e | |||
| 0d665c637f | |||
| 1b6a14ac39 | |||
| 367013f589 | |||
| c980a30bad | |||
| 78b459590c | |||
| 054e241033 | |||
| f7e9649be4 | |||
| fd1f6ec75c | |||
| 6b0d7cb26c | |||
| 3aea08df78 | |||
| 3c6f90e552 | |||
| 3703ca4df4 | |||
| 41b8281c73 | |||
| acd1f83bd0 | |||
| ba8371104d | |||
| 991682e9fd | |||
| f0de994059 | |||
| ebb2a9812c | |||
| 265c05927f | |||
| 05ad38ae2d | |||
| 596a2c8355 | |||
| 9f52b2c283 | |||
| 8035a407a6 | |||
| 97760c3fa4 | |||
| d75291097e | |||
| db632b7e22 | |||
| 1a75dfe075 | |||
| e00d88d82e | |||
| 04cce1826b | |||
| cc28cda053 | |||
| cfabc0e61f | |||
| 91b534d128 | |||
| 3268f43340 | |||
| 05e374934d | |||
| 3e1ff0ec67 | |||
| 65945dac09 | |||
| 1608da2dc8 | |||
| c340827381 | |||
| 74fa7ca25d | |||
| 5a9223afda | |||
| febcd73323 | |||
| df06236076 | |||
| b0d3fbba47 | |||
| adb6c7637e | |||
| 425f83b17d | |||
| 976415ff9d | |||
| 4d7fb3e8d6 | |||
| bcca3bf322 | |||
| 74aaa3408f | |||
| 2a5beee88c | |||
| cec9f7abfe | |||
| 284a9cd4c3 | |||
| 5955c101d4 | |||
| f80b910ba3 | |||
| 2b0521347b | |||
| 0c06a8d154 | |||
| b0e3a4e276 | |||
| b651466630 | |||
| 24c09c9201 | |||
| e48346a9ee | |||
| 9bd8bdaa5a | |||
| a137699d95 | |||
| f6a56c2f82 | |||
| dffa791607 | |||
| 5ce6555721 | |||
| 53b3ad186f | |||
| 82c1c5b3fe | |||
| 6d880bc3bb | |||
| 40281d595d | |||
| 85fab55e57 | |||
| 1d2eb8055e | |||
| 9e0b69312b | |||
| bbddbba340 | |||
| 0d01a6f552 | |||
| ae3672608d | |||
| e5c39fb2a9 | |||
| eb4b3f5976 | |||
| dbb070524f | |||
| 36b0b50ba4 | |||
| ac46b2053d | |||
| 0ffcccdae5 | |||
| 4777bd607e | |||
| 39e9b50482 | |||
| 30adb9c770 | |||
| b1d1497f4b | |||
| 9df3a94d33 | |||
| d4f335d068 | |||
| 74341b9b74 | |||
| 66ee2cb6ed | |||
| 1d4881cbbe | |||
| 04b917a60a | |||
| e6c99cd289 | |||
| 6bc5584add | |||
| 121f0185d6 | |||
| e7999f8450 | |||
| 0b29e42adb | |||
| fcc8b89e6b | |||
| 529d1c78c7 | |||
| 414486829a | |||
| 3e05be8eb8 | |||
| ae24a8e5ae | |||
| d2588f9d1d | |||
| 1eb9994d88 | |||
| a43b89f36e | |||
| 0ed34af19d | |||
| 71729c2855 | |||
| 6c8c430c2a | |||
| 57b97ad0bd | |||
| 56f7a859df | |||
| e5e14b9947 | |||
| 3d8bf36a30 | |||
| 85f7c2d040 | |||
| 26ea8f6dcb | |||
| e05fe1837d | |||
| 94762b56f6 | |||
| b3b688fa50 | |||
| 5eaa8de8f9 | |||
| 26d3c54aff | |||
| 349a62121c | |||
| bbb0e14633 | |||
| 42312d9def | |||
| 065d0e4ee3 | |||
| b772ad7094 | |||
| 444d366c39 | |||
| 8e4233b86a | |||
| 6424966b7a | |||
| 4e42d7df43 | |||
| 580ee5cc4a | |||
| 56a98a483f | |||
| df7a4eda8a | |||
| 91cc0b282a | |||
| 01d8aea4df | |||
| ee904060c5 | |||
| afb5538e83 | |||
| 1f24f105cc | |||
| 8f39ebbe5a | |||
| c1e720a49b | |||
| f38c8875b2 | |||
| e7e51f53ce | |||
| 5259de5872 | |||
| e2b9c87aa8 | |||
| 8c7cf0dbb0 | |||
| e6e9375b09 | |||
| c6096f9205 | |||
| 11614c2649 | |||
| 793bc8c585 | |||
| 335e88b738 | |||
| b77ea94976 | |||
| ae17a51c0d | |||
| ee7a83f124 | |||
| 67ac551a2f | |||
| 572ac616c1 | |||
| 96bf6a5bcb | |||
| c43d66c286 | |||
| 95fb5fa46c | |||
| d614913c11 | |||
| 3bfaac0844 | |||
| 14d0cbf6d7 | |||
| 61a163d773 | |||
| 3a644dad78 | |||
| d2c1c719bd | |||
| 333db4dc94 | |||
| cbcf4b6071 | |||
| e6e0aba8c3 | |||
| 85097a9958 | |||
| 7791c343c4 | |||
| 3bd762591a | |||
| 8e3b77aba8 | |||
| 36e3a02f67 | |||
| 566a242ba3 | |||
| 1e3b3c107c | |||
| 2ac33285c1 | |||
| 7cb8016df3 | |||
| cf3c5a878a | |||
| 2d20bde495 | |||
| b9e347ef50 | |||
| 6707c8750e | |||
| e5502c13ee | |||
| f30d2e43ea | |||
| 6c73f9d3fd | |||
| 1161aa829d | |||
| 01519f2fd5 | |||
| 33aad3a8ce | |||
| 4262c125c5 | |||
| a09d5959ef | |||
| d7bd3f8402 | |||
| 0fff6a2b74 | |||
| f4c0405221 | |||
| 49d337c830 | |||
| 294092979e | |||
| c454ede184 | |||
| d854c5003c | |||
| 66d8776b83 | |||
| ba6ecf35cf | |||
| 10cc9cf661 | |||
| 2db971eedd | |||
| 1775e80b41 | |||
| e4a93619db | |||
| 4d14b3bcb4 | |||
| 9f4f5f9346 | |||
| 0fae31fb54 | |||
| 8987a6630c | |||
| 10ff8e0426 | |||
| a0ae02168a | |||
| a3c1ac2030 | |||
| 629b248f53 | |||
| 62a72f0163 | |||
| 655931f0ea | |||
| ca36fabfc0 | |||
| 7bd62481ad | |||
| fbd27d7c45 | |||
| 3546391311 | |||
| 24c812115e | |||
| 28be0ad69b | |||
| f0980c0a98 | |||
| 1df4aa90ce | |||
| 6b3cf051f8 | |||
| 4ecd6e592b | |||
| dbddec33c8 | |||
| 401a5955a4 | |||
| 9a3b4167bb | |||
| 13bc6eeea4 | |||
| 2da18b6d33 | |||
| 6d37ed12d2 | |||
| eab23cd5b7 | |||
| d233706a2d | |||
| f1ab17ed4e | |||
| 6113164211 | |||
| 4db462a703 | |||
| a22c6d6c0c | |||
| 59fb7b020a | |||
| 65f079ebc4 | |||
| d16aa79492 | |||
| 5af0acc4af | |||
| a459364de3 | |||
| 277ef1a68f | |||
| 193c7c82c8 | |||
| f7d8ba408c | |||
| 9a8759efef | |||
| 054948e701 | |||
| 1c5ddd65b4 | |||
| b8697fb4ed | |||
| 03570275c1 | |||
| b5587f1937 | |||
| c4c6975f1b | |||
| 0be0fb2a57 | |||
| 115e6e7f9e | |||
| 3868a9a0f0 | |||
| ba5050ac7c | |||
| d936ca1ea0 | |||
| fd8c4d58bb | |||
| ce4b7b8b7d | |||
| 069a47220e | |||
| 66e4aaffc5 | |||
| 81336b58cb | |||
| b201670f7a | |||
| 4b051a0d3b | |||
| 45353465a6 | |||
| c63cb98019 | |||
| 773cf5ca08 | |||
| 2db03cb4a5 | |||
| eed873c6ec | |||
| 3d2d461867 | |||
| 36392d658e | |||
| 82696179e8 | |||
| 188bc28f6a | |||
| 240da5c8e0 | |||
| 689a0c0b49 | |||
| bc16b290ba | |||
| 96d32680fe | |||
| d782b3d21d | |||
| ed089b44b9 | |||
| 33f4af2e19 | |||
| 69f7382eec | |||
| 7e3293fc20 | |||
| e4a8283327 | |||
| 001baf4419 | |||
| d167290b28 | |||
| f4879d4723 | |||
| fd81c06c35 | |||
| 94afcec757 | |||
| 4f28e9e1fb | |||
| 0622509807 | |||
| 9ca2246bac | |||
| 647e2cafd7 | |||
| 5df854fcef | |||
| 260089431e | |||
| d0d8da8c08 | |||
| d1365b3466 | |||
| c949ca2a5c | |||
| d974b29f67 | |||
| cc7316bb35 | |||
| a0d8dcd974 | |||
| c642e326ce | |||
| 362a118782 | |||
| 3ab481df17 | |||
| 4e7150b470 | |||
| 1ced92be47 | |||
| 15dbea6899 | |||
| c4081393c1 | |||
| 1d81b73df9 | |||
| 18f885efab | |||
| bba088bee7 | |||
| 6cbb6bef0b | |||
| 8744c60563 | |||
| 8197c02dcf | |||
| 9faf0020cc | |||
| 53075e2570 | |||
| 264ca00db7 | |||
| 6b65ef6d88 | |||
| 5957d7f7be | |||
| 35c102137f | |||
| 5427d14416 | |||
| 178236d1ff | |||
| 736c880ba9 | |||
| 126f7aa892 | |||
| 2957f007e3 | |||
| 04501c93fe | |||
| 4236519b84 | |||
| e4944b4f2e | |||
| 2deb2f8eeb | |||
| 3fa398ec43 | |||
| 1851674b50 | |||
| c5ef5279d4 | |||
| d3c24d159f | |||
| 23f9f9064e | |||
| a134307dcd | |||
| c3b510c2d9 | |||
| e7fc24e48c | |||
| 6a88dc322a | |||
| 6b464e3558 | |||
| 76b0c7b765 | |||
| 91857e8f16 | |||
| ccda456c0a | |||
| 83bad13e9e | |||
| e6a206a430 | |||
| f52a1e4ded | |||
| a8e458339b | |||
| 6b5e9aec8e | |||
| 2ab0d97573 | |||
| 0c05fc1432 | |||
| 33eeb58521 | |||
| 8fafdb185c | |||
| c2c935ba81 | |||
| 2d73c8868b | |||
| b95bb1286b | |||
| 4237c8ec30 | |||
| 49b4b39055 | |||
| bf15fea135 | |||
| 47c03e376d | |||
| 1cabfac36c | |||
| 8e32276283 | |||
| 366b306df0 | |||
| 4bf1f798f5 | |||
| b2fdb69b4d | |||
| af2736daec | |||
| 5cad7d44a6 | |||
| 2b96be0ae8 | |||
| 2a89d8021c | |||
| 13deb4706c | |||
| 9b61adb97d | |||
| 333924cce1 | |||
| 574b82c0c7 | |||
| f60c772c11 | |||
| 107740ca5e | |||
| 88b990eb63 | |||
| d2e7d730ac | |||
| 817e4b663e | |||
| 214bb73454 | |||
| eba2c74bff | |||
| 7c5e6c808b | |||
| ebe5beaafd | |||
| 029a6095d9 | |||
| 2c0e59ae06 | |||
| 9d1a4c304a | |||
| 13b8a1e348 | |||
| 0d4945dc87 | |||
| e0b9c4a275 | |||
| fec6df65b3 | |||
| 78494e84d5 | |||
| 60d7c833c0 | |||
| 98dbbf11f3 | |||
| f4924e39d4 | |||
| 826e05c96e | |||
| d3f63e5903 | |||
| 80c034ec7c | |||
| b41f09b730 | |||
| 06185e1769 | |||
| f8fa7fe380 | |||
| 45dbe8d354 | |||
| ddb99dd638 | |||
| 41aa4e606b | |||
| e025a828ca | |||
| 807e17207a | |||
| 3e18f5f057 | |||
| 9637cc5690 | |||
| ded99a2cab | |||
| 45eecc0905 | |||
| 87f1a62ca4 | |||
| c6d531df95 | |||
| 8677c81da7 | |||
| 5595daf5a3 | |||
| 64b5afd820 | |||
| 7692061eef | |||
| f7f2272c50 | |||
| 03fbdc3f75 | |||
| ea6a4859ed | |||
| 615fa82d1f | |||
| b60b310121 | |||
| c7f7e562a0 | |||
| a317237404 | |||
| 51ea59d76a | |||
| 789b297f32 | |||
| 3b25f924cb | |||
| cc6282a6e3 | |||
| 206a3e093c | |||
| 19bde275a3 | |||
| 634ee450f4 | |||
| 750d7256fc | |||
| fae5df2ed8 | |||
| 01d9161772 | |||
| aceabb2f2f | |||
| 04f5fff7fa | |||
| dc5587eae2 | |||
| 7057034b75 | |||
| 1430ca30a3 | |||
| e63393e394 | |||
| 784f3ecf7e | |||
| 54ea70df98 | |||
| d05ec5e484 | |||
| c7575164cc | |||
| 99125dc743 | |||
| b78e970698 | |||
| 5b8be25938 | |||
| 29efdc5fc1 | |||
| a80872b60d | |||
| 822bb51b55 | |||
| c2fa79012e | |||
| 3fd37c6dc5 | |||
| 0ea815db49 | |||
| 91ed51ff5c | |||
| 4d0afc55c3 | |||
| 9a1566d665 | |||
| a713e33007 | |||
| c5411a25a9 | |||
| 95692fda52 | |||
| 813a028ed0 | |||
| 0c22081e5f | |||
| 6d9fadf351 | |||
| a213061f33 | |||
| d1a0a46141 | |||
| 187b186112 | |||
| 5041a35b95 | |||
| 92d4fcedee | |||
| c69df7cd3a | |||
| 67d8f48553 | |||
| b4a339f2e3 | |||
| 0d7bf58b60 | |||
| abb9930725 | |||
| 169310a9f6 | |||
| 23a0a6de4b | |||
| 0d2dbee84e | |||
| d8d22e34dd | |||
| 627ee002e8 | |||
| 8e73d1ce1f | |||
| b53d16d1d5 | |||
| f5819eafa9 | |||
| 5916e71d4f | |||
| 913b9b6447 | |||
| 8e55bb2a6c | |||
| 98d493504b | |||
| 3a3202fbc6 | |||
| aaf355e750 | |||
| 0683d2b4f4 | |||
| d7fdd3d7b8 | |||
| 83ebb24015 | |||
| 70f9cacdce | |||
| 6b33b254e9 | |||
| c0019cc305 | |||
| c067a1f0ec | |||
| 63345cd0d8 | |||
| e41d6261c2 | |||
| 3e80411d37 | |||
| f952c7c747 | |||
| 642256f9ba | |||
| c9c82da1f3 | |||
| 382a5ca6a2 | |||
| 96e8bb5b6f | |||
| 22afac2b90 | |||
| 01da0d1377 | |||
| 8ce58573df | |||
| ce0d874efd | |||
| 2c8b99337b | |||
| 5008e2c88b | |||
| 90fc9abeae | |||
| dc303cde21 | |||
| 24b33374b7 | |||
| 3315dc7f25 | |||
| 77b3295de5 | |||
| 1349aa6f2c | |||
| a75ccb6fbc | |||
| 7a28827602 | |||
| c61015b1fe | |||
| e935f8e2ff | |||
| 690c682847 | |||
| f541dd40db | |||
| c7bb861d3c | |||
| 188b290dd5 | |||
| c6ff961088 | |||
| c26990c22d | |||
| c34d839f9f | |||
| 5562364a98 | |||
| 32150e401e | |||
| aaec8bf423 | |||
| 0fcbda951a | |||
| e2734a2dc6 | |||
| 5adfbec847 | |||
| 4ef4605d6d | |||
| 2aa402f462 | |||
| 00f6bee454 | |||
| 6e1864d21c | |||
| fb2d611dcd | |||
| d890731716 | |||
| 9e8c9be1ea | |||
| 231ea8b026 | |||
| 9bc37f4400 | |||
| f29e303ce7 | |||
| 3c9143957c | |||
| 18b3c0b2fc | |||
| c59f6b7d0b | |||
| 5bbdb3a3a3 | |||
| 67ed8a9a4a | |||
| 27aa07307b | |||
| 4cc4d604bc | |||
| eec709c545 | |||
| 20b9f1ff59 | |||
| 561c583b3f | |||
| 8d5896ab7e | |||
| 802b1a70f8 | |||
| aaa4dd5c36 | |||
| 9d19ee7e4c | |||
| 8df3175f10 | |||
| ebb10e5597 | |||
| 047f883078 | |||
| 320c22e08a | |||
| a9398bf30f | |||
| 7829421085 | |||
| c50aabd916 | |||
| 3f3122bccc | |||
| fc1a006de1 | |||
| 754b368140 | |||
| a49e888ce6 | |||
| 99c663d9f3 | |||
| afac95e092 | |||
| 05486f9fa3 | |||
| cad46ae51c | |||
| 3424b2badd | |||
| 3445a28c4a | |||
| 7f6b83d50c | |||
| 72d4bfb32a | |||
| 37f7630a9e | |||
| 73c5c5d5d3 | |||
| 584869730a | |||
| 90ab448bca | |||
| 8becbdc1b2 | |||
| eeeb90c441 | |||
| 6efd400c98 | |||
| 5cfa4ba580 |
@@ -0,0 +1 @@
|
||||
*.odin linguist-language=Odin
|
||||
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: gingerbill
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
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:
|
||||
* Please paste `odin version` output:
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
Please describe the behavior you are expecting
|
||||
|
||||
## Current Behavior
|
||||
|
||||
What is the current behavior?
|
||||
|
||||
## Failure Information (for bugs)
|
||||
|
||||
Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template.
|
||||
|
||||
### Steps to Reproduce
|
||||
|
||||
Please provide detailed steps for reproducing the issue.
|
||||
|
||||
1. step 1
|
||||
2. step 2
|
||||
3. you get it...
|
||||
|
||||
### Failure Logs
|
||||
|
||||
Please include any relevant log snippets or files here.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**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 [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,64 @@
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM
|
||||
run: sudo apt-get install llvm-11 clang-11 llvm
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin run -llvm-api
|
||||
run: ./odin run examples/demo/demo.odin -llvm-api
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
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/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
|
||||
./odin run examples/demo/demo.odin -llvm-api
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download and unpack LLVM bins
|
||||
shell: powershell
|
||||
run: |
|
||||
cd bin
|
||||
$ProgressPreference = "SilentlyContinue";
|
||||
Invoke-WebRequest -Uri https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip -OutFile llvm-binaries.zip
|
||||
7z x llvm-binaries.zip > $null
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
./build.bat 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: Odin check
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/demo/demo.odin -vet
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
name: Nightly
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 0 20 * * *
|
||||
|
||||
jobs:
|
||||
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
|
||||
rm -f llvm-binaries.zip
|
||||
- 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 -llvm-api
|
||||
run: ./odin run examples/demo/demo.odin -llvm-api
|
||||
- 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
|
||||
echo "/usr/local/opt/llvm/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 -llvm-api
|
||||
run: ./odin run examples/demo/demo.odin -llvm-api
|
||||
- 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
|
||||
+25
-4
@@ -18,11 +18,14 @@ bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
![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
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
@@ -251,7 +254,25 @@ paket-files/
|
||||
|
||||
|
||||
# Project Specific
|
||||
|
||||
# - Windows
|
||||
*.sln
|
||||
!misc/llvm-bim/lli.exe
|
||||
!misc/llvm-bim/opt.exe
|
||||
builds
|
||||
builds/
|
||||
bin/
|
||||
*.exe
|
||||
*.obj
|
||||
*.pdb
|
||||
|
||||
# - Linux/MacOS
|
||||
odin
|
||||
odin.dSYM
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
|
||||
# temp files
|
||||
* .ll
|
||||
*.bc
|
||||
*.ll
|
||||
|
||||
*.sublime-workspace
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2020 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.
@@ -0,0 +1,34 @@
|
||||
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++14 -DGIT_SHA=\"$(GIT_SHA)\"
|
||||
CC=clang
|
||||
|
||||
OS=$(shell uname)
|
||||
|
||||
ifeq ($(OS), Darwin)
|
||||
LDFLAGS:=$(LDFLAGS) -liconv
|
||||
CFLAGS:=$(CFLAGS) $(shell llvm-config --cxxflags --ldflags) -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
|
||||
LDFLAGS:=$(LDFLAGS) -lLLVM-C
|
||||
endif
|
||||
ifeq ($(OS), Linux)
|
||||
CFLAGS:=$(CFLAGS) $(shell llvm-config-11 --cxxflags --ldflags) -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
|
||||
LDFLAGS:=$(LDFLAGS) $(shell llvm-config-11 --libs core native --system-libs)
|
||||
endif
|
||||
|
||||
all: debug demo
|
||||
|
||||
demo:
|
||||
./odin run examples/demo/demo.odin
|
||||
|
||||
debug:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# The Proposal Process
|
||||
|
||||
## Introduction
|
||||
|
||||
The Odin project's development process is driven by design and pragmatism. Significant changes to the language, libraries, or tools _must_ be first discussed, and maybe formally documented, before they can be implemented.
|
||||
|
||||
This document describes the process for proposing, documenting, and implementing changes to the Odin project.
|
||||
|
||||
## The Proposal Process
|
||||
|
||||
The proposal process is the process for reviewing a proposal and reaching a decision about whether to accept or decline the proposal.
|
||||
|
||||
1. [Ginger Bill](https://github.com/gingerBill) is [BDFL](https://wikipedia.org/wiki/Benevolent_dictator_for_life) and significant changes _must_ be passed by him.
|
||||
|
||||
2. The proposal author creates a brief issue describing the proposal.
|
||||
|
||||
Note: There is no need for a design document at this point.<br>
|
||||
Note: A non-proposal issue can be turned into a proposal by simply adding the _proposal_ label.
|
||||
|
||||
3. A discussion on the issue tracker will classify the proposal into one of three outcomes:
|
||||
* Accept proposal
|
||||
* Decline proposal
|
||||
* Ask for a design document.
|
||||
|
||||
If the proposal is accepted or declined, the process is done. Otherwise the discussion around the process is expected to identify issues that ought to be addressed in a more detailed design.
|
||||
|
||||
4. The proposal author writes a design document to work out details of the proposed design and address the concerns raised in the initial discussion.
|
||||
|
||||
5. Once comments and revisions on the design document calm, there is a final discussion on the issue, to reach one of two outcomes:
|
||||
* Accept proposal
|
||||
* Decline proposal
|
||||
|
||||
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
|
||||
|
||||
The design document should follow this template:
|
||||
|
||||
|
||||
```
|
||||
# Proposal: [Title]
|
||||
|
||||
Author(s): [Author Name, Co-Author Name]
|
||||
Last updated: [Date ISO-8601]
|
||||
Discussion at https://github.com/odin-lang/Odin/issues/######
|
||||
|
||||
## Abstract
|
||||
|
||||
## Background
|
||||
|
||||
## Proposal
|
||||
|
||||
## Rationale
|
||||
|
||||
## Compatibility
|
||||
|
||||
## Implementation
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Help
|
||||
|
||||
If you need help with this process, please contact an Odin contributor by posting an issue to the [issue tracker](https://github.com/odin-lang/Odin/issues).
|
||||
@@ -1,14 +1,159 @@
|
||||
<img src="logo-slim.png" alt="Odin logo" height="74">
|
||||
<p align="center">
|
||||
<img src="misc/logo-slim.png" alt="Odin logo" height="120">
|
||||
<br/>
|
||||
A fast, concise, readable, pragmatic and open sourced programming language.
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://github.com/odin-lang/odin/releases/latest">
|
||||
<img src="https://img.shields.io/github/release/odin-lang/odin.svg">
|
||||
</a>
|
||||
<a href="https://github.com/odin-lang/odin/releases/latest">
|
||||
<img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://discord.gg/hnwN2Rj">
|
||||
<img src="https://img.shields.io/discord/568138951836172421?logo=discord">
|
||||
</a>
|
||||
<a href="https://github.com/odin-lang/odin/actions">
|
||||
<img src="https://github.com/odin-lang/odin/workflows/CI/badge.svg?branch=master&event=push">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
# 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
|
||||
* joy of programming
|
||||
* metaprogramming
|
||||
* designed for good programmers
|
||||
|
||||
Website: [https://odin-lang.org/](https://odin-lang.org/)
|
||||
|
||||
```odin
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
program := "+ + * 😃 - /";
|
||||
accumulator := 0;
|
||||
|
||||
for token in program {
|
||||
switch token {
|
||||
case '+': accumulator += 1;
|
||||
case '-': accumulator -= 1;
|
||||
case '*': accumulator *= 2;
|
||||
case '/': accumulator /= 2;
|
||||
case '😃': accumulator *= accumulator;
|
||||
case: // Ignore everything else
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printf("The program \"%s\" calculates the value %d\n",
|
||||
program, accumulator);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
#### [Getting Started](https://odin-lang.org/docs/install)
|
||||
|
||||
Instructions for downloading and installing the Odin compiler and libraries.
|
||||
|
||||
### Learning Odin
|
||||
|
||||
#### [Overview of Odin](https://odin-lang.org/docs/overview)
|
||||
|
||||
An overview of the Odin programming language.
|
||||
|
||||
#### [Frequently Asked Questions (FAQ)](https://odin-lang.org/docs/faq)
|
||||
|
||||
Answers to common questions about Odin.
|
||||
|
||||
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
|
||||
|
||||
A wiki maintained by the Odin community.
|
||||
|
||||
#### [Odin Discord](https://discord.gg/sVBPHEv)
|
||||
|
||||
Get live support and talk with other odiners on the Odin Discord.
|
||||
|
||||
### References
|
||||
|
||||
#### [Language Specification](https://odin-lang.org/ref/spec)
|
||||
|
||||
The official Odin Language specification.
|
||||
|
||||
### Articles
|
||||
|
||||
#### [The Odin Blog](https://odin-lang.org/blog)
|
||||
|
||||
The official blog of the Odin programming language, featuring announcements, news, and in-depth articles by the Odin team and guests.
|
||||
|
||||
## Setup
|
||||
|
||||
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
|
||||
|
||||
- MacOS
|
||||
* Have LLVM explicitly installed (`brew install llvm`)
|
||||
* Have XCode installed (version X.X or later, for linking)
|
||||
* 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`.
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin) for more information.
|
||||
|
||||
## Requirements to build and run
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin).
|
||||
|
||||
- Windows
|
||||
* 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/amd64
|
||||
* LLVM explicitly installed (`brew install llvm`)
|
||||
* XCode installed (for the linker)
|
||||
|
||||
- GNU/Linux
|
||||
* 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
|
||||
|
||||
* The Odin compiler is still in development.
|
||||
|
||||
## Demonstrations:
|
||||
* First Talk & Demo
|
||||
@@ -18,38 +163,11 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
|
||||
* [Composition & Refactorability](https://www.youtube.com/watch?v=n1wemZfcbXM)
|
||||
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
|
||||
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
|
||||
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [when, for & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [Context Types, Unexported Entities, Labelled Branches](https://www.youtube.com/watch?v=CkHVwT1Qk-g)
|
||||
* [Bit Fields, i128 & u128, Syntax Changes](https://www.youtube.com/watch?v=NlTutcLyF64)
|
||||
* [Default and Named Arguments; Explicit Parametric Polymorphism](https://www.youtube.com/watch?v=-XQZE6S6zUU)
|
||||
* [Loadsachanges](https://www.youtube.com/watch?v=ar0vFMoMtrI)
|
||||
* [Packages, Bit Sets, cstring](https://youtu.be/b8bJbjiXZrQ)
|
||||
- [Q&A](https://youtu.be/5jmxyIfyyTk)
|
||||
|
||||
## Requirements to build and run
|
||||
|
||||
* Windows
|
||||
* x86-64
|
||||
* MSVC 2015 installed (C99 support)
|
||||
* Requires MSVC's link.exe as the linker
|
||||
- run `vcvarsall.bat` to setup the path
|
||||
|
||||
## Warnings
|
||||
|
||||
* This is still highly in development and the language's design is quite volatile.
|
||||
* Syntax is not fixed.
|
||||
|
||||
## Roadmap
|
||||
|
||||
Not in any particular order and not be implemented
|
||||
|
||||
* Compile Time Execution (CTE)
|
||||
- More metaprogramming madness
|
||||
- Compiler as a library
|
||||
- AST inspection and modification
|
||||
* CTE-based build system
|
||||
* Replace LLVM backend with my own custom backend
|
||||
* Improve SSA design to accommodate for lowering to a "bytecode"
|
||||
* SSA optimizations
|
||||
* Documentation Generator for "Entities"
|
||||
* Multiple Architecture support
|
||||
* Debug Information
|
||||
- pdb format too
|
||||
* Command Line Tooling
|
||||
* Compiler Internals:
|
||||
- Big numbers library
|
||||
- Multithreading for performance increase
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
This file is a list of the people responsible for ensuring that patches for a
|
||||
particular part of LLVM are reviewed, either by themself or by someone else.
|
||||
They are also the gatekeepers for their part of LLVM, with the final word on
|
||||
what goes in or not.
|
||||
|
||||
The list is sorted by surname and formatted to allow easy grepping and
|
||||
beautification by scripts. The fields are: name (N), email (E), web-address
|
||||
(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
|
||||
(S). Each entry should contain at least the (N), (E) and (D) fields.
|
||||
|
||||
N: Joe Abbey
|
||||
E: jabbey@arxan.com
|
||||
D: LLVM Bitcode (lib/Bitcode/* include/llvm/Bitcode/*)
|
||||
|
||||
N: Owen Anderson
|
||||
E: resistor@mac.com
|
||||
D: SelectionDAG (lib/CodeGen/SelectionDAG/*)
|
||||
|
||||
N: Rafael Avila de Espindola
|
||||
E: rafael.espindola@gmail.com
|
||||
D: Gold plugin (tools/gold/*)
|
||||
|
||||
N: Justin Bogner
|
||||
E: mail@justinbogner.com
|
||||
D: InstrProfiling and related parts of ProfileData
|
||||
|
||||
N: Chandler Carruth
|
||||
E: chandlerc@gmail.com
|
||||
E: chandlerc@google.com
|
||||
D: Config, ADT, Support, inlining & related passes, SROA/mem2reg & related passes, CMake, library layering
|
||||
|
||||
N: Evan Cheng
|
||||
E: evan.cheng@apple.com
|
||||
D: parts of code generator not covered by someone else
|
||||
|
||||
N: Eric Christopher
|
||||
E: echristo@gmail.com
|
||||
D: Debug Information, autotools/configure/make build, inline assembly
|
||||
|
||||
N: Greg Clayton
|
||||
E: gclayton@apple.com
|
||||
D: LLDB
|
||||
|
||||
N: Marshall Clow
|
||||
E: mclow.lists@gmail.com
|
||||
D: libc++
|
||||
|
||||
N: Peter Collingbourne
|
||||
E: peter@pcc.me.uk
|
||||
D: llgo
|
||||
|
||||
N: Quentin Colombet
|
||||
E: qcolombet@apple.com
|
||||
D: Register allocators
|
||||
|
||||
N: Duncan P. N. Exon Smith
|
||||
E: dexonsmith@apple.com
|
||||
D: Branch weights and BlockFrequencyInfo
|
||||
|
||||
N: Hal Finkel
|
||||
E: hfinkel@anl.gov
|
||||
D: BBVectorize, the loop reroller, alias analysis and the PowerPC target
|
||||
|
||||
N: Dan Gohman
|
||||
E: sunfish@mozilla.com
|
||||
D: WebAssembly Backend (lib/Target/WebAssembly/*)
|
||||
|
||||
N: Renato Golin
|
||||
E: renato.golin@linaro.org
|
||||
D: ARM Linux support
|
||||
|
||||
N: Venkatraman Govindaraju
|
||||
E: venkatra@cs.wisc.edu
|
||||
D: Sparc Backend (lib/Target/Sparc/*)
|
||||
|
||||
N: Tobias Grosser
|
||||
E: tobias@grosser.es
|
||||
D: Polly
|
||||
|
||||
N: James Grosbach
|
||||
E: grosbach@apple.com
|
||||
D: MC layer
|
||||
|
||||
N: Justin Holewinski
|
||||
E: jholewinski@nvidia.com
|
||||
D: NVPTX Target (lib/Target/NVPTX/*)
|
||||
|
||||
N: Lang Hames
|
||||
E: lhames@gmail.com
|
||||
D: MCJIT, RuntimeDyld and JIT event listeners
|
||||
|
||||
N: Galina Kistanova
|
||||
E: gkistanova@gmail.com
|
||||
D: LLVM Buildbot
|
||||
|
||||
N: Anton Korobeynikov
|
||||
E: anton@korobeynikov.info
|
||||
D: Exception handling, Windows codegen, ARM EABI
|
||||
|
||||
N: Benjamin Kramer
|
||||
E: benny.kra@gmail.com
|
||||
D: DWARF Parser
|
||||
|
||||
N: Sergei Larin
|
||||
E: slarin@codeaurora.org
|
||||
D: VLIW Instruction Scheduling, Packetization
|
||||
|
||||
N: Chris Lattner
|
||||
E: sabre@nondot.org
|
||||
W: http://nondot.org/~sabre/
|
||||
D: Everything not covered by someone else
|
||||
|
||||
N: David Majnemer
|
||||
E: david.majnemer@gmail.com
|
||||
D: IR Constant Folder, InstCombine
|
||||
|
||||
N: Dylan McKay
|
||||
E: dylanmckay34@gmail.com
|
||||
D: AVR Backend
|
||||
|
||||
N: Tim Northover
|
||||
E: t.p.northover@gmail.com
|
||||
D: AArch64 backend, misc ARM backend
|
||||
|
||||
N: Diego Novillo
|
||||
E: dnovillo@google.com
|
||||
D: SampleProfile and related parts of ProfileData
|
||||
|
||||
N: Jakob Olesen
|
||||
E: stoklund@2pi.dk
|
||||
D: TableGen
|
||||
|
||||
N: Richard Osborne
|
||||
E: richard@xmos.com
|
||||
D: XCore Backend
|
||||
|
||||
N: Krzysztof Parzyszek
|
||||
E: kparzysz@codeaurora.org
|
||||
D: Hexagon Backend
|
||||
|
||||
N: Paul Robinson
|
||||
E: paul_robinson@playstation.sony.com
|
||||
D: Sony PlayStation®4 support
|
||||
|
||||
N: Chad Rosier
|
||||
E: mcrosier@codeaurora.org
|
||||
D: Fast-Isel
|
||||
|
||||
N: Nadav Rotem
|
||||
E: nrotem@apple.com
|
||||
D: X86 Backend, Loop Vectorizer
|
||||
|
||||
N: Daniel Sanders
|
||||
E: daniel.sanders@imgtec.com
|
||||
D: MIPS Backend (lib/Target/Mips/*)
|
||||
|
||||
N: Duncan Sands
|
||||
E: baldrick@free.fr
|
||||
D: DragonEgg
|
||||
|
||||
N: Kostya Serebryany
|
||||
E: kcc@google.com
|
||||
D: AddressSanitizer, ThreadSanitizer (LLVM parts)
|
||||
|
||||
N: Michael Spencer
|
||||
E: bigcheesegs@gmail.com
|
||||
D: Windows parts of Support, Object, ar, nm, objdump, ranlib, size
|
||||
|
||||
N: Alexei Starovoitov
|
||||
E: alexei.starovoitov@gmail.com
|
||||
D: BPF backend
|
||||
|
||||
N: Tom Stellard
|
||||
E: thomas.stellard@amd.com
|
||||
E: mesa-dev@lists.freedesktop.org
|
||||
D: Release manager for the 3.5 and 3.6 branches, R600 Backend, libclc
|
||||
|
||||
N: Evgeniy Stepanov
|
||||
E: eugenis@google.com
|
||||
D: MemorySanitizer (LLVM part)
|
||||
|
||||
N: Andrew Trick
|
||||
E: atrick@apple.com
|
||||
D: IndVar Simplify, Loop Strength Reduction, Instruction Scheduling
|
||||
|
||||
N: Ulrich Weigand
|
||||
E: uweigand@de.ibm.com
|
||||
D: SystemZ Backend
|
||||
|
||||
N: Bill Wendling
|
||||
E: isanbard@gmail.com
|
||||
D: libLTO, IR Linker
|
||||
|
||||
N: Peter Zotov
|
||||
E: whitequark@whitequark.org
|
||||
D: OCaml bindings
|
||||
|
||||
N: Andrey Churbanov
|
||||
E: andrey.churbanov@intel.com
|
||||
D: OpenMP runtime library
|
||||
-467
@@ -1,467 +0,0 @@
|
||||
This file is a partial list of people who have contributed to the LLVM
|
||||
project. If you have contributed a patch or made some other contribution to
|
||||
LLVM, please submit a patch to this file to add yourself, and it will be
|
||||
done!
|
||||
|
||||
The list is sorted by surname and formatted to allow easy grepping and
|
||||
beautification by scripts. The fields are: name (N), email (E), web-address
|
||||
(W), PGP key ID and fingerprint (P), description (D), snail-mail address
|
||||
(S), and (I) IRC handle.
|
||||
|
||||
|
||||
N: Vikram Adve
|
||||
E: vadve@cs.uiuc.edu
|
||||
W: http://www.cs.uiuc.edu/~vadve/
|
||||
D: The Sparc64 backend, provider of much wisdom, and motivator for LLVM
|
||||
|
||||
N: Owen Anderson
|
||||
E: resistor@mac.com
|
||||
D: LCSSA pass and related LoopUnswitch work
|
||||
D: GVNPRE pass, DataLayout refactoring, random improvements
|
||||
|
||||
N: Henrik Bach
|
||||
D: MingW Win32 API portability layer
|
||||
|
||||
N: Aaron Ballman
|
||||
E: aaron@aaronballman.com
|
||||
D: __declspec attributes, Windows support, general bug fixing
|
||||
|
||||
N: Nate Begeman
|
||||
E: natebegeman@mac.com
|
||||
D: PowerPC backend developer
|
||||
D: Target-independent code generator and analysis improvements
|
||||
|
||||
N: Daniel Berlin
|
||||
E: dberlin@dberlin.org
|
||||
D: ET-Forest implementation.
|
||||
D: Sparse bitmap
|
||||
|
||||
N: David Blaikie
|
||||
E: dblaikie@gmail.com
|
||||
D: General bug fixing/fit & finish, mostly in Clang
|
||||
|
||||
N: Neil Booth
|
||||
E: neil@daikokuya.co.uk
|
||||
D: APFloat implementation.
|
||||
|
||||
N: Misha Brukman
|
||||
E: brukman+llvm@uiuc.edu
|
||||
W: http://misha.brukman.net
|
||||
D: Portions of X86 and Sparc JIT compilers, PowerPC backend
|
||||
D: Incremental bitcode loader
|
||||
|
||||
N: Cameron Buschardt
|
||||
E: buschard@uiuc.edu
|
||||
D: The `mem2reg' pass - promotes values stored in memory to registers
|
||||
|
||||
N: Brendon Cahoon
|
||||
E: bcahoon@codeaurora.org
|
||||
D: Loop unrolling with run-time trip counts.
|
||||
|
||||
N: Chandler Carruth
|
||||
E: chandlerc@gmail.com
|
||||
E: chandlerc@google.com
|
||||
D: Hashing algorithms and interfaces
|
||||
D: Inline cost analysis
|
||||
D: Machine block placement pass
|
||||
D: SROA
|
||||
|
||||
N: Casey Carter
|
||||
E: ccarter@uiuc.edu
|
||||
D: Fixes to the Reassociation pass, various improvement patches
|
||||
|
||||
N: Evan Cheng
|
||||
E: evan.cheng@apple.com
|
||||
D: ARM and X86 backends
|
||||
D: Instruction scheduler improvements
|
||||
D: Register allocator improvements
|
||||
D: Loop optimizer improvements
|
||||
D: Target-independent code generator improvements
|
||||
|
||||
N: Dan Villiom Podlaski Christiansen
|
||||
E: danchr@gmail.com
|
||||
E: danchr@cs.au.dk
|
||||
W: http://villiom.dk
|
||||
D: LLVM Makefile improvements
|
||||
D: Clang diagnostic & driver tweaks
|
||||
S: Aarhus, Denmark
|
||||
|
||||
N: Jeff Cohen
|
||||
E: jeffc@jolt-lang.org
|
||||
W: http://jolt-lang.org
|
||||
D: Native Win32 API portability layer
|
||||
|
||||
N: John T. Criswell
|
||||
E: criswell@uiuc.edu
|
||||
D: Original Autoconf support, documentation improvements, bug fixes
|
||||
|
||||
N: Anshuman Dasgupta
|
||||
E: adasgupt@codeaurora.org
|
||||
D: Deterministic finite automaton based infrastructure for VLIW packetization
|
||||
|
||||
N: Stefanus Du Toit
|
||||
E: stefanus.du.toit@intel.com
|
||||
D: Bug fixes and minor improvements
|
||||
|
||||
N: Rafael Avila de Espindola
|
||||
E: rafael.espindola@gmail.com
|
||||
D: The ARM backend
|
||||
|
||||
N: Dave Estes
|
||||
E: cestes@codeaurora.org
|
||||
D: AArch64 machine description for Cortex-A53
|
||||
|
||||
N: Alkis Evlogimenos
|
||||
E: alkis@evlogimenos.com
|
||||
D: Linear scan register allocator, many codegen improvements, Java frontend
|
||||
|
||||
N: Hal Finkel
|
||||
E: hfinkel@anl.gov
|
||||
D: Basic-block autovectorization, PowerPC backend improvements
|
||||
|
||||
N: Eric Fiselier
|
||||
E: eric@efcs.ca
|
||||
D: LIT patches and documentation.
|
||||
|
||||
N: Ryan Flynn
|
||||
E: pizza@parseerror.com
|
||||
D: Miscellaneous bug fixes
|
||||
|
||||
N: Brian Gaeke
|
||||
E: gaeke@uiuc.edu
|
||||
W: http://www.students.uiuc.edu/~gaeke/
|
||||
D: Portions of X86 static and JIT compilers; initial SparcV8 backend
|
||||
D: Dynamic trace optimizer
|
||||
D: FreeBSD/X86 compatibility fixes, the llvm-nm tool
|
||||
|
||||
N: Nicolas Geoffray
|
||||
E: nicolas.geoffray@lip6.fr
|
||||
W: http://www-src.lip6.fr/homepages/Nicolas.Geoffray/
|
||||
D: PPC backend fixes for Linux
|
||||
|
||||
N: Louis Gerbarg
|
||||
E: lgg@apple.com
|
||||
D: Portions of the PowerPC backend
|
||||
|
||||
N: Saem Ghani
|
||||
E: saemghani@gmail.com
|
||||
D: Callgraph class cleanups
|
||||
|
||||
N: Mikhail Glushenkov
|
||||
E: foldr@codedgers.com
|
||||
D: Author of llvmc2
|
||||
|
||||
N: Dan Gohman
|
||||
E: sunfish@mozilla.com
|
||||
D: Miscellaneous bug fixes
|
||||
D: WebAssembly Backend
|
||||
|
||||
N: David Goodwin
|
||||
E: david@goodwinz.net
|
||||
D: Thumb-2 code generator
|
||||
|
||||
N: David Greene
|
||||
E: greened@obbligato.org
|
||||
D: Miscellaneous bug fixes
|
||||
D: Register allocation refactoring
|
||||
|
||||
N: Gabor Greif
|
||||
E: ggreif@gmail.com
|
||||
D: Improvements for space efficiency
|
||||
|
||||
N: James Grosbach
|
||||
E: grosbach@apple.com
|
||||
I: grosbach
|
||||
D: SjLj exception handling support
|
||||
D: General fixes and improvements for the ARM back-end
|
||||
D: MCJIT
|
||||
D: ARM integrated assembler and assembly parser
|
||||
D: Led effort for the backend formerly known as ARM64
|
||||
|
||||
N: Lang Hames
|
||||
E: lhames@gmail.com
|
||||
D: PBQP-based register allocator
|
||||
|
||||
N: Gordon Henriksen
|
||||
E: gordonhenriksen@mac.com
|
||||
D: Pluggable GC support
|
||||
D: C interface
|
||||
D: Ocaml bindings
|
||||
|
||||
N: Raul Fernandes Herbster
|
||||
E: raul@dsc.ufcg.edu.br
|
||||
D: JIT support for ARM
|
||||
|
||||
N: Paolo Invernizzi
|
||||
E: arathorn@fastwebnet.it
|
||||
D: Visual C++ compatibility fixes
|
||||
|
||||
N: Patrick Jenkins
|
||||
E: patjenk@wam.umd.edu
|
||||
D: Nightly Tester
|
||||
|
||||
N: Dale Johannesen
|
||||
E: dalej@apple.com
|
||||
D: ARM constant islands improvements
|
||||
D: Tail merging improvements
|
||||
D: Rewrite X87 back end
|
||||
D: Use APFloat for floating point constants widely throughout compiler
|
||||
D: Implement X87 long double
|
||||
|
||||
N: Brad Jones
|
||||
E: kungfoomaster@nondot.org
|
||||
D: Support for packed types
|
||||
|
||||
N: Rod Kay
|
||||
E: rkay@auroraux.org
|
||||
D: Author of LLVM Ada bindings
|
||||
|
||||
N: Eric Kidd
|
||||
W: http://randomhacks.net/
|
||||
D: llvm-config script
|
||||
|
||||
N: Anton Korobeynikov
|
||||
E: asl@math.spbu.ru
|
||||
D: Mingw32 fixes, cross-compiling support, stdcall/fastcall calling conv.
|
||||
D: x86/linux PIC codegen, aliases, regparm/visibility attributes
|
||||
D: Switch lowering refactoring
|
||||
|
||||
N: Sumant Kowshik
|
||||
E: kowshik@uiuc.edu
|
||||
D: Author of the original C backend
|
||||
|
||||
N: Benjamin Kramer
|
||||
E: benny.kra@gmail.com
|
||||
D: Miscellaneous bug fixes
|
||||
|
||||
N: Sundeep Kushwaha
|
||||
E: sundeepk@codeaurora.org
|
||||
D: Implemented DFA-based target independent VLIW packetizer
|
||||
|
||||
N: Christopher Lamb
|
||||
E: christopher.lamb@gmail.com
|
||||
D: aligned load/store support, parts of noalias and restrict support
|
||||
D: vreg subreg infrastructure, X86 codegen improvements based on subregs
|
||||
D: address spaces
|
||||
|
||||
N: Jim Laskey
|
||||
E: jlaskey@apple.com
|
||||
D: Improvements to the PPC backend, instruction scheduling
|
||||
D: Debug and Dwarf implementation
|
||||
D: Auto upgrade mangler
|
||||
D: llvm-gcc4 svn wrangler
|
||||
|
||||
N: Chris Lattner
|
||||
E: sabre@nondot.org
|
||||
W: http://nondot.org/~sabre/
|
||||
D: Primary architect of LLVM
|
||||
|
||||
N: Tanya Lattner (Tanya Brethour)
|
||||
E: tonic@nondot.org
|
||||
W: http://nondot.org/~tonic/
|
||||
D: The initial llvm-ar tool, converted regression testsuite to dejagnu
|
||||
D: Modulo scheduling in the SparcV9 backend
|
||||
D: Release manager (1.7+)
|
||||
|
||||
N: Sylvestre Ledru
|
||||
E: sylvestre@debian.org
|
||||
W: http://sylvestre.ledru.info/
|
||||
W: http://llvm.org/apt/
|
||||
D: Debian and Ubuntu packaging
|
||||
D: Continuous integration with jenkins
|
||||
|
||||
N: Andrew Lenharth
|
||||
E: alenhar2@cs.uiuc.edu
|
||||
W: http://www.lenharth.org/~andrewl/
|
||||
D: Alpha backend
|
||||
D: Sampling based profiling
|
||||
|
||||
N: Nick Lewycky
|
||||
E: nicholas@mxc.ca
|
||||
D: PredicateSimplifier pass
|
||||
|
||||
N: Tony Linthicum, et. al.
|
||||
E: tlinth@codeaurora.org
|
||||
D: Backend for Qualcomm's Hexagon VLIW processor.
|
||||
|
||||
N: Bruno Cardoso Lopes
|
||||
E: bruno.cardoso@gmail.com
|
||||
I: bruno
|
||||
W: http://brunocardoso.cc
|
||||
D: Mips backend
|
||||
D: Random ARM integrated assembler and assembly parser improvements
|
||||
D: General X86 AVX1 support
|
||||
|
||||
N: Duraid Madina
|
||||
E: duraid@octopus.com.au
|
||||
W: http://kinoko.c.u-tokyo.ac.jp/~duraid/
|
||||
D: IA64 backend, BigBlock register allocator
|
||||
|
||||
N: John McCall
|
||||
E: rjmccall@apple.com
|
||||
D: Clang semantic analysis and IR generation
|
||||
|
||||
N: Michael McCracken
|
||||
E: michael.mccracken@gmail.com
|
||||
D: Line number support for llvmgcc
|
||||
|
||||
N: Vladimir Merzliakov
|
||||
E: wanderer@rsu.ru
|
||||
D: Test suite fixes for FreeBSD
|
||||
|
||||
N: Scott Michel
|
||||
E: scottm@aero.org
|
||||
D: Added STI Cell SPU backend.
|
||||
|
||||
N: Kai Nacke
|
||||
E: kai@redstar.de
|
||||
D: Support for implicit TLS model used with MS VC runtime
|
||||
D: Dumping of Win64 EH structures
|
||||
|
||||
N: Takumi Nakamura
|
||||
E: geek4civic@gmail.com
|
||||
E: chapuni@hf.rim.or.jp
|
||||
D: Cygwin and MinGW support.
|
||||
D: Win32 tweaks.
|
||||
S: Yokohama, Japan
|
||||
|
||||
N: Edward O'Callaghan
|
||||
E: eocallaghan@auroraux.org
|
||||
W: http://www.auroraux.org
|
||||
D: Add Clang support with various other improvements to utils/NewNightlyTest.pl
|
||||
D: Fix and maintain Solaris & AuroraUX support for llvm, various build warnings
|
||||
D: and error clean ups.
|
||||
|
||||
N: Morten Ofstad
|
||||
E: morten@hue.no
|
||||
D: Visual C++ compatibility fixes
|
||||
|
||||
N: Jakob Stoklund Olesen
|
||||
E: stoklund@2pi.dk
|
||||
D: Machine code verifier
|
||||
D: Blackfin backend
|
||||
D: Fast register allocator
|
||||
D: Greedy register allocator
|
||||
|
||||
N: Richard Osborne
|
||||
E: richard@xmos.com
|
||||
D: XCore backend
|
||||
|
||||
N: Devang Patel
|
||||
E: dpatel@apple.com
|
||||
D: LTO tool, PassManager rewrite, Loop Pass Manager, Loop Rotate
|
||||
D: GCC PCH Integration (llvm-gcc), llvm-gcc improvements
|
||||
D: Optimizer improvements, Loop Index Split
|
||||
|
||||
N: Ana Pazos
|
||||
E: apazos@codeaurora.org
|
||||
D: Fixes and improvements to the AArch64 backend
|
||||
|
||||
N: Wesley Peck
|
||||
E: peckw@wesleypeck.com
|
||||
W: http://wesleypeck.com/
|
||||
D: MicroBlaze backend
|
||||
|
||||
N: Francois Pichet
|
||||
E: pichet2000@gmail.com
|
||||
D: MSVC support
|
||||
|
||||
N: Vladimir Prus
|
||||
W: http://vladimir_prus.blogspot.com
|
||||
E: ghost@cs.msu.su
|
||||
D: Made inst_iterator behave like a proper iterator, LowerConstantExprs pass
|
||||
|
||||
N: Kalle Raiskila
|
||||
E: kalle.rasikila@nokia.com
|
||||
D: Some bugfixes to CellSPU
|
||||
|
||||
N: Xerxes Ranby
|
||||
E: xerxes@zafena.se
|
||||
D: Cmake dependency chain and various bug fixes
|
||||
|
||||
N: Alex Rosenberg
|
||||
E: alexr@leftfield.org
|
||||
I: arosenberg
|
||||
D: ARM calling conventions rewrite, hard float support
|
||||
|
||||
N: Chad Rosier
|
||||
E: mcrosier@codeaurora.org
|
||||
I: mcrosier
|
||||
D: AArch64 fast instruction selection pass
|
||||
D: Fixes and improvements to the ARM fast-isel pass
|
||||
D: Fixes and improvements to the AArch64 backend
|
||||
|
||||
N: Nadav Rotem
|
||||
E: nrotem@apple.com
|
||||
D: X86 code generation improvements, Loop Vectorizer.
|
||||
|
||||
N: Roman Samoilov
|
||||
E: roman@codedgers.com
|
||||
D: MSIL backend
|
||||
|
||||
N: Duncan Sands
|
||||
E: baldrick@free.fr
|
||||
I: baldrick
|
||||
D: Ada support in llvm-gcc
|
||||
D: Dragonegg plugin
|
||||
D: Exception handling improvements
|
||||
D: Type legalizer rewrite
|
||||
|
||||
N: Ruchira Sasanka
|
||||
E: sasanka@uiuc.edu
|
||||
D: Graph coloring register allocator for the Sparc64 backend
|
||||
|
||||
N: Arnold Schwaighofer
|
||||
E: arnold.schwaighofer@gmail.com
|
||||
D: Tail call optimization for the x86 backend
|
||||
|
||||
N: Shantonu Sen
|
||||
E: ssen@apple.com
|
||||
D: Miscellaneous bug fixes
|
||||
|
||||
N: Anand Shukla
|
||||
E: ashukla@cs.uiuc.edu
|
||||
D: The `paths' pass
|
||||
|
||||
N: Michael J. Spencer
|
||||
E: bigcheesegs@gmail.com
|
||||
D: Shepherding Windows COFF support into MC.
|
||||
D: Lots of Windows stuff.
|
||||
|
||||
N: Reid Spencer
|
||||
E: rspencer@reidspencer.com
|
||||
W: http://reidspencer.com/
|
||||
D: Lots of stuff, see: http://wiki.llvm.org/index.php/User:Reid
|
||||
|
||||
N: Alp Toker
|
||||
E: alp@nuanti.com
|
||||
W: http://atoker.com/
|
||||
D: C++ frontend next generation standards implementation
|
||||
|
||||
N: Craig Topper
|
||||
E: craig.topper@gmail.com
|
||||
D: X86 codegen and disassembler improvements. AVX2 support.
|
||||
|
||||
N: Edwin Torok
|
||||
E: edwintorok@gmail.com
|
||||
D: Miscellaneous bug fixes
|
||||
|
||||
N: Adam Treat
|
||||
E: manyoso@yahoo.com
|
||||
D: C++ bugs filed, and C++ front-end bug fixes.
|
||||
|
||||
N: Lauro Ramos Venancio
|
||||
E: lauro.venancio@indt.org.br
|
||||
D: ARM backend improvements
|
||||
D: Thread Local Storage implementation
|
||||
|
||||
N: Bill Wendling
|
||||
I: wendling
|
||||
E: isanbard@gmail.com
|
||||
D: Release manager, IR Linker, LTO
|
||||
D: Bunches of stuff
|
||||
|
||||
N: Bob Wilson
|
||||
E: bob.wilson@acm.org
|
||||
D: Advanced SIMD (NEON) support in the ARM backend.
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
==============================================================================
|
||||
LLVM Release License
|
||||
==============================================================================
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2003-2015 University of Illinois at Urbana-Champaign.
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
LLVM Team
|
||||
|
||||
University of Illinois at Urbana-Champaign
|
||||
|
||||
http://llvm.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal with
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the LLVM Team, University of Illinois at
|
||||
Urbana-Champaign, nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this Software without specific
|
||||
prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
Copyrights and Licenses for Third Party Software Distributed with LLVM:
|
||||
==============================================================================
|
||||
The LLVM software contains code written by third parties. Such software will
|
||||
have its own individual LICENSE.TXT file in the directory in which it appears.
|
||||
This file will describe the copyrights, license, and restrictions which apply
|
||||
to that code.
|
||||
|
||||
The disclaimer of warranty in the University of Illinois Open Source License
|
||||
applies to all code in the LLVM Distribution, and nothing in any of the
|
||||
other licenses gives permission to use the names of the LLVM Team or the
|
||||
University of Illinois to endorse or promote products derived from this
|
||||
Software.
|
||||
|
||||
The following pieces of software have additional or alternate copyrights,
|
||||
licenses, and/or restrictions:
|
||||
|
||||
Program Directory
|
||||
------- ---------
|
||||
Autoconf llvm/autoconf
|
||||
llvm/projects/ModuleMaker/autoconf
|
||||
Google Test llvm/utils/unittest/googletest
|
||||
OpenBSD regex llvm/lib/Support/{reg*, COPYRIGHT.regex}
|
||||
pyyaml tests llvm/test/YAMLParser/{*.data, LICENSE.TXT}
|
||||
ARM contributions llvm/lib/Target/ARM/LICENSE.TXT
|
||||
md5 contributions llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h
|
||||
@@ -0,0 +1,30 @@
|
||||
# The Odin Programming Language
|
||||
|
||||
## Setup
|
||||
|
||||
Odin only supports x86-64 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
|
||||
|
||||
- MacOS
|
||||
* Have LLVM explicitly installed (`brew install llvm`)
|
||||
* Have XCode installed (version X.X or later, for linking)
|
||||
* 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`.
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin) for more information.
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
release_mode=$1
|
||||
|
||||
warnings_to_disable="-std=c++11 -Wno-switch"
|
||||
|
||||
libraries="-pthread -ldl -lm -lstdc++ -lz -lcurses -lxml2"
|
||||
other_args="-DLLVM_BACKEND_SUPPORT"
|
||||
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"
|
||||
|
||||
llvm_config_flags="--cxxflags --ldflags"
|
||||
# llvm_config_flags="${llvm_config_flags} --link-static"
|
||||
llvm_config="llvm-config ${llvm_config_flags}"
|
||||
|
||||
other_args="${other_args} -liconv"
|
||||
other_args="${other_args} `${llvm_config}` -lLLVM-C"
|
||||
elif [[ "$(uname)" == "FreeBSD" ]]; then
|
||||
compiler="clang"
|
||||
fi
|
||||
|
||||
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin
|
||||
# && ./odin run examples/demo/demo.odin -llvm-api
|
||||
@@ -4,28 +4,46 @@
|
||||
set exe_name=odin.exe
|
||||
|
||||
:: Debug = 0, Release = 1
|
||||
set release_mode=0
|
||||
set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
|
||||
if "%1" == "1" (
|
||||
set release_mode=1
|
||||
) else if "%1" == "release" (
|
||||
set release_mode=1
|
||||
) else (
|
||||
set release_mode=0
|
||||
)
|
||||
|
||||
:: Normal = 0, CI Nightly = 1
|
||||
if "%2" == "1" (
|
||||
set nightly=1
|
||||
) else (
|
||||
set nightly=0
|
||||
)
|
||||
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||
set compiler_defines= -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
|
||||
|
||||
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
|
||||
set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
|
||||
)
|
||||
|
||||
set compiler_warnings= ^
|
||||
-W4 -WX ^
|
||||
-wd4100 -wd4101 -wd4127 -wd4189 ^
|
||||
-wd4201 -wd4204 -wd4244 ^
|
||||
-wd4306 ^
|
||||
-wd4201 -wd4204 ^
|
||||
-wd4456 -wd4457 -wd4480 ^
|
||||
-wd4505 -wd4512 -wd4550
|
||||
-wd4512
|
||||
|
||||
set compiler_includes=
|
||||
set libs= ^
|
||||
kernel32.lib
|
||||
rem "src\dyncall\lib\*.lib"
|
||||
kernel32.lib ^
|
||||
bin\llvm\windows\LLVM-C.lib
|
||||
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||
|
||||
@@ -35,24 +53,17 @@ 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.c" ^
|
||||
/link %linker_settings% -OUT:%exe_name% ^
|
||||
&& odin run code/demo.odin
|
||||
rem && odin build_dll code/example.odin ^
|
||||
rem odin run code/demo.odin
|
||||
cl %compiler_settings% "src\main.cpp" /link %linker_settings% -OUT:%exe_name%
|
||||
|
||||
rem pushd src\asm
|
||||
rem nasm hellope.asm -fwin64 -o hellope.obj ^
|
||||
rem && cl /nologo hellope.obj /link kernel32.lib /entry:main ^
|
||||
rem && hellope.exe
|
||||
rem popd
|
||||
if %errorlevel% neq 0 goto end_of_build
|
||||
if %release_mode% EQU 0 odin run examples/demo/demo.odin
|
||||
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
:end_of_build
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env 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"
|
||||
elif [[ "$(uname)" == "FreeBSD" ]]; then
|
||||
compiler="clang"
|
||||
fi
|
||||
|
||||
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin && ./odin run examples/demo/demo.odin
|
||||
@@ -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"
|
||||
-148
@@ -1,148 +0,0 @@
|
||||
#import "fmt.odin";
|
||||
#import "atomic.odin";
|
||||
#import "hash.odin";
|
||||
#import "math.odin";
|
||||
#import "mem.odin";
|
||||
#import "opengl.odin";
|
||||
#import "os.odin";
|
||||
|
||||
main :: proc() {
|
||||
/*
|
||||
Version 0.1.1
|
||||
|
||||
Added:
|
||||
* Dynamic Arrays `[dynamic]Type`
|
||||
* Dynamic Maps `map[Key]Value`
|
||||
* Dynamic array and map literals
|
||||
* Custom struct alignemnt `struct #align 8 { bar: i8 }`
|
||||
* Allow `_` in numbers
|
||||
* Variadic `append`
|
||||
* fmt.sprint*
|
||||
* Entities prefixes with an underscore do not get exported on imports
|
||||
* Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps
|
||||
* enum types have an implict `names` field, a []string of all the names in that enum
|
||||
* immutable variables are "completely immutable" - rules need a full explanation
|
||||
* `slice_to_bytes` - convert any slice to a slice of bytes
|
||||
* `union_cast` allows for optional ok check
|
||||
* Record type field `names` (struct/raw_union/enum)
|
||||
* ?: ternary operator
|
||||
* Unions with variants and common fields
|
||||
* New built-in procedures
|
||||
- `delete` to delete map entries `delete(m, key)`
|
||||
- `clear` to clear dynamic maps and arrays `clear(map_or_array)`
|
||||
- `reserve` to reserve space for the dynamic maps and arrays `reserve(map_or_array)`
|
||||
* Unexported entities and fields using an underscore prefix
|
||||
|
||||
Removed:
|
||||
* Maybe/option types
|
||||
* Remove `type` keyword and other "reserved" keywords
|
||||
* `compile_assert` and `assert`return the value of the condition for semantic reasons
|
||||
|
||||
Changed:
|
||||
* thread_local -> #thread_local
|
||||
* #include -> #load
|
||||
* Files only get checked if they are actually used
|
||||
* match x in y {} // For type match statements
|
||||
* Version numbering now starts from 0.1.0 and uses the convention:
|
||||
- major.minor.patch
|
||||
* Core library additions to Windows specific stuff
|
||||
|
||||
Fixes:
|
||||
* Many fmt.* fixes
|
||||
* Overloading bug due to comparison of named types
|
||||
* Overloading bug due to `#import .` collision
|
||||
* disallow a `cast` from pointers of unions
|
||||
* Minor bugs in generated IR code for slices
|
||||
|
||||
To come very Soon™:
|
||||
* Linux and OS X builds (unofficial ones do exist already)
|
||||
*/
|
||||
|
||||
{
|
||||
Fruit :: enum {
|
||||
APPLE,
|
||||
BANANA,
|
||||
COCONUT,
|
||||
}
|
||||
fmt.println(Fruit.names);
|
||||
}
|
||||
|
||||
{
|
||||
m: map[f32]int;
|
||||
reserve(m, 16);
|
||||
defer free(m);
|
||||
|
||||
m[1.0] = 1278;
|
||||
m[2.0] = 7643;
|
||||
m[3.0] = 564;
|
||||
_, ok := m[3.0];
|
||||
c := m[3.0];
|
||||
assert(ok && c == 564);
|
||||
|
||||
fmt.print("map[");
|
||||
i := 0;
|
||||
for val, key in m {
|
||||
if i > 0 {
|
||||
fmt.print(", ");
|
||||
}
|
||||
fmt.printf("%v=%v", key, val);
|
||||
i += 1;
|
||||
}
|
||||
fmt.println("]");
|
||||
}
|
||||
{
|
||||
m := map[string]u32{
|
||||
"a" = 56,
|
||||
"b" = 13453,
|
||||
"c" = 7654,
|
||||
};
|
||||
defer free(m);
|
||||
|
||||
c := m["c"];
|
||||
_, ok := m["c"];
|
||||
assert(ok && c == 7654);
|
||||
|
||||
fmt.println(m);
|
||||
}
|
||||
|
||||
{
|
||||
x: [dynamic]f64;
|
||||
reserve(x, 16);
|
||||
defer free(x);
|
||||
append(x, 2_000_000.500_000, 3, 5, 7);
|
||||
|
||||
for p, i in x {
|
||||
if i > 0 { fmt.print(", "); }
|
||||
fmt.print(p);
|
||||
}
|
||||
fmt.println();
|
||||
}
|
||||
|
||||
{
|
||||
x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
|
||||
defer free(x);
|
||||
fmt.println(x);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
Vec3 :: [vector 3]f32;
|
||||
|
||||
x := Vec3{1, 2, 3};
|
||||
y := Vec3{4, 5, 6};
|
||||
fmt.println(x < y);
|
||||
fmt.println(x + y);
|
||||
fmt.println(x - y);
|
||||
fmt.println(x * y);
|
||||
fmt.println(x / y);
|
||||
|
||||
for i in x {
|
||||
fmt.println(i);
|
||||
}
|
||||
|
||||
compile_assert(size_of([vector 7]bool) == size_of([7]bool));
|
||||
compile_assert(size_of([vector 7]i32) == size_of([7]i32));
|
||||
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
|
||||
}
|
||||
}
|
||||
|
||||
-215
@@ -1,215 +0,0 @@
|
||||
#import "win32.odin" when ODIN_OS == "windows";
|
||||
#import "fmt.odin";
|
||||
#import "math.odin";
|
||||
#import "os.odin";
|
||||
#import gl "opengl.odin";
|
||||
|
||||
TWO_HEARTS :: '💕';
|
||||
|
||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
|
||||
time_now :: proc() -> f64 {
|
||||
assert(win32_perf_count_freq != 0);
|
||||
|
||||
counter: i64;
|
||||
win32.QueryPerformanceCounter(^counter);
|
||||
result := counter as f64 / win32_perf_count_freq as f64;
|
||||
return result;
|
||||
}
|
||||
win32_print_last_error :: proc() {
|
||||
err_code := win32.GetLastError() as int;
|
||||
if err_code != 0 {
|
||||
fmt.println("GetLastError: %", err_code);
|
||||
}
|
||||
}
|
||||
|
||||
// Yuk!
|
||||
to_c_string :: proc(s: string) -> []u8 {
|
||||
c_str := new_slice(u8, s.count+1);
|
||||
copy(c_str, s as []byte);
|
||||
c_str[s.count] = 0;
|
||||
return c_str;
|
||||
}
|
||||
|
||||
|
||||
Window :: struct {
|
||||
width, height: int;
|
||||
wc: win32.WNDCLASSEXA;
|
||||
dc: win32.HDC;
|
||||
hwnd: win32.HWND;
|
||||
opengl_context, rc: win32.HGLRC;
|
||||
c_title: []u8;
|
||||
}
|
||||
|
||||
make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC) -> (Window, bool) {
|
||||
using win32;
|
||||
|
||||
w: Window;
|
||||
w.width, w.height = msg, height;
|
||||
|
||||
class_name := "Win32-Odin-Window\x00";
|
||||
c_class_name := class_name.data;
|
||||
if title[title.count-1] != 0 {
|
||||
w.c_title = to_c_string(title);
|
||||
} else {
|
||||
w.c_title = title as []u8;
|
||||
}
|
||||
|
||||
instance := GetModuleHandleA(nil);
|
||||
|
||||
w.wc = WNDCLASSEXA{
|
||||
size = size_of(WNDCLASSEXA) as u32,
|
||||
style = CS_VREDRAW | CS_HREDRAW,
|
||||
instance = instance as HINSTANCE,
|
||||
class_name = c_class_name,
|
||||
wnd_proc = window_proc,
|
||||
};
|
||||
|
||||
if RegisterClassExA(^w.wc) == 0 {
|
||||
win32_print_last_error();
|
||||
return w, false;
|
||||
}
|
||||
|
||||
w.hwnd = CreateWindowExA(0,
|
||||
c_class_name, w.c_title.data,
|
||||
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
w.width as i32, w.height as i32,
|
||||
nil, nil, instance, nil);
|
||||
|
||||
if w.hwnd == nil {
|
||||
win32_print_last_error();
|
||||
return w, false;
|
||||
}
|
||||
|
||||
w.dc = GetDC(w.hwnd);
|
||||
|
||||
{
|
||||
pfd := PIXELFORMATDESCRIPTOR{
|
||||
size = size_of(PIXELFORMATDESCRIPTOR) as u32,
|
||||
version = 1,
|
||||
flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
|
||||
pixel_type = PFD_TYPE_RGBA,
|
||||
color_bits = 32,
|
||||
alpha_bits = 8,
|
||||
depth_bits = 24,
|
||||
stencil_bits = 8,
|
||||
layer_type = PFD_MAIN_PLANE,
|
||||
};
|
||||
|
||||
SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil);
|
||||
w.opengl_context = wglCreateContext(w.dc);
|
||||
wglMakeCurrent(w.dc, w.opengl_context);
|
||||
|
||||
attribs := [8]i32{
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
0, // NOTE(bill): tells the proc that this is the end of attribs
|
||||
};
|
||||
|
||||
wglCreateContextAttribsARB := wglGetProcAddress(("wglCreateContextAttribsARB\x00" as string).data) as wglCreateContextAttribsARBType;
|
||||
w.rc = wglCreateContextAttribsARB(w.dc, 0, ^attribs[0]);
|
||||
wglMakeCurrent(w.dc, w.rc);
|
||||
SwapBuffers(w.dc);
|
||||
}
|
||||
|
||||
return w, true;
|
||||
}
|
||||
|
||||
destroy_window :: proc(w: ^Window) {
|
||||
free(w.c_title.data);
|
||||
}
|
||||
|
||||
display_window :: proc(w: ^Window) {
|
||||
win32.SwapBuffers(w.dc);
|
||||
}
|
||||
|
||||
|
||||
run :: proc() {
|
||||
using win32;
|
||||
using math;
|
||||
|
||||
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline {
|
||||
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
|
||||
os.exit(0);
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProcA(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
window, window_success := make_window("Odin Language Demo", 854, 480, win32_proc);
|
||||
if !window_success {
|
||||
return;
|
||||
}
|
||||
defer destroy_window(^window);
|
||||
|
||||
gl.init();
|
||||
|
||||
|
||||
prev_time := time_now();
|
||||
running := true;
|
||||
|
||||
pos := Vec2{100, 100};
|
||||
|
||||
for running {
|
||||
curr_time := time_now();
|
||||
dt := (curr_time - prev_time) as f32;
|
||||
prev_time = curr_time;
|
||||
|
||||
msg: MSG;
|
||||
for PeekMessageA(^msg, nil, 0, 0, PM_REMOVE) > 0 {
|
||||
if msg.message == WM_QUIT {
|
||||
running = false;
|
||||
}
|
||||
TranslateMessage(^msg);
|
||||
DispatchMessageA(^msg);
|
||||
}
|
||||
|
||||
if is_key_down(Key_Code.ESCAPE) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
{
|
||||
SPEED :: 500;
|
||||
v: Vec2;
|
||||
|
||||
if is_key_down(Key_Code.RIGHT) { v[0] += 1; }
|
||||
if is_key_down(Key_Code.LEFT) { v[0] -= 1; }
|
||||
if is_key_down(Key_Code.UP) { v[1] += 1; }
|
||||
if is_key_down(Key_Code.DOWN) { v[1] -= 1; }
|
||||
|
||||
v = vec2_norm0(v);
|
||||
|
||||
pos += v * Vec2{SPEED * dt};
|
||||
}
|
||||
|
||||
|
||||
gl.ClearColor(0.5, 0.7, 1.0, 1.0);
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
gl.LoadIdentity();
|
||||
gl.Ortho(0, window.width as f64,
|
||||
0, window.height as f64, 0, 1);
|
||||
|
||||
draw_rect :: proc(x, y, w, h: f32) {
|
||||
gl.Begin(gl.TRIANGLES);
|
||||
defer gl.End();
|
||||
|
||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
||||
gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, y, 0);
|
||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
|
||||
|
||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
|
||||
gl.Color3f(1, 1, 0); gl.Vertex3f(x, y+h, 0);
|
||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
||||
}
|
||||
|
||||
draw_rect(pos.x, pos.y, 50, 50);
|
||||
|
||||
display_window(^window);
|
||||
ms_to_sleep := (16 - 1000*dt) as i32;
|
||||
if ms_to_sleep > 0 {
|
||||
win32.Sleep(ms_to_sleep);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
#import "fmt.odin";
|
||||
|
||||
#foreign_system_library ws2 "Ws2_32.lib" when ODIN_OS == "windows";
|
||||
|
||||
|
||||
SOCKET :: #type uint;
|
||||
INVALID_SOCKET :: ~(cast(SOCKET)0);
|
||||
|
||||
AF :: enum i32 {
|
||||
UNSPEC = 0, // unspecified
|
||||
UNIX = 1, // local to host (pipes, portals)
|
||||
INET = 2, // internetwork: UDP, TCP, etc.
|
||||
IMPLINK = 3, // arpanet imp addresses
|
||||
PUP = 4, // pup protocols: e.g. BSP
|
||||
CHAOS = 5, // mit CHAOS protocols
|
||||
NS = 6, // XEROX NS protocols
|
||||
ISO = 7, // ISO protocols
|
||||
OSI = ISO, // OSI is ISO
|
||||
ECMA = 8, // european computer manufacturers
|
||||
DATAKIT = 9, // datakit protocols
|
||||
CCITT = 10, // CCITT protocols, X.25 etc
|
||||
SNA = 11, // IBM SNA
|
||||
DECnet = 12, // DECnet
|
||||
DLI = 13, // Direct data link interface
|
||||
LAT = 14, // LAT
|
||||
HYLINK = 15, // NSC Hyperchannel
|
||||
APPLETALK = 16, // AppleTalk
|
||||
ROUTE = 17, // Internal Routing Protocol
|
||||
LINK = 18, // Link layer interface
|
||||
XTP = 19, // eXpress Transfer Protocol (no AF)
|
||||
COIP = 20, // connection-oriented IP, aka ST II
|
||||
CNT = 21, // Computer Network Technology
|
||||
RTIP = 22, // Help Identify RTIP packets
|
||||
IPX = 23, // Novell Internet Protocol
|
||||
SIP = 24, // Simple Internet Protocol
|
||||
PIP = 25, // Help Identify PIP packets
|
||||
MAX = 26,
|
||||
};
|
||||
|
||||
SOCK_STREAM :: 1;
|
||||
SOCKET_ERROR :: -1;
|
||||
IPPROTO_TCP :: 6;
|
||||
AI_PASSIVE :: 0x0020;
|
||||
SOMAXCONN :: 128;
|
||||
|
||||
SD_RECEIVE :: 0;
|
||||
SD_SEND :: 1;
|
||||
SD_BOTH :: 2;
|
||||
|
||||
WSADESCRIPTION_LEN :: 256;
|
||||
WSASYS_STATUS_LEN :: 128;
|
||||
WSADATA :: struct #ordered {
|
||||
version: i16,
|
||||
high_version: i16,
|
||||
|
||||
|
||||
// NOTE(bill): This is x64 ordering
|
||||
max_sockets: u16,
|
||||
max_udp_dg: u16,
|
||||
vendor_info: ^byte,
|
||||
description: [WSADESCRIPTION_LEN+1]byte,
|
||||
system_status: [WSASYS_STATUS_LEN+1]byte,
|
||||
}
|
||||
|
||||
addrinfo :: struct #ordered {
|
||||
flags: i32,
|
||||
family: i32,
|
||||
socktype: i32,
|
||||
protocol: i32,
|
||||
addrlen: uint,
|
||||
canonname: ^u8,
|
||||
addr: ^sockaddr,
|
||||
next: ^addrinfo,
|
||||
}
|
||||
|
||||
sockaddr :: struct #ordered {
|
||||
family: u16,
|
||||
data: [14]byte,
|
||||
}
|
||||
|
||||
|
||||
WSAStartup :: proc(version_requested: i16, data: ^WSADATA) -> i32 #foreign ws2;
|
||||
WSACleanup :: proc() -> i32 #foreign ws2;
|
||||
getaddrinfo :: proc(node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32 #foreign ws2;
|
||||
freeaddrinfo :: proc(ai: ^addrinfo) #foreign ws2;
|
||||
socket :: proc(af, type_, protocol: i32) -> SOCKET #foreign ws2;
|
||||
closesocket :: proc(s: SOCKET) -> i32 #foreign ws2;
|
||||
bind :: proc(s: SOCKET, name: ^sockaddr, name_len: i32) -> i32 #foreign ws2;
|
||||
listen :: proc(s: SOCKET, back_log: i32) -> i32 #foreign ws2;
|
||||
accept :: proc(s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET #foreign ws2;
|
||||
recv :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign ws2;
|
||||
send :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign ws2;
|
||||
shutdown :: proc(s: SOCKET, how: i32) -> i32 #foreign ws2;
|
||||
WSAGetLastError :: proc() -> i32 #foreign ws2;
|
||||
|
||||
to_c_string :: proc(s: string) -> ^byte {
|
||||
c_str := new_slice(byte, s.count+1);
|
||||
assert(c_str.data != nil);
|
||||
copy(c_str, cast([]byte)s);
|
||||
c_str[s.count] = 0;
|
||||
return c_str.data;
|
||||
}
|
||||
|
||||
run :: proc() {
|
||||
wsa: WSADATA;
|
||||
res: ^addrinfo = nil;
|
||||
hints: addrinfo;
|
||||
s, client: SOCKET;
|
||||
|
||||
if WSAStartup(2 | (2 << 8), ^wsa) != 0 {
|
||||
fmt.println("WSAStartup failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer WSACleanup();
|
||||
|
||||
hints.family = cast(i32)AF.INET;
|
||||
hints.socktype = SOCK_STREAM;
|
||||
hints.protocol = IPPROTO_TCP;
|
||||
hints.flags = AI_PASSIVE;
|
||||
|
||||
if getaddrinfo(nil, to_c_string("8080"), ^hints, ^res) != 0 {
|
||||
fmt.println("getaddrinfo failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer freeaddrinfo(res);
|
||||
|
||||
s = socket(res.family, res.socktype, res.protocol);
|
||||
if s == INVALID_SOCKET {
|
||||
fmt.println("socket failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer closesocket(s);
|
||||
|
||||
bind(s, res.addr, cast(i32)res.addrlen);
|
||||
listen(s, SOMAXCONN);
|
||||
|
||||
client = accept(s, nil, 0);
|
||||
if client == INVALID_SOCKET {
|
||||
fmt.println("socket failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer closesocket(client);
|
||||
|
||||
html :=
|
||||
`HTTP/1.1 200 OK
|
||||
Connection: close
|
||||
Content-type: text/html
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Demo Title</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 style="color: orange;">Odin Server Demo</h1>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
buf: [1024]byte;
|
||||
for {
|
||||
bytes := recv(client, ^buf[0], cast(i32)buf.count, 0);
|
||||
if bytes > 0 {
|
||||
// fmt.println(buf[:bytes] as string)
|
||||
bytes_sent := send(client, html.data, cast(i32)(html.count-1), 0);
|
||||
if bytes_sent == SOCKET_ERROR {
|
||||
fmt.println("send failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
break;
|
||||
} else if bytes == 0 {
|
||||
fmt.println("Connection closing...");
|
||||
break;
|
||||
} else {
|
||||
fmt.println("recv failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
shutdown(client, SD_SEND);
|
||||
}
|
||||
@@ -1,900 +0,0 @@
|
||||
// Demo 002
|
||||
#include "basic.odin"
|
||||
#include "math.odin"
|
||||
// #include "game.odin"
|
||||
|
||||
#thread_local tls_int: int
|
||||
|
||||
main :: proc() {
|
||||
// Forenotes
|
||||
|
||||
// Semicolons are now optional
|
||||
// Rule for when a semicolon is expected after a statement
|
||||
// - If the next token is not on the same line
|
||||
// - if the next token is a closing brace }
|
||||
// - Otherwise, a semicolon is needed
|
||||
//
|
||||
// Expections:
|
||||
// for, if, match
|
||||
// if x := thing(); x < 123 {}
|
||||
// for i := 0; i < 123; i++ {}
|
||||
|
||||
// Q: Should I use the new rule or go back to the old one without optional semicolons?
|
||||
|
||||
|
||||
// #thread_local - see runtime.odin and above at `tls_int`
|
||||
// #foreign_system_library - see win32.odin
|
||||
|
||||
// struct_compound_literals()
|
||||
// enumerations()
|
||||
// variadic_procedures()
|
||||
// new_builtins()
|
||||
// match_statement()
|
||||
// namespacing()
|
||||
// subtyping()
|
||||
// tagged_unions()
|
||||
}
|
||||
|
||||
struct_compound_literals :: proc() {
|
||||
Thing :: type struct {
|
||||
id: int
|
||||
x: f32
|
||||
name: string
|
||||
}
|
||||
{
|
||||
t1: Thing
|
||||
t1.id = 1
|
||||
|
||||
t3 := Thing{}
|
||||
t4 := Thing{1, 2, "Fred"}
|
||||
// t5 := Thing{1, 2}
|
||||
|
||||
t6 := Thing{
|
||||
name = "Tom",
|
||||
x = 23,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enumerations :: proc() {
|
||||
{
|
||||
Fruit :: type enum {
|
||||
APPLE, // 0
|
||||
BANANA, // 1
|
||||
PEAR, // 2
|
||||
}
|
||||
|
||||
f := Fruit.APPLE
|
||||
// g12: int = Fruit.BANANA
|
||||
g: int = Fruit.BANANA as int
|
||||
// However, you can use enums are index values as _any_ integer allowed
|
||||
}
|
||||
{
|
||||
Fruit1 :: type enum int {
|
||||
APPLE,
|
||||
BANANA,
|
||||
PEAR,
|
||||
}
|
||||
|
||||
Fruit2 :: type enum u8 {
|
||||
APPLE,
|
||||
BANANA,
|
||||
PEAR,
|
||||
}
|
||||
|
||||
Fruit3 :: type enum u8 {
|
||||
APPLE = 1,
|
||||
BANANA, // 2
|
||||
PEAR = 5,
|
||||
TOMATO, // 6
|
||||
}
|
||||
}
|
||||
|
||||
// Q: remove the need for `type` if it's a record (struct/enum/raw_union/union)?
|
||||
}
|
||||
|
||||
variadic_procedures :: proc() {
|
||||
print_ints :: proc(args: ..int) {
|
||||
for i := 0; i < len(args); i++ {
|
||||
if i > 0 {
|
||||
print_string(", ")
|
||||
}
|
||||
print_int(args[i])
|
||||
}
|
||||
}
|
||||
|
||||
print_ints(); // nl()
|
||||
print_ints(1); nl()
|
||||
print_ints(1, 2, 3); nl()
|
||||
|
||||
print_prefix_f32s :: proc(prefix: string, args: ..f32) {
|
||||
print_string(prefix)
|
||||
print_string(": ")
|
||||
for i := 0; i < len(args); i++ {
|
||||
if i > 0 {
|
||||
print_string(", ")
|
||||
}
|
||||
print_f32(args[i])
|
||||
}
|
||||
}
|
||||
|
||||
print_prefix_f32s("a"); nl()
|
||||
print_prefix_f32s("b", 1); nl()
|
||||
7 print_prefix_f32s("c", 1, 2, 3); nl()
|
||||
|
||||
// Internally, the variadic procedures get allocated to an array on the stack,
|
||||
// and this array is passed a slice
|
||||
|
||||
// This is first step for a `print` procedure but I do not have an `any` type
|
||||
// yet as this requires a few other things first - i.e. introspection
|
||||
|
||||
// NOTE(bill): I haven't yet added the feature of expanding a slice or array into
|
||||
// a variadic a parameter but it's pretty trivial to add
|
||||
}
|
||||
|
||||
new_builtins :: proc() {
|
||||
{
|
||||
a := new(int)
|
||||
b := new_slice(int, 12)
|
||||
c := new_slice(int, 12, 16)
|
||||
|
||||
defer delete(a)
|
||||
defer delete(b)
|
||||
defer delete(c)
|
||||
|
||||
// NOTE(bill): These use the current context's allocator not the default allocator
|
||||
// see runtime.odin
|
||||
|
||||
// Q: Should this be `free` rather than `delete` and should I overload it for slices too?
|
||||
|
||||
{
|
||||
prev_context := context
|
||||
defer context = prev_context
|
||||
// Q: Should I add a `push_context` feature to the language?
|
||||
|
||||
context.allocator = __default_allocator()
|
||||
|
||||
a := new(int)
|
||||
defer delete(a)
|
||||
|
||||
// Do whatever
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
a: int = 123
|
||||
b: type_of_val(a) = 321
|
||||
|
||||
// NOTE(bill): This matches the current naming scheme
|
||||
// size_of
|
||||
// align_of
|
||||
// offset_of
|
||||
//
|
||||
// size_of_val
|
||||
// align_of_val
|
||||
// offset_of_val
|
||||
// type_of_val
|
||||
}
|
||||
|
||||
{
|
||||
// Compile time assert
|
||||
COND :: true
|
||||
compile_assert(COND)
|
||||
// compile_assert(!COND)
|
||||
|
||||
// Runtime assert
|
||||
x := true
|
||||
assert(x)
|
||||
// assert(!x)
|
||||
}
|
||||
|
||||
{
|
||||
x: ^u32 = null;
|
||||
y := ptr_offset(x, 100)
|
||||
z := ptr_sub(y, x)
|
||||
w := slice_ptr(x, 12)
|
||||
t := slice_ptr(x, 12, 16)
|
||||
|
||||
// NOTE(bill): These are here because I've removed:
|
||||
// pointer arithmetic
|
||||
// pointer indexing
|
||||
// pointer slicing
|
||||
|
||||
// Reason
|
||||
|
||||
a: [16]int
|
||||
a[1] = 1;
|
||||
b := ^a
|
||||
// Auto pointer deref
|
||||
// consistent with record members
|
||||
assert(b[1] == 1)
|
||||
|
||||
// Q: Should I add them back in at the cost of inconsitency?
|
||||
}
|
||||
|
||||
{
|
||||
a, b := -1, 2
|
||||
print_int(min(a, b)); nl()
|
||||
print_int(max(a, b)); nl()
|
||||
print_int(abs(a)); nl()
|
||||
|
||||
// These work at compile time too
|
||||
A :: -1
|
||||
B :: 2
|
||||
C :: min(A, B)
|
||||
D :: max(A, B)
|
||||
E :: abs(A)
|
||||
|
||||
print_int(C); nl()
|
||||
print_int(D); nl()
|
||||
print_int(E); nl()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
match_statement :: proc() {
|
||||
// NOTE(bill): `match` statements are similar to `switch` statements
|
||||
// in other languages but there are few differences
|
||||
|
||||
{
|
||||
match x := 5; x {
|
||||
case 1: // cases must be constant expression
|
||||
print_string("1!\n")
|
||||
// break by default
|
||||
|
||||
case 2:
|
||||
s := "2!\n"; // Each case has its own scope
|
||||
print_string(s)
|
||||
break // explicit break
|
||||
|
||||
case 3, 4: // multiple cases
|
||||
print_string("3 or 4!\n")
|
||||
|
||||
case 5:
|
||||
print_string("5!\n")
|
||||
fallthrough // explicit fallthrough
|
||||
|
||||
default:
|
||||
print_string("default!\n")
|
||||
}
|
||||
|
||||
|
||||
|
||||
match x := 1.5; x {
|
||||
case 1.5:
|
||||
print_string("1.5!\n")
|
||||
// break by default
|
||||
case MATH_TAU:
|
||||
print_string("τ!\n")
|
||||
default:
|
||||
print_string("default!\n")
|
||||
}
|
||||
|
||||
|
||||
|
||||
match x := "Hello"; x {
|
||||
case "Hello":
|
||||
print_string("greeting\n")
|
||||
// break by default
|
||||
case "Goodbye":
|
||||
print_string("farewell\n")
|
||||
default:
|
||||
print_string("???\n")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
a := 53
|
||||
match {
|
||||
case a == 1:
|
||||
print_string("one\n")
|
||||
case a == 2:
|
||||
print_string("a couple\n")
|
||||
case a < 7, a == 7:
|
||||
print_string("a few\n")
|
||||
case a < 12: // intentional bug
|
||||
print_string("several\n")
|
||||
case a >= 12 && a < 100:
|
||||
print_string("dozens\n")
|
||||
case a >= 100 && a < 1000:
|
||||
print_string("hundreds\n")
|
||||
default:
|
||||
print_string("a fuck ton\n")
|
||||
}
|
||||
|
||||
// Identical to this
|
||||
|
||||
b := 53
|
||||
if b == 1 {
|
||||
print_string("one\n")
|
||||
} else if b == 2 {
|
||||
print_string("a couple\n")
|
||||
} else if b < 7 || b == 7 {
|
||||
print_string("a few\n")
|
||||
} else if b < 12 { // intentional bug
|
||||
print_string("several\n")
|
||||
} else if b >= 12 && b < 100 {
|
||||
print_string("dozens\n")
|
||||
} else if b >= 100 && b < 1000 {
|
||||
print_string("hundreds\n")
|
||||
} else {
|
||||
print_string("a fuck ton\n")
|
||||
}
|
||||
|
||||
// However, match statements allow for `break` and `fallthrough` unlike
|
||||
// an if statement
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 :: type struct {
|
||||
x, y, z: f32
|
||||
}
|
||||
|
||||
print_floats :: proc(args: ..f32) {
|
||||
for i := 0; i < len(args); i++ {
|
||||
if i > 0 {
|
||||
print_string(", ")
|
||||
}
|
||||
print_f32(args[i])
|
||||
}
|
||||
print_nl()
|
||||
}
|
||||
|
||||
namespacing :: proc() {
|
||||
{
|
||||
Thing :: type struct {
|
||||
x: f32
|
||||
name: string
|
||||
}
|
||||
|
||||
a: Thing
|
||||
a.x = 3
|
||||
{
|
||||
Thing :: type struct {
|
||||
y: int
|
||||
test: bool
|
||||
}
|
||||
|
||||
b: Thing // Uses this scope's Thing
|
||||
b.test = true
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Entity :: type struct {
|
||||
Guid :: type int
|
||||
Nested :: type struct {
|
||||
MyInt :: type int
|
||||
i: int
|
||||
}
|
||||
|
||||
CONSTANT :: 123
|
||||
|
||||
|
||||
guid: Guid
|
||||
name: string
|
||||
pos: Vector3
|
||||
vel: Vector3
|
||||
nested: Nested
|
||||
}
|
||||
|
||||
guid: Entity.Guid = Entity.CONSTANT
|
||||
i: Entity.Nested.MyInt
|
||||
|
||||
|
||||
|
||||
{
|
||||
using Entity
|
||||
guid: Guid = CONSTANT
|
||||
using Nested
|
||||
i: MyInt
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
using Entity.Nested
|
||||
guid: Entity.Guid = Entity.CONSTANT
|
||||
i: MyInt
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
e: Entity
|
||||
using e
|
||||
guid = 27832
|
||||
name = "Bob"
|
||||
|
||||
print_int(e.guid as int); nl()
|
||||
print_string(e.name); nl()
|
||||
}
|
||||
|
||||
{
|
||||
using e: Entity
|
||||
guid = 78456
|
||||
name = "Thing"
|
||||
|
||||
print_int(e.guid as int); nl()
|
||||
print_string(e.name); nl()
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Entity :: type struct {
|
||||
Guid :: type int
|
||||
Nested :: type struct {
|
||||
MyInt :: type int
|
||||
i: int
|
||||
}
|
||||
|
||||
CONSTANT :: 123
|
||||
|
||||
|
||||
guid: Guid
|
||||
name: string
|
||||
using pos: Vector3
|
||||
vel: Vector3
|
||||
using nested: ^Nested
|
||||
}
|
||||
|
||||
e := Entity{nested = new(Entity.Nested)}
|
||||
e.x = 123
|
||||
e.i = Entity.CONSTANT
|
||||
}
|
||||
|
||||
|
||||
|
||||
{
|
||||
Entity :: type struct {
|
||||
position: Vector3
|
||||
}
|
||||
|
||||
print_pos_1 :: proc(entity: ^Entity) {
|
||||
print_string("print_pos_1: ")
|
||||
print_floats(entity.position.x, entity.position.y, entity.position.z)
|
||||
}
|
||||
|
||||
print_pos_2 :: proc(entity: ^Entity) {
|
||||
using entity
|
||||
print_string("print_pos_2: ")
|
||||
print_floats(position.x, position.y, position.z)
|
||||
}
|
||||
|
||||
print_pos_3 :: proc(using entity: ^Entity) {
|
||||
print_string("print_pos_3: ")
|
||||
print_floats(position.x, position.y, position.z)
|
||||
}
|
||||
|
||||
print_pos_4 :: proc(using entity: ^Entity) {
|
||||
using position
|
||||
print_string("print_pos_4: ")
|
||||
print_floats(x, y, z)
|
||||
}
|
||||
|
||||
e := Entity{position = Vector3{1, 2, 3}}
|
||||
print_pos_1(^e)
|
||||
print_pos_2(^e)
|
||||
print_pos_3(^e)
|
||||
print_pos_4(^e)
|
||||
|
||||
// This is similar to C++'s `this` pointer that is implicit and only available in methods
|
||||
}
|
||||
}
|
||||
|
||||
subtyping :: proc() {
|
||||
{
|
||||
// C way for subtyping/subclassing
|
||||
|
||||
Entity :: type struct {
|
||||
position: Vector3
|
||||
}
|
||||
|
||||
Frog :: type struct {
|
||||
entity: Entity
|
||||
jump_height: f32
|
||||
}
|
||||
|
||||
f: Frog
|
||||
f.entity.position = Vector3{1, 2, 3}
|
||||
|
||||
using f.entity
|
||||
position = Vector3{1, 2, 3}
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
// C++ way for subtyping/subclassing
|
||||
|
||||
Entity :: type struct {
|
||||
position: Vector3
|
||||
}
|
||||
|
||||
Frog :: type struct {
|
||||
using entity: Entity
|
||||
jump_height: f32
|
||||
}
|
||||
|
||||
f: Frog
|
||||
f.position = Vector3{1, 2, 3}
|
||||
|
||||
|
||||
print_pos :: proc(using entity: Entity) {
|
||||
print_string("print_pos: ")
|
||||
print_floats(position.x, position.y, position.z)
|
||||
}
|
||||
|
||||
print_pos(f.entity)
|
||||
print_pos(f)
|
||||
|
||||
// Subtype Polymorphism
|
||||
}
|
||||
|
||||
{
|
||||
// More than C++ way for subtyping/subclassing
|
||||
|
||||
Entity :: type struct {
|
||||
position: Vector3
|
||||
}
|
||||
|
||||
Frog :: type struct {
|
||||
jump_height: f32
|
||||
using entity: ^Entity // Doesn't have to be first member!
|
||||
}
|
||||
|
||||
f: Frog
|
||||
f.entity = new(Entity)
|
||||
f.position = Vector3{1, 2, 3}
|
||||
|
||||
|
||||
print_pos :: proc(using entity: ^Entity) {
|
||||
print_string("print_pos: ")
|
||||
print_floats(position.x, position.y, position.z)
|
||||
}
|
||||
|
||||
print_pos(f.entity)
|
||||
print_pos(^f)
|
||||
print_pos(f)
|
||||
}
|
||||
|
||||
{
|
||||
// More efficient subtyping
|
||||
|
||||
Entity :: type struct {
|
||||
position: Vector3
|
||||
}
|
||||
|
||||
Frog :: type struct {
|
||||
jump_height: f32
|
||||
using entity: ^Entity
|
||||
}
|
||||
|
||||
MAX_ENTITES :: 64
|
||||
entities: [MAX_ENTITES]Entity
|
||||
entity_count := 0
|
||||
|
||||
next_entity :: proc(entities: []Entity, entity_count: ^int) -> ^Entity {
|
||||
e := ^entities[entity_count^]
|
||||
entity_count^++
|
||||
return e
|
||||
}
|
||||
|
||||
f: Frog
|
||||
f.entity = next_entity(entities[:], ^entity_count)
|
||||
f.position = Vector3{3, 4, 6}
|
||||
|
||||
using f.position
|
||||
print_floats(x, y, z)
|
||||
}
|
||||
|
||||
{
|
||||
// Down casting
|
||||
|
||||
Entity :: type struct {
|
||||
position: Vector3
|
||||
}
|
||||
|
||||
Frog :: type struct {
|
||||
jump_height: f32
|
||||
using entity: Entity
|
||||
}
|
||||
|
||||
f: Frog
|
||||
f.jump_height = 564
|
||||
e := ^f.entity
|
||||
|
||||
frog := e down_cast ^Frog
|
||||
print_string("down_cast: ")
|
||||
print_f32(frog.jump_height); nl()
|
||||
|
||||
// NOTE(bill): `down_cast` is unsafe and there are not check are compile time or run time
|
||||
// Q: Should I completely remove `down_cast` as I added it in about 30 minutes
|
||||
}
|
||||
|
||||
{
|
||||
// Multiple "inheritance"/subclassing
|
||||
|
||||
Entity :: type struct {
|
||||
position: Vector3
|
||||
}
|
||||
Climber :: type struct {
|
||||
speed: f32
|
||||
}
|
||||
|
||||
Frog :: type struct {
|
||||
using entity: Entity
|
||||
using climber: Climber
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tagged_unions :: proc() {
|
||||
{
|
||||
EntityKind :: type enum {
|
||||
INVALID,
|
||||
FROG,
|
||||
GIRAFFE,
|
||||
HELICOPTER,
|
||||
}
|
||||
|
||||
Entity :: type struct {
|
||||
kind: EntityKind
|
||||
using data: raw_union {
|
||||
frog: struct {
|
||||
jump_height: f32
|
||||
colour: u32
|
||||
}
|
||||
giraffe: struct {
|
||||
neck_length: f32
|
||||
spot_count: int
|
||||
}
|
||||
helicopter: struct {
|
||||
blade_count: int
|
||||
weight: f32
|
||||
pilot_name: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
e: Entity
|
||||
e.kind = EntityKind.FROG
|
||||
e.frog.jump_height = 12
|
||||
|
||||
f: type_of_val(e.frog);
|
||||
|
||||
// But this is very unsafe and extremely cumbersome to write
|
||||
// In C++, I use macros to alleviate this but it's not a solution
|
||||
}
|
||||
|
||||
{
|
||||
Entity :: type union {
|
||||
Frog: struct {
|
||||
jump_height: f32
|
||||
colour: u32
|
||||
}
|
||||
Giraffe: struct {
|
||||
neck_length: f32
|
||||
spot_count: int
|
||||
}
|
||||
Helicopter: struct {
|
||||
blade_count: int
|
||||
weight: f32
|
||||
pilot_name: string
|
||||
}
|
||||
}
|
||||
|
||||
using Entity
|
||||
f1: Frog = Frog{12, 0xff9900}
|
||||
f2: Entity = Frog{12, 0xff9900} // Implicit cast
|
||||
f3 := Frog{12, 0xff9900} as Entity // Explicit cast
|
||||
|
||||
// f3.Frog.jump_height = 12 // There are "members" of a union
|
||||
|
||||
|
||||
|
||||
e, f, g, h: Entity
|
||||
f = Frog{12, 0xff9900}
|
||||
g = Giraffe{2.1, 23}
|
||||
h = Helicopter{4, 1000, "Frank"}
|
||||
|
||||
|
||||
|
||||
|
||||
// Requires a pointer to the union
|
||||
// `x` will be a pointer to type of the case
|
||||
|
||||
match type x : ^f {
|
||||
case Frog:
|
||||
print_string("Frog!\n")
|
||||
print_f32(x.jump_height); nl()
|
||||
x.jump_height = 3
|
||||
print_f32(x.jump_height); nl()
|
||||
case Giraffe:
|
||||
print_string("Giraffe!\n")
|
||||
case Helicopter:
|
||||
print_string("ROFLCOPTER!\n")
|
||||
default:
|
||||
print_string("invalid entity\n")
|
||||
}
|
||||
|
||||
|
||||
// Q: Allow for a non pointer version with takes a copy instead?
|
||||
// Or it takes the pointer the data and not a copy
|
||||
|
||||
|
||||
fp := ^f as ^Frog // Unsafe
|
||||
print_f32(fp.jump_height); nl()
|
||||
|
||||
|
||||
// Internals of a tagged union
|
||||
/*
|
||||
struct {
|
||||
data: [size_of_biggest_tag]u8
|
||||
tag_index: int
|
||||
}
|
||||
*/
|
||||
// This is to allow for pointer casting if needed
|
||||
|
||||
|
||||
// Advantage over subtyping version
|
||||
MAX_ENTITES :: 64
|
||||
entities: [MAX_ENTITES]Entity
|
||||
|
||||
entities[0] = Frog{}
|
||||
entities[1] = Helicopter{}
|
||||
// etc.
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// Transliteration of code from this actual compiler
|
||||
// Some stuff is missing
|
||||
Type :: type struct {}
|
||||
Scope :: type struct {}
|
||||
Token :: type struct {}
|
||||
AstNode :: type struct {}
|
||||
ExactValue :: type struct {}
|
||||
|
||||
EntityKind :: type enum {
|
||||
Invalid,
|
||||
Constant,
|
||||
Variable,
|
||||
UsingVariable,
|
||||
TypeName,
|
||||
Procedure,
|
||||
Builtin,
|
||||
Count,
|
||||
}
|
||||
|
||||
Entity :: type struct {
|
||||
Guid :: type i64
|
||||
|
||||
kind: EntityKind
|
||||
guid: Guid
|
||||
|
||||
scope: ^Scope
|
||||
token: Token
|
||||
type_: ^Type
|
||||
|
||||
using data: raw_union {
|
||||
Constant: struct {
|
||||
value: ExactValue
|
||||
}
|
||||
Variable: struct {
|
||||
visited: bool // Cycle detection
|
||||
used: bool // Variable is used
|
||||
is_field: bool // Is struct field
|
||||
anonymous: bool // Variable is an anonymous
|
||||
}
|
||||
UsingVariable: struct {
|
||||
}
|
||||
TypeName: struct {
|
||||
}
|
||||
Procedure: struct {
|
||||
used: bool
|
||||
}
|
||||
Builtin: struct {
|
||||
id: int
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Plus all the constructing procedures that go along with them!!!!
|
||||
// It's a nightmare
|
||||
}
|
||||
|
||||
{
|
||||
Type :: type struct {}
|
||||
Scope :: type struct {}
|
||||
Token :: type struct {}
|
||||
AstNode :: type struct {}
|
||||
ExactValue :: type struct {}
|
||||
|
||||
|
||||
Entity :: type union {
|
||||
Base :: type struct {
|
||||
Guid :: type i64
|
||||
guid: Guid
|
||||
|
||||
scope: ^Scope
|
||||
token: Token
|
||||
type_: ^Type
|
||||
}
|
||||
|
||||
|
||||
Constant: struct {
|
||||
using base: Base
|
||||
value: ExactValue
|
||||
}
|
||||
Variable: struct {
|
||||
using base: Base
|
||||
visited: bool // Cycle detection
|
||||
used: bool // Variable is used
|
||||
is_field: bool // Is struct field
|
||||
anonymous: bool // Variable is an anonymous
|
||||
}
|
||||
UsingVariable: struct {
|
||||
using base: Base
|
||||
}
|
||||
TypeName: struct {
|
||||
using base: Base
|
||||
}
|
||||
Procedure: struct {
|
||||
using base: Base
|
||||
used: bool
|
||||
}
|
||||
Builtin: struct {
|
||||
using base: Base
|
||||
id: int
|
||||
}
|
||||
}
|
||||
|
||||
using Entity
|
||||
|
||||
e: Entity
|
||||
|
||||
e = Variable{
|
||||
base = Base{},
|
||||
used = true,
|
||||
anonymous = false,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Q: Allow a "base" type to be added to a union?
|
||||
// Or even `using` on union to get the same properties?
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// `Raw` unions still have uses, especially for mathematic types
|
||||
|
||||
Vector2 :: type raw_union {
|
||||
using xy_: struct { x, y: f32 }
|
||||
e: [2]f32
|
||||
v: {2}f32
|
||||
}
|
||||
|
||||
Vector3 :: type raw_union {
|
||||
using xyz_: struct { x, y, z: f32 }
|
||||
xy: Vector2
|
||||
e: [3]f32
|
||||
v: {3}f32
|
||||
}
|
||||
|
||||
v2: Vector2
|
||||
v2.x = 1
|
||||
v2.e[0] = 1
|
||||
v2.v[0] = 1
|
||||
|
||||
v3: Vector3
|
||||
v3.x = 1
|
||||
v3.e[0] = 1
|
||||
v3.v[0] = 1
|
||||
v3.xy.x = 1
|
||||
}
|
||||
}
|
||||
|
||||
nl :: proc() { print_nl() }
|
||||
@@ -1,489 +0,0 @@
|
||||
#import win32 "sys/windows.odin";
|
||||
#import "fmt.odin";
|
||||
#import "os.odin";
|
||||
#import "mem.odin";
|
||||
|
||||
CANVAS_WIDTH :: 128;
|
||||
CANVAS_HEIGHT :: 128;
|
||||
CANVAS_SCALE :: 3;
|
||||
FRAME_TIME :: 1.0/30.0;
|
||||
WINDOW_TITLE :: "Punity\x00";
|
||||
|
||||
_ := compile_assert(CANVAS_WIDTH % 16 == 0);
|
||||
|
||||
WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE;
|
||||
WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE;
|
||||
|
||||
|
||||
STACK_CAPACITY :: 1<<20;
|
||||
STORAGE_CAPACITY :: 1<<20;
|
||||
|
||||
DRAW_LIST_RESERVE :: 128;
|
||||
|
||||
MAX_KEYS :: 256;
|
||||
|
||||
Core :: struct {
|
||||
stack: ^Bank,
|
||||
storage: ^Bank,
|
||||
|
||||
running: bool,
|
||||
key_modifiers: u32,
|
||||
key_states: [MAX_KEYS]byte,
|
||||
key_deltas: [MAX_KEYS]byte,
|
||||
|
||||
perf_frame,
|
||||
perf_frame_inner,
|
||||
perf_step,
|
||||
perf_audio,
|
||||
perf_blit,
|
||||
perf_blit_cvt,
|
||||
perf_blit_gdi: Perf_Span,
|
||||
|
||||
frame: i64,
|
||||
|
||||
canvas: Canvas,
|
||||
draw_list: ^Draw_List,
|
||||
}
|
||||
|
||||
Perf_Span :: struct {
|
||||
stamp: f64,
|
||||
delta: f32,
|
||||
}
|
||||
|
||||
Bank :: struct {
|
||||
memory: []byte,
|
||||
cursor: int,
|
||||
}
|
||||
|
||||
Bank_State :: struct {
|
||||
state: Bank,
|
||||
bank: ^Bank,
|
||||
}
|
||||
|
||||
|
||||
Color :: raw_union {
|
||||
using channels: struct{a, b, g, r: byte},
|
||||
rgba: u32,
|
||||
}
|
||||
|
||||
Palette :: struct {
|
||||
colors: [256]Color,
|
||||
colors_count: byte,
|
||||
}
|
||||
|
||||
|
||||
Rect :: raw_union {
|
||||
using minmax: struct {min_x, min_y, max_x, max_y: int},
|
||||
using pos: struct {left, top, right, bottom: int},
|
||||
e: [4]int,
|
||||
}
|
||||
|
||||
Bitmap :: struct {
|
||||
pixels: []byte,
|
||||
width: int,
|
||||
height: int,
|
||||
}
|
||||
|
||||
Font :: struct {
|
||||
using bitmap: Bitmap,
|
||||
char_width: int,
|
||||
char_height: int,
|
||||
}
|
||||
|
||||
Canvas :: struct {
|
||||
using bitmap: ^Bitmap,
|
||||
palette: Palette,
|
||||
translate_x: int,
|
||||
translate_y: int,
|
||||
clip: Rect,
|
||||
font: ^Font,
|
||||
}
|
||||
|
||||
DrawFlag :: enum {
|
||||
NONE = 0,
|
||||
FLIP_H = 1<<0,
|
||||
FLIP_V = 1<<1,
|
||||
MASK = 1<<2,
|
||||
}
|
||||
|
||||
Draw_Item :: struct {}
|
||||
Draw_List :: struct {
|
||||
items: []Draw_Item,
|
||||
}
|
||||
|
||||
Key :: enum {
|
||||
MOD_SHIFT = 0x0001,
|
||||
MOD_CONTROL = 0x0002,
|
||||
MOD_ALT = 0x0004,
|
||||
MOD_SUPER = 0x0008,
|
||||
|
||||
UNKNOWN =-1,
|
||||
INVALID =-2,
|
||||
|
||||
LBUTTON = 1,
|
||||
RBUTTON = 2,
|
||||
CANCEL = 3,
|
||||
MBUTTON = 4,
|
||||
|
||||
BACK = 8,
|
||||
TAB = 9,
|
||||
CLEAR = 12,
|
||||
RETURN = 13,
|
||||
SHIFT = 16,
|
||||
CONTROL = 17,
|
||||
MENU = 18,
|
||||
PAUSE = 19,
|
||||
CAPITAL = 20,
|
||||
KANA = 0x15,
|
||||
HANGEUL = 0x15,
|
||||
HANGUL = 0x15,
|
||||
JUNJA = 0x17,
|
||||
FINAL = 0x18,
|
||||
HANJA = 0x19,
|
||||
KANJI = 0x19,
|
||||
ESCAPE = 0x1B,
|
||||
CONVERT = 0x1C,
|
||||
NONCONVERT = 0x1D,
|
||||
ACCEPT = 0x1E,
|
||||
MODECHANGE = 0x1F,
|
||||
SPACE = 32,
|
||||
PRIOR = 33,
|
||||
NEXT = 34,
|
||||
END = 35,
|
||||
HOME = 36,
|
||||
LEFT = 37,
|
||||
UP = 38,
|
||||
RIGHT = 39,
|
||||
DOWN = 40,
|
||||
SELECT = 41,
|
||||
PRINT = 42,
|
||||
EXEC = 43,
|
||||
SNAPSHOT = 44,
|
||||
INSERT = 45,
|
||||
DELETE = 46,
|
||||
HELP = 47,
|
||||
LWIN = 0x5B,
|
||||
RWIN = 0x5C,
|
||||
APPS = 0x5D,
|
||||
SLEEP = 0x5F,
|
||||
NUMPAD0 = 0x60,
|
||||
NUMPAD1 = 0x61,
|
||||
NUMPAD2 = 0x62,
|
||||
NUMPAD3 = 0x63,
|
||||
NUMPAD4 = 0x64,
|
||||
NUMPAD5 = 0x65,
|
||||
NUMPAD6 = 0x66,
|
||||
NUMPAD7 = 0x67,
|
||||
NUMPAD8 = 0x68,
|
||||
NUMPAD9 = 0x69,
|
||||
MULTIPLY = 0x6A,
|
||||
ADD = 0x6B,
|
||||
SEPARATOR = 0x6C,
|
||||
SUBTRACT = 0x6D,
|
||||
DECIMAL = 0x6E,
|
||||
DIVIDE = 0x6F,
|
||||
F1 = 0x70,
|
||||
F2 = 0x71,
|
||||
F3 = 0x72,
|
||||
F4 = 0x73,
|
||||
F5 = 0x74,
|
||||
F6 = 0x75,
|
||||
F7 = 0x76,
|
||||
F8 = 0x77,
|
||||
F9 = 0x78,
|
||||
F10 = 0x79,
|
||||
F11 = 0x7A,
|
||||
F12 = 0x7B,
|
||||
F13 = 0x7C,
|
||||
F14 = 0x7D,
|
||||
F15 = 0x7E,
|
||||
F16 = 0x7F,
|
||||
F17 = 0x80,
|
||||
F18 = 0x81,
|
||||
F19 = 0x82,
|
||||
F20 = 0x83,
|
||||
F21 = 0x84,
|
||||
F22 = 0x85,
|
||||
F23 = 0x86,
|
||||
F24 = 0x87,
|
||||
NUMLOCK = 0x90,
|
||||
SCROLL = 0x91,
|
||||
LSHIFT = 0xA0,
|
||||
RSHIFT = 0xA1,
|
||||
LCONTROL = 0xA2,
|
||||
RCONTROL = 0xA3,
|
||||
LMENU = 0xA4,
|
||||
RMENU = 0xA5,
|
||||
|
||||
APOSTROPHE = 39, /* ' */
|
||||
COMMA = 44, /* , */
|
||||
MINUS = 45, /* - */
|
||||
PERIOD = 46, /* . */
|
||||
SLASH = 47, /* / */
|
||||
NUM0 = 48,
|
||||
NUM1 = 49,
|
||||
NUM2 = 50,
|
||||
NUM3 = 51,
|
||||
NUM4 = 52,
|
||||
NUM5 = 53,
|
||||
NUM6 = 54,
|
||||
NUM7 = 55,
|
||||
NUM8 = 56,
|
||||
NUM9 = 57,
|
||||
SEMICOLON = 59, /* ; */
|
||||
EQUAL = 61, /* = */
|
||||
A = 65,
|
||||
B = 66,
|
||||
C = 67,
|
||||
D = 68,
|
||||
E = 69,
|
||||
F = 70,
|
||||
G = 71,
|
||||
H = 72,
|
||||
I = 73,
|
||||
J = 74,
|
||||
K = 75,
|
||||
L = 76,
|
||||
M = 77,
|
||||
N = 78,
|
||||
O = 79,
|
||||
P = 80,
|
||||
Q = 81,
|
||||
R = 82,
|
||||
S = 83,
|
||||
T = 84,
|
||||
U = 85,
|
||||
V = 86,
|
||||
W = 87,
|
||||
X = 88,
|
||||
Y = 89,
|
||||
Z = 90,
|
||||
LEFT_BRACKET = 91, /* [ */
|
||||
BACKSLASH = 92, /* \ */
|
||||
RIGHT_BRACKET = 93, /* ] */
|
||||
GRAVE_ACCENT = 96, /* ` */
|
||||
};
|
||||
|
||||
|
||||
key_down :: proc(k: Key) -> bool {
|
||||
return _core.key_states[k] != 0;
|
||||
}
|
||||
|
||||
key_pressed :: proc(k: Key) -> bool {
|
||||
return (_core.key_deltas[k] != 0) && key_down(k);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
|
||||
time_now :: proc() -> f64 {
|
||||
assert(win32_perf_count_freq != 0);
|
||||
|
||||
counter: i64;
|
||||
win32.QueryPerformanceCounter(^counter);
|
||||
result := cast(f64)counter / cast(f64)win32_perf_count_freq;
|
||||
return result;
|
||||
}
|
||||
|
||||
_core: Core;
|
||||
|
||||
run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
using win32;
|
||||
|
||||
_core.running = true;
|
||||
|
||||
win32_proc :: proc(hwnd: win32.HWND, msg: u32, wparam: win32.WPARAM, lparam: win32.LPARAM) -> win32.LRESULT #no_inline #cc_c {
|
||||
win32_app_key_mods :: proc() -> u32 {
|
||||
mods: u32 = 0;
|
||||
|
||||
if is_key_down(Key_Code.SHIFT) {
|
||||
mods |= cast(u32)Key.MOD_SHIFT;
|
||||
}
|
||||
if is_key_down(Key_Code.CONTROL) {
|
||||
mods |= cast(u32)Key.MOD_CONTROL;
|
||||
}
|
||||
if is_key_down(Key_Code.MENU) {
|
||||
mods |= cast(u32)Key.MOD_ALT;
|
||||
}
|
||||
if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) {
|
||||
mods |= cast(u32)Key.MOD_SUPER;
|
||||
}
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
match msg {
|
||||
case WM_KEYDOWN:
|
||||
_core.key_modifiers = win32_app_key_mods();
|
||||
if wparam < MAX_KEYS {
|
||||
_core.key_states[wparam] = 1;
|
||||
_core.key_deltas[wparam] = 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_KEYUP:
|
||||
_core.key_modifiers = win32_app_key_mods();
|
||||
if wparam < MAX_KEYS {
|
||||
_core.key_states[wparam] = 0;
|
||||
_core.key_deltas[wparam] = 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case WM_CLOSE:
|
||||
PostQuitMessage(0);
|
||||
_core.running = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProcA(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
|
||||
window_class := WNDCLASSEXA{
|
||||
class_name = (cast(string)"Punity\x00").data, // C-style string
|
||||
size = size_of(WNDCLASSEXA),
|
||||
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
|
||||
instance = cast(HINSTANCE)GetModuleHandleA(nil),
|
||||
wnd_proc = win32_proc,
|
||||
// wnd_proc = DefWindowProcA,
|
||||
background = cast(HBRUSH)GetStockObject(BLACK_BRUSH),
|
||||
};
|
||||
|
||||
if RegisterClassExA(^window_class) == 0 {
|
||||
fmt.fprintln(os.stderr, "RegisterClassExA failed");
|
||||
return;
|
||||
}
|
||||
|
||||
screen_width := GetSystemMetrics(SM_CXSCREEN);
|
||||
screen_height := GetSystemMetrics(SM_CYSCREEN);
|
||||
|
||||
rc: RECT;
|
||||
rc.left = (screen_width - WINDOW_WIDTH) / 2;
|
||||
rc.top = (screen_height - WINDOW_HEIGHT) / 2;
|
||||
rc.right = rc.left + WINDOW_WIDTH;
|
||||
rc.bottom = rc.top + WINDOW_HEIGHT;
|
||||
|
||||
style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
||||
assert(AdjustWindowRect(^rc, style, 0) != 0);
|
||||
|
||||
wt := WINDOW_TITLE;
|
||||
|
||||
win32_window := CreateWindowExA(0,
|
||||
window_class.class_name,
|
||||
wt.data,
|
||||
style,
|
||||
rc.left, rc.top,
|
||||
rc.right-rc.left, rc.bottom-rc.top,
|
||||
nil, nil, window_class.instance,
|
||||
nil);
|
||||
|
||||
if win32_window == nil {
|
||||
fmt.fprintln(os.stderr, "CreateWindowExA failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
window_bmi: BITMAPINFO;
|
||||
window_bmi.size = size_of(BITMAPINFOHEADER);
|
||||
window_bmi.width = CANVAS_WIDTH;
|
||||
window_bmi.height = CANVAS_HEIGHT;
|
||||
window_bmi.planes = 1;
|
||||
window_bmi.bit_count = 32;
|
||||
window_bmi.compression = BI_RGB;
|
||||
|
||||
|
||||
user_init(^_core);
|
||||
|
||||
ShowWindow(win32_window, SW_SHOW);
|
||||
|
||||
window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT);
|
||||
defer free(window_buffer);
|
||||
|
||||
|
||||
for i := 0; i < window_buffer.count; i += 1 {
|
||||
window_buffer[i] = 0xff00ff;
|
||||
}
|
||||
|
||||
|
||||
dt: f64;
|
||||
prev_time := time_now();
|
||||
curr_time := time_now();
|
||||
total_time : f64 = 0;
|
||||
offset_x := 0;
|
||||
offset_y := 0;
|
||||
|
||||
message: MSG;
|
||||
for _core.running {
|
||||
curr_time = time_now();
|
||||
dt = curr_time - prev_time;
|
||||
prev_time = curr_time;
|
||||
total_time += dt;
|
||||
|
||||
offset_x += 1;
|
||||
offset_y += 2;
|
||||
|
||||
{
|
||||
data: [128]byte;
|
||||
buf: fmt.Buffer;
|
||||
buf.data = data[:];
|
||||
fmt.bprintf(^buf, "Punity: %.4f ms\x00", dt*1000);
|
||||
win32.SetWindowTextA(win32_window, ^buf[0]);
|
||||
}
|
||||
|
||||
|
||||
for y := 0; y < CANVAS_HEIGHT; y += 1 {
|
||||
for x := 0; x < CANVAS_WIDTH; x += 1 {
|
||||
g := (x % 32) * 8;
|
||||
b := (y % 32) * 8;
|
||||
window_buffer[x + y*CANVAS_WIDTH] = cast(u32)(g << 8 | b);
|
||||
}
|
||||
}
|
||||
|
||||
mem.zero(^_core.key_deltas[0], size_of_val(_core.key_deltas));
|
||||
|
||||
for PeekMessageA(^message, nil, 0, 0, PM_REMOVE) != 0 {
|
||||
if message.message == WM_QUIT {
|
||||
_core.running = false;
|
||||
}
|
||||
TranslateMessage(^message);
|
||||
DispatchMessageA(^message);
|
||||
}
|
||||
|
||||
user_step(^_core);
|
||||
|
||||
dc := GetDC(win32_window);
|
||||
StretchDIBits(dc,
|
||||
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
|
||||
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
||||
window_buffer.data,
|
||||
^window_bmi,
|
||||
DIB_RGB_COLORS,
|
||||
SRCCOPY);
|
||||
ReleaseDC(win32_window, dc);
|
||||
|
||||
|
||||
{
|
||||
delta := time_now() - prev_time;
|
||||
ms := cast(i32)((FRAME_TIME - delta) * 1000);
|
||||
if ms > 0 {
|
||||
win32.Sleep(ms);
|
||||
}
|
||||
}
|
||||
|
||||
_core.frame += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
main :: proc() {
|
||||
user_init :: proc(c: ^Core) {
|
||||
|
||||
}
|
||||
|
||||
user_step :: proc(c: ^Core) {
|
||||
|
||||
}
|
||||
|
||||
run(user_init, user_step);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#import "fmt.odin" as fmt
|
||||
|
||||
thing :: proc() {
|
||||
fmt.println("Sub Hello!")
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*#import "fmt.odin"
|
||||
|
||||
thing :: proc() {
|
||||
fmt.println("Hello1!")
|
||||
}*/
|
||||
|
||||
|
||||
#import "fmt.odin";
|
||||
|
||||
main :: proc() {
|
||||
fmt.println("hello, world!");
|
||||
}
|
||||
|
||||
|
||||
/*#import "fmt.odin" as .
|
||||
|
||||
thing :: proc() {
|
||||
println("Hello3!")
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
/*#import "fmt.odin" as _
|
||||
|
||||
thing :: proc() {
|
||||
// println("Hello4!")
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
#include "fmt.odin"
|
||||
|
||||
thing :: proc() {
|
||||
println("Hello5!")
|
||||
}*/
|
||||
@@ -1,657 +0,0 @@
|
||||
#shared_global_scope;
|
||||
|
||||
#import "os.odin";
|
||||
#import "fmt.odin";
|
||||
#import "mem.odin";
|
||||
#import "utf8.odin";
|
||||
#import "hash.odin";
|
||||
|
||||
// IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
|
||||
// #shared_global_scope due to the internals of the compiler.
|
||||
// This could change at a later date if the all these data structures are
|
||||
// implemented within the compiler rather than in this "preload" file
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill): Do not change the order of any of this data
|
||||
// The compiler relies upon this _exact_ order
|
||||
Type_Info_Enum_Value :: raw_union {
|
||||
f: f64,
|
||||
i: i64,
|
||||
}
|
||||
// NOTE(bill): This must match the compiler's
|
||||
Calling_Convention :: enum {
|
||||
ODIN = 0,
|
||||
C = 1,
|
||||
STD = 2,
|
||||
FAST = 3,
|
||||
}
|
||||
|
||||
Type_Info_Record :: struct #ordered {
|
||||
types: []^Type_Info,
|
||||
names: []string,
|
||||
offsets: []int, // offsets may not be used in tuples
|
||||
size: int, // in bytes
|
||||
align: int, // in bytes
|
||||
packed: bool,
|
||||
ordered: bool,
|
||||
custom_align: bool,
|
||||
}
|
||||
|
||||
Type_Info :: union {
|
||||
Named{name: string, base: ^Type_Info},
|
||||
Integer{size: int, signed: bool},
|
||||
Float{size: int},
|
||||
String{},
|
||||
Boolean{},
|
||||
Any{},
|
||||
Pointer{
|
||||
elem: ^Type_Info, // nil -> rawptr
|
||||
},
|
||||
Procedure{
|
||||
params: ^Type_Info, // Type_Info.Tuple
|
||||
results: ^Type_Info, // Type_Info.Tuple
|
||||
variadic: bool,
|
||||
convention: Calling_Convention,
|
||||
},
|
||||
Array{
|
||||
elem: ^Type_Info,
|
||||
elem_size: int,
|
||||
count: int,
|
||||
},
|
||||
Dynamic_Array{elem: ^Type_Info, elem_size: int},
|
||||
Slice {elem: ^Type_Info, elem_size: int},
|
||||
Vector {elem: ^Type_Info, elem_size, count, align: int},
|
||||
Tuple {using record: Type_Info_Record}, // Only really used for procedures
|
||||
Struct {using record: Type_Info_Record},
|
||||
Raw_Union {using record: Type_Info_Record},
|
||||
Union{
|
||||
common_fields: struct {
|
||||
types: []^Type_Info,
|
||||
names: []string,
|
||||
offsets: []int, // offsets may not be used in tuples
|
||||
},
|
||||
variant_names: []string,
|
||||
variant_types: []^Type_Info,
|
||||
size: int,
|
||||
align: int,
|
||||
},
|
||||
Enum{
|
||||
base: ^Type_Info,
|
||||
names: []string,
|
||||
values: []Type_Info_Enum_Value,
|
||||
},
|
||||
Map{
|
||||
key: ^Type_Info,
|
||||
value: ^Type_Info,
|
||||
generated_struct: ^Type_Info,
|
||||
count: int, // == 0 if dynamic
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): only the ones that are needed (not all types)
|
||||
// This will be set by the compiler
|
||||
__type_table: []Type_Info;
|
||||
|
||||
type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil;
|
||||
}
|
||||
base := info;
|
||||
match i in base {
|
||||
case Type_Info.Named:
|
||||
base = i.base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
type_info_base_without_enum :: proc(info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil;
|
||||
}
|
||||
base := info;
|
||||
match i in base {
|
||||
case Type_Info.Named:
|
||||
base = i.base;
|
||||
case Type_Info.Enum:
|
||||
base = i.base;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
|
||||
assume :: proc(cond: bool) #foreign __llvm_core "llvm.assume";
|
||||
|
||||
__debug_trap :: proc() #foreign __llvm_core "llvm.debugtrap";
|
||||
__trap :: proc() #foreign __llvm_core "llvm.trap";
|
||||
read_cycle_counter :: proc() -> u64 #foreign __llvm_core "llvm.readcyclecounter";
|
||||
|
||||
__cpuid :: proc(level: u32, sig: ^u32) -> i32 #foreign __llvm_core "__get_cpuid";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
||||
Allocator_Mode :: enum u8 {
|
||||
ALLOC,
|
||||
FREE,
|
||||
FREE_ALL,
|
||||
RESIZE,
|
||||
}
|
||||
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr;
|
||||
Allocator :: struct #ordered {
|
||||
procedure: Allocator_Proc,
|
||||
data: rawptr,
|
||||
}
|
||||
|
||||
Context :: struct #ordered {
|
||||
thread_id: int,
|
||||
|
||||
allocator: Allocator,
|
||||
|
||||
user_data: rawptr,
|
||||
user_index: int,
|
||||
}
|
||||
|
||||
#thread_local __context: Context;
|
||||
|
||||
|
||||
DEFAULT_ALIGNMENT :: align_of([vector 4]f32);
|
||||
|
||||
|
||||
__check_context :: proc() {
|
||||
c := ^__context;
|
||||
|
||||
if c.allocator.procedure == nil {
|
||||
c.allocator = default_allocator();
|
||||
}
|
||||
if c.thread_id == 0 {
|
||||
c.thread_id = os.current_thread_id();
|
||||
}
|
||||
}
|
||||
|
||||
alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT); }
|
||||
|
||||
alloc_align :: proc(size, alignment: int) -> rawptr #inline {
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
return a.procedure(a.data, Allocator_Mode.ALLOC, size, alignment, nil, 0, 0);
|
||||
}
|
||||
|
||||
free_ptr_with_allocator :: proc(a: Allocator, ptr: rawptr) #inline {
|
||||
if ptr == nil {
|
||||
return;
|
||||
}
|
||||
if a.procedure == nil {
|
||||
return;
|
||||
}
|
||||
a.procedure(a.data, Allocator_Mode.FREE, 0, 0, ptr, 0, 0);
|
||||
}
|
||||
|
||||
free_ptr :: proc(ptr: rawptr) #inline {
|
||||
__check_context();
|
||||
free_ptr_with_allocator(context.allocator, ptr);
|
||||
}
|
||||
|
||||
free_all :: proc() #inline {
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
a.procedure(a.data, Allocator_Mode.FREE_ALL, 0, 0, nil, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT); }
|
||||
resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
return a.procedure(a.data, Allocator_Mode.RESIZE, new_size, alignment, ptr, old_size, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr {
|
||||
if old_memory == nil {
|
||||
return alloc_align(new_size, alignment);
|
||||
}
|
||||
|
||||
if new_size == 0 {
|
||||
free(old_memory);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if new_size == old_size {
|
||||
return old_memory;
|
||||
}
|
||||
|
||||
new_memory := alloc_align(new_size, alignment);
|
||||
if new_memory == nil {
|
||||
return nil;
|
||||
}
|
||||
|
||||
mem.copy(new_memory, old_memory, min(old_size, new_size));;
|
||||
free(old_memory);
|
||||
return new_memory;
|
||||
}
|
||||
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
||||
using Allocator_Mode;
|
||||
|
||||
match mode {
|
||||
case ALLOC:
|
||||
return os.heap_alloc(size);
|
||||
|
||||
case FREE:
|
||||
os.heap_free(old_memory);
|
||||
return nil;
|
||||
|
||||
case FREE_ALL:
|
||||
// NOTE(bill): Does nothing
|
||||
|
||||
case RESIZE:
|
||||
ptr := os.heap_resize(old_memory, size);
|
||||
assert(ptr != nil);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
__string_eq :: proc(a, b: string) -> bool {
|
||||
if a.count != b.count {
|
||||
return false;
|
||||
}
|
||||
if a.data == b.data {
|
||||
return true;
|
||||
}
|
||||
return __string_cmp(a, b) == 0;
|
||||
}
|
||||
|
||||
__string_cmp :: proc(a, b: string) -> int {
|
||||
return mem.compare(cast([]byte)a, cast([]byte)b);
|
||||
}
|
||||
|
||||
__string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b); }
|
||||
__string_lt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) < 0; }
|
||||
__string_gt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) > 0; }
|
||||
__string_le :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) <= 0; }
|
||||
__string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0; }
|
||||
|
||||
|
||||
__assert :: proc(file: string, line, column: int, msg: string) #inline {
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n",
|
||||
file, line, column, msg);
|
||||
__debug_trap();
|
||||
}
|
||||
__panic :: proc(file: string, line, column: int, msg: string) #inline {
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Panic: %s\n",
|
||||
file, line, column, msg);
|
||||
__debug_trap();
|
||||
}
|
||||
__bounds_check_error :: proc(file: string, line, column: int, index, count: int) {
|
||||
if 0 <= index && index < count {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Index %d is out of bounds range 0..<%d\n",
|
||||
file, line, column, index, count);
|
||||
__debug_trap();
|
||||
}
|
||||
|
||||
__slice_expr_error :: proc(file: string, line, column: int, low, high: int) {
|
||||
if 0 <= low && low <= high {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d:%d]\n",
|
||||
file, line, column, low, high);
|
||||
__debug_trap();
|
||||
}
|
||||
__substring_expr_error :: proc(file: string, line, column: int, low, high: int) {
|
||||
if 0 <= low && low <= high {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid substring indices: [%d:%d]\n",
|
||||
file, line, column, low, high);
|
||||
__debug_trap();
|
||||
}
|
||||
__union_cast_check :: proc(ok: bool, file: string, line, column: int, from, to: ^Type_Info) {
|
||||
if !ok {
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid `union_cast` from %T to %T\n",
|
||||
file, line, column, from, to);
|
||||
__debug_trap();
|
||||
}
|
||||
}
|
||||
|
||||
__string_decode_rune :: proc(s: string) -> (rune, int) #inline {
|
||||
return utf8.decode_rune(s);
|
||||
}
|
||||
|
||||
|
||||
Raw_Any :: struct #ordered {
|
||||
type_info: ^Type_Info,
|
||||
data: rawptr,
|
||||
}
|
||||
|
||||
Raw_String :: struct #ordered {
|
||||
data: ^byte,
|
||||
count: int,
|
||||
};
|
||||
|
||||
Raw_Slice :: struct #ordered {
|
||||
data: rawptr,
|
||||
count: int,
|
||||
};
|
||||
|
||||
Raw_Dynamic_Array :: struct #ordered {
|
||||
data: rawptr,
|
||||
count: int,
|
||||
capacity: int,
|
||||
allocator: Allocator,
|
||||
};
|
||||
|
||||
Raw_Dynamic_Map :: struct #ordered {
|
||||
hashes: [dynamic]int,
|
||||
entries: Raw_Dynamic_Array,
|
||||
};
|
||||
|
||||
|
||||
|
||||
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capacity: int) -> bool {
|
||||
array := cast(^Raw_Dynamic_Array)array_;
|
||||
|
||||
if capacity <= array.capacity {
|
||||
return true;
|
||||
}
|
||||
|
||||
__check_context();
|
||||
if array.allocator.procedure == nil {
|
||||
array.allocator = context.allocator;
|
||||
}
|
||||
assert(array.allocator.procedure != nil);
|
||||
|
||||
old_size := array.capacity * elem_size;
|
||||
new_size := capacity * elem_size;
|
||||
allocator := array.allocator;
|
||||
|
||||
new_data := allocator.procedure(allocator.data, Allocator_Mode.RESIZE, new_size, elem_align, array.data, old_size, 0);
|
||||
if new_data == nil {
|
||||
return false;
|
||||
}
|
||||
|
||||
array.data = new_data;
|
||||
array.capacity = capacity;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
|
||||
items: rawptr, item_count: int) -> int {
|
||||
array := cast(^Raw_Dynamic_Array)array_;
|
||||
|
||||
if item_count <= 0 || items == nil {
|
||||
return array.count;
|
||||
}
|
||||
|
||||
|
||||
ok := true;
|
||||
if array.capacity <= array.count+item_count {
|
||||
capacity := 2 * array.capacity + max(8, item_count);
|
||||
ok = __dynamic_array_reserve(array, elem_size, elem_align, capacity);
|
||||
}
|
||||
if !ok {
|
||||
// TODO(bill): Better error handling for failed reservation
|
||||
return array.count;
|
||||
}
|
||||
data := cast(^byte)array.data;
|
||||
assert(data != nil);
|
||||
mem.copy(data + (elem_size*array.count), items, elem_size * item_count);
|
||||
array.count += item_count;
|
||||
return array.count;
|
||||
}
|
||||
|
||||
__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int) -> int {
|
||||
array := cast(^Raw_Dynamic_Array)array_;
|
||||
|
||||
ok := true;
|
||||
if array.capacity <= array.count+1 {
|
||||
capacity := 2 * array.capacity + max(8, 1);
|
||||
ok = __dynamic_array_reserve(array, elem_size, elem_align, capacity);
|
||||
}
|
||||
if !ok {
|
||||
// TODO(bill): Better error handling for failed reservation
|
||||
return array.count;
|
||||
}
|
||||
data := cast(^byte)array.data;
|
||||
assert(data != nil);
|
||||
mem.zero(data + (elem_size*array.count), elem_size);
|
||||
array.count += 1;
|
||||
return array.count;
|
||||
}
|
||||
|
||||
|
||||
// Map stuff
|
||||
|
||||
__default_hash :: proc(data: []byte) -> u64 {
|
||||
return hash.fnv64a(data);
|
||||
}
|
||||
__default_hash_string :: proc(s: string) -> u64 {
|
||||
return __default_hash(cast([]byte)s);
|
||||
}
|
||||
|
||||
__Map_Key :: struct #ordered {
|
||||
hash: u64,
|
||||
str: string,
|
||||
}
|
||||
|
||||
__Map_Find_Result :: struct #ordered {
|
||||
hash_index: int,
|
||||
entry_prev: int,
|
||||
entry_index: int,
|
||||
}
|
||||
|
||||
__Map_Entry_Header :: struct #ordered {
|
||||
key: __Map_Key,
|
||||
next: int,
|
||||
/*
|
||||
value: Value_Type,
|
||||
*/
|
||||
}
|
||||
|
||||
__Map_Header :: struct #ordered {
|
||||
m: ^Raw_Dynamic_Map,
|
||||
is_key_string: bool,
|
||||
entry_size: int,
|
||||
entry_align: int,
|
||||
value_offset: int,
|
||||
}
|
||||
|
||||
__dynamic_map_reserve :: proc(using header: __Map_Header, capacity: int) -> bool {
|
||||
h := __dynamic_array_reserve(^m.hashes, size_of(int), align_of(int), capacity);
|
||||
e := __dynamic_array_reserve(^m.entries, entry_size, entry_align, capacity);
|
||||
return h && e;
|
||||
}
|
||||
|
||||
__dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
|
||||
new_header := header;
|
||||
nm: Raw_Dynamic_Map;
|
||||
new_header.m = ^nm;
|
||||
|
||||
reserve(nm.hashes, new_count);
|
||||
nm.hashes.count = nm.hashes.capacity;
|
||||
__dynamic_array_reserve(^nm.entries, entry_size, entry_align, m.entries.count);
|
||||
for _, i in nm.hashes {
|
||||
nm.hashes[i] = -1;
|
||||
}
|
||||
|
||||
for i := 0; i < nm.entries.count; i += 1 {
|
||||
entry_header := __dynamic_map_get_entry(new_header, i);
|
||||
data := cast(^byte)entry_header;
|
||||
|
||||
if nm.hashes.count == 0 {
|
||||
__dynamic_map_grow(new_header);
|
||||
}
|
||||
|
||||
fr := __dynamic_map_find(new_header, entry_header.key);
|
||||
j := __dynamic_map_add_entry(new_header, entry_header.key);
|
||||
if fr.entry_prev < 0 {
|
||||
nm.hashes[fr.hash_index] = j;
|
||||
} else {
|
||||
e := __dynamic_map_get_entry(new_header, fr.entry_prev);
|
||||
e.next = j;
|
||||
}
|
||||
|
||||
e := __dynamic_map_get_entry(new_header, j);
|
||||
e.next = fr.entry_index;
|
||||
ndata := cast(^byte)e;
|
||||
mem.copy(ndata+value_offset, data+value_offset, entry_size-value_offset);
|
||||
if __dynamic_map_full(new_header) {
|
||||
__dynamic_map_grow(new_header);
|
||||
}
|
||||
}
|
||||
free_ptr_with_allocator(header.m.hashes.allocator, header.m.hashes.data);
|
||||
free_ptr_with_allocator(header.m.entries.allocator, header.m.entries.data);
|
||||
header.m^ = nm;
|
||||
}
|
||||
|
||||
__dynamic_map_get :: proc(h: __Map_Header, key: __Map_Key) -> rawptr {
|
||||
index := __dynamic_map_find(h, key).entry_index;
|
||||
if index >= 0 {
|
||||
data := cast(^byte)__dynamic_map_get_entry(h, index);
|
||||
val := data + h.value_offset;
|
||||
return val;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
__dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr) {
|
||||
index: int;
|
||||
|
||||
if m.hashes.count == 0 {
|
||||
__dynamic_map_grow(h);
|
||||
}
|
||||
fr := __dynamic_map_find(h, key);
|
||||
if fr.entry_index >= 0 {
|
||||
index = fr.entry_index;
|
||||
} else {
|
||||
index = __dynamic_map_add_entry(h, key);
|
||||
if fr.entry_prev >= 0 {
|
||||
entry := __dynamic_map_get_entry(h, fr.entry_prev);
|
||||
entry.next = index;
|
||||
} else {
|
||||
m.hashes[fr.hash_index] = index;
|
||||
}
|
||||
}
|
||||
{
|
||||
data := cast(^byte)__dynamic_map_get_entry(h, index);
|
||||
val := data+value_offset;
|
||||
mem.copy(val, value, entry_size-value_offset);
|
||||
}
|
||||
|
||||
if __dynamic_map_full(h) {
|
||||
__dynamic_map_grow(h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
__dynamic_map_grow :: proc(using h: __Map_Header) {
|
||||
new_count := 2*m.entries.count + 8;
|
||||
__dynamic_map_rehash(h, new_count);
|
||||
}
|
||||
|
||||
__dynamic_map_full :: proc(using h: __Map_Header) -> bool {
|
||||
return cast(int)(0.75 * cast(f64)m.hashes.count) <= m.entries.count;
|
||||
}
|
||||
|
||||
|
||||
__dynamic_map_hash_equal :: proc(h: __Map_Header, a, b: __Map_Key) -> bool {
|
||||
if a.hash == b.hash {
|
||||
if h.is_key_string {
|
||||
return a.str == b.str;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
__dynamic_map_find :: proc(using h: __Map_Header, key: __Map_Key) -> __Map_Find_Result {
|
||||
fr := __Map_Find_Result{-1, -1, -1};
|
||||
if m.hashes.count > 0 {
|
||||
fr.hash_index = cast(int)(key.hash % cast(u64)m.hashes.count);
|
||||
fr.entry_index = m.hashes[fr.hash_index];
|
||||
for fr.entry_index >= 0 {
|
||||
entry := __dynamic_map_get_entry(h, fr.entry_index);
|
||||
if __dynamic_map_hash_equal(h, entry.key, key) {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = entry.next;
|
||||
}
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
__dynamic_map_add_entry :: proc(using h: __Map_Header, key: __Map_Key) -> int {
|
||||
prev := m.entries.count;
|
||||
c := __dynamic_array_append_nothing(^m.entries, entry_size, entry_align);
|
||||
if c != prev {
|
||||
end := __dynamic_map_get_entry(h, c-1);
|
||||
end.key = key;
|
||||
end.next = -1;
|
||||
}
|
||||
return prev;
|
||||
}
|
||||
|
||||
|
||||
__dynamic_map_delete :: proc(using h: __Map_Header, key: __Map_Key) {
|
||||
fr := __dynamic_map_find(h, key);
|
||||
if fr.entry_index >= 0 {
|
||||
__dynamic_map_erase(h, fr);
|
||||
}
|
||||
}
|
||||
|
||||
__dynamic_map_get_entry :: proc(using h: __Map_Header, index: int) -> ^__Map_Entry_Header {
|
||||
data := cast(^byte)m.entries.data + index*entry_size;
|
||||
return cast(^__Map_Entry_Header)data;
|
||||
}
|
||||
|
||||
__dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) {
|
||||
if fr.entry_prev < 0 {
|
||||
m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next;
|
||||
} else {
|
||||
__dynamic_map_get_entry(h, fr.entry_prev).next = __dynamic_map_get_entry(h, fr.entry_index).next;
|
||||
}
|
||||
|
||||
if fr.entry_index == m.entries.count-1 {
|
||||
m.entries.count -= 1;
|
||||
}
|
||||
mem.copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.count-1), entry_size);
|
||||
last := __dynamic_map_find(h, __dynamic_map_get_entry(h, fr.entry_index).key);
|
||||
if last.entry_prev >= 0 {
|
||||
__dynamic_map_get_entry(h, last.entry_prev).next = fr.entry_index;
|
||||
} else {
|
||||
m.hashes[last.hash_index] = fr.entry_index;
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
#shared_global_scope;
|
||||
|
||||
|
||||
// import "fmt.odin";
|
||||
|
||||
// proc __u128_mod(a, b: u128) -> u128 #link_name "__umodti3" {
|
||||
// var _, r := __u128_quo_mod(a, b)
|
||||
// return r
|
||||
// }
|
||||
|
||||
// proc __u128_quo(a, b: u128) -> u128 #link_name "__udivti3" {
|
||||
// var n, _ := __u128_quo_mod(a, b)
|
||||
// return n
|
||||
// }
|
||||
|
||||
// proc __i128_mod(a, b: i128) -> i128 #link_name "__modti3" {
|
||||
// var _, r := __i128_quo_mod(a, b)
|
||||
// return r
|
||||
// }
|
||||
|
||||
// proc __i128_quo(a, b: i128) -> i128 #link_name "__divti3" {
|
||||
// var n, _ := __i128_quo_mod(a, b)
|
||||
// return n
|
||||
// }
|
||||
|
||||
// proc __i128_quo_mod(a, b: i128) -> (i128, i128) #link_name "__divmodti4" {
|
||||
// var s := b >> 127
|
||||
// b = (b ~ s) - s
|
||||
// s = a >> 127
|
||||
// a = (a ~ s) - s
|
||||
|
||||
// var n, r := __u128_quo_mod(a as u128, b as u128)
|
||||
// return (n as i128 ~ s) - s, (r as i128 ~ s) - s
|
||||
// }
|
||||
|
||||
|
||||
// proc __u128_quo_mod(a, b: u128) -> (u128, u128) #link_name "__udivmodti4" {
|
||||
// proc clz(x: u64) -> u64 {
|
||||
// proc clz_u64(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.ctlz.i64"
|
||||
// return clz_u64(x, false)
|
||||
// }
|
||||
// proc ctz(x: u64) -> u64 {
|
||||
// proc ctz_u64(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.cttz.i64"
|
||||
// return ctz_u64(x, false)
|
||||
// }
|
||||
|
||||
|
||||
// u128_lo_hi :: raw_union {
|
||||
// all: u128
|
||||
// using _lohi: struct {lo, hi: u64;}
|
||||
// }
|
||||
|
||||
// n, d, q, r: u128_lo_hi
|
||||
// sr: u64
|
||||
|
||||
// n.all = a
|
||||
// d.all = b
|
||||
|
||||
// if n.hi == 0 {
|
||||
// if d.hi == 0 {
|
||||
// return (n.lo / d.lo) as u128, (n.lo % d.lo) as u128
|
||||
// }
|
||||
// return 0, n.lo as u128
|
||||
// }
|
||||
// if d.lo == 0 {
|
||||
// if d.hi == 0 {
|
||||
// return (n.hi / d.lo) as u128, (n.hi % d.lo) as u128
|
||||
// }
|
||||
// if n.lo == 0 {
|
||||
// r.hi = n.hi % d.hi
|
||||
// r.lo = 0
|
||||
// return (n.hi / d.hi) as u128, r.all
|
||||
// }
|
||||
// if (d.hi & (d.hi-1)) == 0 {
|
||||
// r.lo = n.lo
|
||||
// r.hi = n.hi & (d.hi-1)
|
||||
// return (n.hi >> ctz(d.hi)) as u128, r.all
|
||||
// }
|
||||
|
||||
// sr = clz(d.hi) - clz(n.hi)
|
||||
// if sr > 64 - 2 {
|
||||
// return 0, n.all
|
||||
// }
|
||||
// sr++
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo << (64-sr)
|
||||
// r.hi = n.hi >> sr
|
||||
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// } else {
|
||||
// if d.hi == 0 {
|
||||
// if (d.lo & (d.lo - 1)) == 0 {
|
||||
// var rem := (n.lo % (d.lo - 1)) as u128
|
||||
// if d.lo == 1 {
|
||||
// return n.all, rem
|
||||
// }
|
||||
// sr = ctz(d.lo)
|
||||
// q.hi = n.hi >> sr
|
||||
// q.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// return q.all, rem
|
||||
// }
|
||||
|
||||
// sr = 1 + 64 + clz(d.lo) - clz(n.hi)
|
||||
|
||||
// q.all = n.all << (128-sr)
|
||||
// r.all = n.all >> sr
|
||||
// if sr == 64 {
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo
|
||||
// r.hi = 0
|
||||
// r.lo = n.hi
|
||||
// } else if sr < 64 {
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo << (64-sr)
|
||||
// r.hi = n.hi >> sr
|
||||
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// } else {
|
||||
// q.lo = n.lo << (128-sr)
|
||||
// q.hi = (n.hi << (128-sr)) | (n.lo >> (sr-64))
|
||||
// r.hi = 0
|
||||
// r.lo = n.hi >> (sr-64)
|
||||
// }
|
||||
// } else {
|
||||
// sr = clz(d.hi) - clz(n.hi)
|
||||
// if sr > 64-1 {
|
||||
// return 0, n.all
|
||||
// }
|
||||
// sr++
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo << (64-sr)
|
||||
// r.all = n.all >> sr
|
||||
// if sr < 64 {
|
||||
// r.hi = n.hi >> sr
|
||||
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// } else {
|
||||
// r.hi = 0
|
||||
// r.lo = n.hi
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// carry: u64
|
||||
// for ; sr > 0; sr-- {
|
||||
// r.hi = (r.hi << 1) | (r.lo >> (64-1))
|
||||
// r.lo = (r.lo << 1) | (r.hi >> (64-1))
|
||||
// q.hi = (q.hi << 1) | (q.lo >> (64-1))
|
||||
// q.lo = (q.lo << 1) | carry
|
||||
|
||||
// carry = 0
|
||||
// if r.all >= d.all {
|
||||
// r.all -= d.all
|
||||
// carry = 1
|
||||
// }
|
||||
// }
|
||||
|
||||
// q.all = (q.all << 1) | (carry as u128)
|
||||
// return q.all, r.all
|
||||
// }
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
// TODO(bill): Use assembly instead here to implement atomics
|
||||
// Inline vs external file?
|
||||
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
_ := compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
|
||||
|
||||
|
||||
yield_thread :: proc() { win32.mm_pause(); }
|
||||
mfence :: proc() { win32.ReadWriteBarrier(); }
|
||||
sfence :: proc() { win32.WriteBarrier(); }
|
||||
lfence :: proc() { win32.ReadBarrier(); }
|
||||
|
||||
|
||||
load :: proc(a: ^i32) -> i32 {
|
||||
return a^;
|
||||
}
|
||||
store :: proc(a: ^i32, value: i32) {
|
||||
a^ = value;
|
||||
}
|
||||
compare_exchange :: proc(a: ^i32, expected, desired: i32) -> i32 {
|
||||
return win32.InterlockedCompareExchange(a, desired, expected);
|
||||
}
|
||||
exchanged :: proc(a: ^i32, desired: i32) -> i32 {
|
||||
return win32.InterlockedExchange(a, desired);
|
||||
}
|
||||
fetch_add :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedExchangeAdd(a, operand);
|
||||
|
||||
}
|
||||
fetch_and :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedAnd(a, operand);
|
||||
}
|
||||
fetch_or :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedOr(a, operand);
|
||||
}
|
||||
spin_lock :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
counter := 0;
|
||||
for old_value != 0 && (time_out < 0 || counter < time_out) {
|
||||
counter += 1;
|
||||
yield_thread();
|
||||
old_value = compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
}
|
||||
return old_value == 0;
|
||||
}
|
||||
spin_unlock :: proc(a: ^i32) {
|
||||
store(a, 0);
|
||||
mfence();
|
||||
}
|
||||
try_acquire_lock :: proc(a: ^i32) -> bool {
|
||||
yield_thread();
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
return old_value == 0;
|
||||
}
|
||||
|
||||
|
||||
load :: proc(a: ^i64) -> i64 {
|
||||
return a^;
|
||||
}
|
||||
store :: proc(a: ^i64, value: i64) {
|
||||
a^ = value;
|
||||
}
|
||||
compare_exchange :: proc(a: ^i64, expected, desired: i64) -> i64 {
|
||||
return win32.InterlockedCompareExchange64(a, desired, expected);
|
||||
}
|
||||
exchanged :: proc(a: ^i64, desired: i64) -> i64 {
|
||||
return win32.InterlockedExchange64(a, desired);
|
||||
}
|
||||
fetch_add :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedExchangeAdd64(a, operand);
|
||||
}
|
||||
fetch_and :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedAnd64(a, operand);
|
||||
}
|
||||
fetch_or :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedOr64(a, operand);
|
||||
}
|
||||
spin_lock :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
counter := 0;
|
||||
for old_value != 0 && (time_out < 0 || counter < time_out) {
|
||||
counter += 1;
|
||||
yield_thread();
|
||||
old_value = compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
}
|
||||
return old_value == 0;
|
||||
}
|
||||
spin_unlock :: proc(a: ^i64) {
|
||||
store(a, 0);
|
||||
mfence();
|
||||
}
|
||||
try_acquire_lock :: proc(a: ^i64) -> bool {
|
||||
yield_thread();
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
return old_value == 0;
|
||||
}
|
||||
@@ -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,474 @@
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
DEFAULT_BUF_SIZE :: 4096;
|
||||
|
||||
@(private)
|
||||
MIN_READ_BUFFER_SIZE :: 16;
|
||||
@(private)
|
||||
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;
|
||||
}
|
||||
|
||||
// read new data, and try a limited number of times
|
||||
for i := 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,255 @@
|
||||
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,
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
m: int;
|
||||
nr := 0;
|
||||
for nr < MAX_CONSECUTIVE_EMPTY_READS {
|
||||
m, err = io.read(r, b.buf[b.n:]);
|
||||
if m != 0 || err != nil {
|
||||
break;
|
||||
}
|
||||
nr += 1;
|
||||
}
|
||||
if nr == MAX_CONSECUTIVE_EMPTY_READS {
|
||||
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);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
// This is purely for documentation
|
||||
package builtin
|
||||
|
||||
nil :: nil;
|
||||
false :: 0!==0;
|
||||
true :: 0==0;
|
||||
|
||||
ODIN_OS :: ODIN_OS;
|
||||
ODIN_ARCH :: ODIN_ARCH;
|
||||
ODIN_ENDIAN :: ODIN_ENDIAN;
|
||||
ODIN_VENDOR :: ODIN_VENDOR;
|
||||
ODIN_VERSION :: ODIN_VERSION;
|
||||
ODIN_ROOT :: ODIN_ROOT;
|
||||
ODIN_DEBUG :: ODIN_DEBUG;
|
||||
|
||||
byte :: u8; // alias
|
||||
|
||||
bool :: bool;
|
||||
b8 :: b8;
|
||||
b16 :: b16;
|
||||
b32 :: b32;
|
||||
b64 :: b64;
|
||||
|
||||
i8 :: i8;
|
||||
u8 :: u8;
|
||||
i16 :: i16;
|
||||
u16 :: u16;
|
||||
i32 :: i32;
|
||||
u32 :: u32;
|
||||
i64 :: i64;
|
||||
u64 :: u64;
|
||||
|
||||
i128 :: i128;
|
||||
u128 :: u128;
|
||||
|
||||
rune :: rune;
|
||||
|
||||
f16 :: f16;
|
||||
f32 :: f32;
|
||||
f64 :: f64;
|
||||
|
||||
complex32 :: complex32;
|
||||
complex64 :: complex64;
|
||||
complex128 :: complex128;
|
||||
|
||||
quaternion64 :: quaternion64;
|
||||
quaternion128 :: quaternion128;
|
||||
quaternion256 :: quaternion256;
|
||||
|
||||
int :: int;
|
||||
uint :: uint;
|
||||
uintptr :: uintptr;
|
||||
|
||||
rawptr :: rawptr;
|
||||
string :: string;
|
||||
cstring :: cstring;
|
||||
any :: any;
|
||||
|
||||
typeid :: typeid;
|
||||
|
||||
// Endian Specific Types
|
||||
i16le :: i16le;
|
||||
u16le :: u16le;
|
||||
i32le :: i32le;
|
||||
u32le :: u32le;
|
||||
i64le :: i64le;
|
||||
u64le :: u64le;
|
||||
i128le :: i128le;
|
||||
u128le :: u128le;
|
||||
|
||||
i16be :: i16be;
|
||||
u16be :: u16be;
|
||||
i32be :: i32be;
|
||||
u32be :: u32be;
|
||||
i64be :: i64be;
|
||||
u64be :: u64be;
|
||||
i128be :: i128be;
|
||||
u128be :: u128be;
|
||||
|
||||
|
||||
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 ---
|
||||
|
||||
size_of :: proc($T: typeid) -> int ---
|
||||
align_of :: proc($T: typeid) -> int ---
|
||||
offset_of :: proc($T: typeid) -> uintptr ---
|
||||
type_of :: proc(x: expr) -> type ---
|
||||
type_info_of :: proc($T: typeid) -> ^runtime.Type_Info ---
|
||||
typeid_of :: proc($T: typeid) -> typeid ---
|
||||
|
||||
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
|
||||
|
||||
complex :: proc(real, imag: Float) -> Complex_Type ---
|
||||
quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type ---
|
||||
real :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||
jmag :: proc(value: Quaternion) -> Float ---
|
||||
kmag :: proc(value: Quaternion) -> Float ---
|
||||
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
|
||||
|
||||
expand_to_tuple :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
|
||||
|
||||
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);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
package c
|
||||
|
||||
import b "core:builtin"
|
||||
|
||||
CHAR_BIT :: 8;
|
||||
|
||||
bool :: b.bool;
|
||||
char :: b.u8;
|
||||
byte :: b.byte;
|
||||
schar :: b.i8;
|
||||
uchar :: b.u8;
|
||||
short :: b.i16;
|
||||
ushort :: b.u16;
|
||||
int :: b.i32;
|
||||
uint :: b.u32;
|
||||
|
||||
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;
|
||||
float :: b.f32;
|
||||
double :: b.f64;
|
||||
complex_float :: b.complex64;
|
||||
complex_double :: b.complex128;
|
||||
|
||||
#assert(size_of(b.uintptr) == size_of(b.int));
|
||||
|
||||
size_t :: b.uint;
|
||||
ssize_t :: b.int;
|
||||
ptrdiff_t :: b.int;
|
||||
uintptr_t :: b.uintptr;
|
||||
intptr_t :: b.int;
|
||||
|
||||
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,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(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(Value)) -> ^Map_Entry(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(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(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};
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package dynlib
|
||||
|
||||
Library :: distinct rawptr;
|
||||
@@ -0,0 +1,23 @@
|
||||
// +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 {
|
||||
flags |= os.RTLD_GLOBAL;
|
||||
}
|
||||
lib := os.dlopen(path, flags);
|
||||
return Library(lib), lib != nil;
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) {
|
||||
os.dlclose(rawptr(library));
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
ptr = os.dlsym(rawptr(library), symbol);
|
||||
found = ptr != nil;
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// +build windows
|
||||
package dynlib
|
||||
|
||||
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.LoadLibraryW(wide_path);
|
||||
return handle, handle != nil;
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) -> bool {
|
||||
ok := win32.FreeLibrary(cast(win32.HMODULE)library);
|
||||
return bool(ok);
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
c_str := strings.clone_to_cstring(symbol, context.temp_allocator);
|
||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str);
|
||||
found = ptr != nil;
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
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).
|
||||
// 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',
|
||||
};
|
||||
|
||||
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, 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,
|
||||
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,
|
||||
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,
|
||||
};
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
|
||||
out_length := (len(data) + 4) / 5 * 8;
|
||||
out := make([]byte, out_length);
|
||||
_encode(out, data);
|
||||
return string(out);
|
||||
}
|
||||
|
||||
@private
|
||||
_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
|
||||
out := out;
|
||||
data := data;
|
||||
|
||||
for len(data) > 0 {
|
||||
carry: byte;
|
||||
switch len(data) {
|
||||
case:
|
||||
out[7] = ENC_TABLE[data[4] & 0x1f];
|
||||
carry = data[4] >> 5;
|
||||
fallthrough;
|
||||
case 4:
|
||||
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f];
|
||||
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f];
|
||||
carry = data[3] >> 7;
|
||||
fallthrough;
|
||||
case 3:
|
||||
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f];
|
||||
carry = (data[2] >> 4) & 0x1f;
|
||||
fallthrough;
|
||||
case 2:
|
||||
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f];
|
||||
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f];
|
||||
carry = (data[1] >> 6) & 0x1f;
|
||||
fallthrough;
|
||||
case 1:
|
||||
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f];
|
||||
out[0] = ENC_TABLE[data[0] >> 3];
|
||||
}
|
||||
|
||||
if len(data) < 5 {
|
||||
out[7] = byte(PADDING);
|
||||
if len(data) < 4 {
|
||||
out[6] = byte(PADDING);
|
||||
out[5] = byte(PADDING);
|
||||
if len(data) < 3 {
|
||||
out[4] = byte(PADDING);
|
||||
if len(data) < 2 {
|
||||
out[3] = byte(PADDING);
|
||||
out[2] = byte(PADDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
data = data[5:];
|
||||
out = out[8:];
|
||||
}
|
||||
}
|
||||
|
||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
|
||||
if len(data) == 0 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
outi := 0;
|
||||
data := data;
|
||||
|
||||
out := make([]byte, len(data) / 8 * 5, allocator);
|
||||
end := false;
|
||||
for len(data) > 0 && !end {
|
||||
dbuf : [8]byte;
|
||||
dlen := 8;
|
||||
|
||||
for j := 0; j < 8; {
|
||||
if len(data) == 0 {
|
||||
dlen, end = j, true;
|
||||
break;
|
||||
}
|
||||
input := data[0];
|
||||
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 {
|
||||
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input");
|
||||
}
|
||||
dlen, end = j, true;
|
||||
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input");
|
||||
break;
|
||||
}
|
||||
dbuf[j] = DEC_TABLE[input];
|
||||
assert(dbuf[j] != 0xff, "Corrupted input");
|
||||
j += 1;
|
||||
}
|
||||
|
||||
switch dlen {
|
||||
case 8:
|
||||
out[outi + 4] = dbuf[6] << 5 | dbuf[7];
|
||||
fallthrough;
|
||||
case 7:
|
||||
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3;
|
||||
fallthrough;
|
||||
case 5:
|
||||
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1;
|
||||
fallthrough;
|
||||
case 4:
|
||||
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4;
|
||||
fallthrough;
|
||||
case 2:
|
||||
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2;
|
||||
}
|
||||
outi += 5;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
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).
|
||||
// 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', '+', '/',
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check {
|
||||
length := len(data);
|
||||
if length == 0 {
|
||||
return "";
|
||||
}
|
||||
|
||||
out_length := ((4 * length / 3) + 3) &~ 3;
|
||||
out := make([]byte, out_length, allocator);
|
||||
|
||||
c0, c1, c2, block: int;
|
||||
|
||||
for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
|
||||
c0, c1, c2 = int(data[i]), -1, -1;
|
||||
|
||||
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 == -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 {
|
||||
length := len(data);
|
||||
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;
|
||||
out := make([]byte, out_length, allocator);
|
||||
|
||||
c0, c1, c2, c3: int;
|
||||
b0, b1, b2: int;
|
||||
|
||||
for i, j := 0, 0; i < length; i, j = i + 4, j + 3 {
|
||||
c0 = DEC_TBL[data[i]];
|
||||
c1 = DEC_TBL[data[i + 1]];
|
||||
c2 = DEC_TBL[data[i + 2]];
|
||||
c3 = DEC_TBL[data[i + 3]];
|
||||
|
||||
b0 = (c0 << 2) | (c1 >> 4);
|
||||
b1 = (c1 << 4) | (c2 >> 2);
|
||||
b2 = (c2 << 6) | c3;
|
||||
|
||||
out[j] = byte(b0);
|
||||
out[j + 1] = byte(b1);
|
||||
out[j + 2] = byte(b2);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,852 @@
|
||||
package cel;
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
import "core:strings"
|
||||
|
||||
Array :: []Value;
|
||||
Dict :: map[string]Value;
|
||||
Nil_Value :: struct{};
|
||||
|
||||
Value :: union {
|
||||
Nil_Value,
|
||||
bool, i64, f64, string,
|
||||
Array, Dict,
|
||||
}
|
||||
|
||||
Parser :: struct {
|
||||
tokens: [dynamic]Token,
|
||||
prev_token: Token,
|
||||
curr_token: Token,
|
||||
curr_token_index: int,
|
||||
|
||||
allocated_strings: [dynamic]string,
|
||||
|
||||
error_count: int,
|
||||
|
||||
root: Dict,
|
||||
dict_stack: [dynamic]^Dict, // NOTE: Pointers may be stored on the stack
|
||||
}
|
||||
|
||||
|
||||
print_value :: proc(value: Value, pretty := true, indent := 0) {
|
||||
print_indent :: proc(indent: int) {
|
||||
for _ in 0..<indent {
|
||||
fmt.print("\t");
|
||||
}
|
||||
}
|
||||
|
||||
switch v in value {
|
||||
case bool: fmt.print(v);
|
||||
case i64: fmt.print(v);
|
||||
case f64: fmt.print(v);
|
||||
case string: fmt.print(v);
|
||||
case Array:
|
||||
fmt.print("[");
|
||||
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 { fmt.print(", "); }
|
||||
print_value(e);
|
||||
}
|
||||
}
|
||||
if pretty { print_indent(indent); }
|
||||
fmt.print("]");
|
||||
case Dict:
|
||||
fmt.print("{");
|
||||
if pretty { fmt.println(); }
|
||||
|
||||
i := 0;
|
||||
for name, val in v {
|
||||
if pretty {
|
||||
print_indent(indent+1);
|
||||
fmt.printf("%s = ", name);
|
||||
print_value(val, pretty, indent+1);
|
||||
fmt.println(",");
|
||||
} else {
|
||||
if i > 0 { fmt.print(", "); }
|
||||
fmt.printf("%s = ", name);
|
||||
print_value(val, pretty, indent+1);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if pretty { print_indent(indent); }
|
||||
fmt.print("}");
|
||||
case:
|
||||
fmt.print("nil");
|
||||
case Nil_Value:
|
||||
fmt.print("nil");
|
||||
}
|
||||
}
|
||||
print :: proc(p: ^Parser, pretty := false) {
|
||||
for name, val in p.root {
|
||||
fmt.printf("%s = ", name);
|
||||
print_value(val, pretty);
|
||||
fmt.println(";");
|
||||
}
|
||||
}
|
||||
|
||||
create_from_string :: proc(src: string) -> (^Parser, bool) {
|
||||
return init(transmute([]byte)src);
|
||||
}
|
||||
|
||||
|
||||
init :: proc(src: []byte) -> (^Parser, bool) {
|
||||
t: Tokenizer;
|
||||
tokenizer_init(&t, src);
|
||||
return create_from_tokenizer(&t);
|
||||
}
|
||||
|
||||
|
||||
create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
|
||||
p := new(Parser);
|
||||
for {
|
||||
tok := scan(t);
|
||||
if tok.kind == .Illegal {
|
||||
return p, false;
|
||||
}
|
||||
append(&p.tokens, tok);
|
||||
if tok.kind == .EOF {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if t.error_count > 0 {
|
||||
return p, false;
|
||||
}
|
||||
|
||||
if len(p.tokens) == 0 {
|
||||
tok := Token{kind = .EOF};
|
||||
tok.line, tok.column = 1, 1;
|
||||
append(&p.tokens, tok);
|
||||
return p, true;
|
||||
}
|
||||
|
||||
p.curr_token_index = 0;
|
||||
p.prev_token = p.tokens[p.curr_token_index];
|
||||
p.curr_token = p.tokens[p.curr_token_index];
|
||||
|
||||
p.root = Dict{};
|
||||
p.dict_stack = make([dynamic]^Dict, 0, 4);
|
||||
append(&p.dict_stack, &p.root);
|
||||
|
||||
for p.curr_token.kind != .EOF &&
|
||||
p.curr_token.kind != .Illegal &&
|
||||
p.curr_token_index < len(p.tokens) {
|
||||
if !parse_assignment(p) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return p, true;
|
||||
}
|
||||
|
||||
destroy :: proc(p: ^Parser) {
|
||||
destroy_value :: proc(value: Value) {
|
||||
#partial switch v in value {
|
||||
case Array:
|
||||
for elem in v {
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(v);
|
||||
|
||||
case Dict:
|
||||
for _, dv in v {
|
||||
destroy_value(dv);
|
||||
}
|
||||
delete(v);
|
||||
}
|
||||
}
|
||||
|
||||
delete(p.tokens);
|
||||
for s in p.allocated_strings {
|
||||
delete(s);
|
||||
}
|
||||
delete(p.allocated_strings);
|
||||
delete(p.dict_stack);
|
||||
|
||||
destroy_value(p.root);
|
||||
free(p);
|
||||
}
|
||||
|
||||
error :: proc(p: ^Parser, pos: Pos, msg: string, args: ..any) {
|
||||
fmt.eprintf("%s(%d:%d) Error: ", pos.file, pos.line, pos.column);
|
||||
fmt.eprintf(msg, ..args);
|
||||
fmt.eprintln();
|
||||
|
||||
p.error_count += 1;
|
||||
}
|
||||
|
||||
next_token :: proc(p: ^Parser) -> Token {
|
||||
p.prev_token = p.curr_token;
|
||||
prev := p.prev_token;
|
||||
|
||||
if p.curr_token_index+1 < len(p.tokens) {
|
||||
p.curr_token_index += 1;
|
||||
p.curr_token = p.tokens[p.curr_token_index];
|
||||
return prev;
|
||||
}
|
||||
p.curr_token_index = len(p.tokens);
|
||||
p.curr_token = p.tokens[p.curr_token_index-1];
|
||||
error(p, prev.pos, "Token is EOF");
|
||||
return prev;
|
||||
}
|
||||
|
||||
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:
|
||||
return;
|
||||
|
||||
case 'a': r = '\a';
|
||||
case 'b': r = '\b';
|
||||
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(p: ^Parser, t: Token) -> (string, bool) {
|
||||
if t.kind != .String {
|
||||
return t.lit, true;
|
||||
}
|
||||
s := t.lit;
|
||||
quote := '"';
|
||||
|
||||
if s == `""` {
|
||||
return "", true;
|
||||
}
|
||||
|
||||
if strings.contains_rune(s, '\n') >= 0 {
|
||||
return s, false;
|
||||
}
|
||||
|
||||
if strings.contains_rune(s, '\\') < 0 && strings.contains_rune(s, quote) < 0 {
|
||||
if quote == '"' {
|
||||
return s, true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
buf_len := 3*len(s) / 2;
|
||||
buf := make([]byte, buf_len);
|
||||
offset := 0;
|
||||
for len(s) > 0 {
|
||||
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote));
|
||||
if !ok {
|
||||
delete(buf);
|
||||
return s, 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]);
|
||||
|
||||
append(&p.allocated_strings, new_string);
|
||||
|
||||
return new_string, true;
|
||||
}
|
||||
|
||||
|
||||
allow_token :: proc(p: ^Parser, kind: Kind) -> bool {
|
||||
if p.curr_token.kind == kind {
|
||||
next_token(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
expect_token :: proc(p: ^Parser, kind: Kind) -> Token {
|
||||
prev := p.curr_token;
|
||||
if prev.kind != kind {
|
||||
got := prev.lit;
|
||||
if got == "\n" {
|
||||
got = ";";
|
||||
}
|
||||
error(p, prev.pos, "Expected %s, got %s", kind_to_string[kind], got);
|
||||
}
|
||||
next_token(p);
|
||||
return prev;
|
||||
}
|
||||
|
||||
expect_operator :: proc(p: ^Parser) -> Token {
|
||||
prev := p.curr_token;
|
||||
if !is_operator(prev.kind) {
|
||||
error(p, prev.pos, "Expected an operator, got %s", prev.lit);
|
||||
}
|
||||
|
||||
|
||||
next_token(p);
|
||||
return prev;
|
||||
}
|
||||
|
||||
fix_advance :: proc(p: ^Parser) {
|
||||
for {
|
||||
#partial switch t := p.curr_token; t.kind {
|
||||
case .EOF, .Semicolon:
|
||||
return;
|
||||
}
|
||||
next_token(p);
|
||||
}
|
||||
}
|
||||
|
||||
copy_value :: proc(value: Value) -> Value {
|
||||
#partial switch v in value {
|
||||
case Array:
|
||||
a := make(Array, len(v));
|
||||
for elem, idx in v {
|
||||
a[idx] = copy_value(elem);
|
||||
}
|
||||
return a;
|
||||
case Dict:
|
||||
d := make(Dict, cap(v));
|
||||
for key, val in v {
|
||||
d[key] = copy_value(val);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
lookup_value :: proc(p: ^Parser, name: string) -> (Value, bool) {
|
||||
for i := len(p.dict_stack)-1; i >= 0; i -= 1 {
|
||||
d := p.dict_stack[i];
|
||||
if val, ok := d[name]; ok {
|
||||
return copy_value(val), true;
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
tok := p.curr_token;
|
||||
#partial switch p.curr_token.kind {
|
||||
case .Ident:
|
||||
next_token(p);
|
||||
v, ok := lookup_value(p, tok.lit);
|
||||
if !ok { error(p, tok.pos, "Undeclared identifier %s", tok.lit); }
|
||||
return v, tok.pos;
|
||||
|
||||
case .True:
|
||||
next_token(p);
|
||||
return true, tok.pos;
|
||||
case .False:
|
||||
next_token(p);
|
||||
return false, tok.pos;
|
||||
|
||||
case .Nil:
|
||||
next_token(p);
|
||||
return Nil_Value{}, tok.pos;
|
||||
|
||||
case .Integer:
|
||||
next_token(p);
|
||||
i, _ := strconv.parse_i64(tok.lit);
|
||||
return i, tok.pos;
|
||||
|
||||
case .Float:
|
||||
next_token(p);
|
||||
f, _ := strconv.parse_f64(tok.lit);
|
||||
return f, tok.pos;
|
||||
|
||||
case .String:
|
||||
next_token(p);
|
||||
str, ok := unquote_string(p, tok);
|
||||
if !ok { error(p, tok.pos, "Unable to unquote string"); }
|
||||
return string(str), tok.pos;
|
||||
|
||||
case .Open_Paren:
|
||||
expect_token(p, .Open_Paren);
|
||||
expr, _ := parse_expr(p);
|
||||
expect_token(p, .Close_Paren);
|
||||
return expr, tok.pos;
|
||||
|
||||
case .Open_Bracket:
|
||||
expect_token(p, .Open_Bracket);
|
||||
elems := make([dynamic]Value, 0, 4);
|
||||
for p.curr_token.kind != .Close_Bracket &&
|
||||
p.curr_token.kind != .EOF {
|
||||
elem, _ := parse_expr(p);
|
||||
append(&elems, elem);
|
||||
|
||||
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
|
||||
next_token(p);
|
||||
} else if !allow_token(p, .Comma) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
expect_token(p, .Close_Bracket);
|
||||
return Array(elems[:]), tok.pos;
|
||||
|
||||
case .Open_Brace:
|
||||
expect_token(p, .Open_Brace);
|
||||
|
||||
dict := Dict{};
|
||||
append(&p.dict_stack, &dict);
|
||||
defer pop(&p.dict_stack);
|
||||
|
||||
for p.curr_token.kind != .Close_Brace &&
|
||||
p.curr_token.kind != .EOF {
|
||||
name_tok := p.curr_token;
|
||||
if !allow_token(p, .Ident) && !allow_token(p, .String) {
|
||||
name_tok = expect_token(p, .Ident);
|
||||
}
|
||||
|
||||
name, ok := unquote_string(p, name_tok);
|
||||
if !ok { error(p, tok.pos, "Unable to unquote string"); }
|
||||
expect_token(p, .Assign);
|
||||
elem, _ := parse_expr(p);
|
||||
|
||||
if _, ok2 := dict[name]; ok2 {
|
||||
error(p, name_tok.pos, "Previous declaration of %s in this scope", name);
|
||||
} else {
|
||||
dict[name] = elem;
|
||||
}
|
||||
|
||||
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
|
||||
next_token(p);
|
||||
} else if !allow_token(p, .Comma) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect_token(p, .Close_Brace);
|
||||
return dict, tok.pos;
|
||||
|
||||
}
|
||||
return nil, tok.pos;
|
||||
}
|
||||
|
||||
parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
|
||||
loop := true;
|
||||
for operand := operand; loop; {
|
||||
#partial switch p.curr_token.kind {
|
||||
case .Period:
|
||||
next_token(p);
|
||||
tok := next_token(p);
|
||||
|
||||
#partial switch tok.kind {
|
||||
case .Ident:
|
||||
d, ok := operand.(Dict);
|
||||
if !ok || d == nil {
|
||||
error(p, tok.pos, "Expected a dictionary");
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
name, usok := unquote_string(p, tok);
|
||||
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);
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
operand = val;
|
||||
case:
|
||||
error(p, tok.pos, "Expected a selector, got %s", tok.kind);
|
||||
operand = nil;
|
||||
}
|
||||
|
||||
case .Open_Bracket:
|
||||
expect_token(p, .Open_Bracket);
|
||||
index, index_pos := parse_expr(p);
|
||||
expect_token(p, .Close_Bracket);
|
||||
|
||||
|
||||
#partial switch a in operand {
|
||||
case Array:
|
||||
i, ok := index.(i64);
|
||||
if !ok {
|
||||
error(p, index_pos, "Index must be an integer for an array");
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
|
||||
if 0 <= i && i < i64(len(a)) {
|
||||
operand = a[i];
|
||||
} else {
|
||||
error(p, index_pos, "Index %d out of bounds range 0..%d", i, len(a));
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
|
||||
case Dict:
|
||||
key, ok := index.(string);
|
||||
if !ok {
|
||||
error(p, index_pos, "Index must be a string for a dictionary");
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
|
||||
val, found := a[key];
|
||||
if found {
|
||||
operand = val;
|
||||
} else {
|
||||
error(p, index_pos, "`%s` was not found in the dictionary", key);
|
||||
operand = nil;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
case:
|
||||
error(p, index_pos, "Indexing is only allowed on an array or dictionary");
|
||||
}
|
||||
|
||||
case:
|
||||
loop = false;
|
||||
}
|
||||
}
|
||||
|
||||
return operand, pos;
|
||||
}
|
||||
|
||||
parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
op := p.curr_token;
|
||||
#partial switch p.curr_token.kind {
|
||||
case .At:
|
||||
next_token(p);
|
||||
tok := expect_token(p, .String);
|
||||
v, ok := lookup_value(p, tok.lit);
|
||||
if !ok { error(p, tok.pos, "Undeclared identifier %s", tok.lit); }
|
||||
return parse_atom_expr(p, v, tok.pos);
|
||||
|
||||
case .Add, .Sub:
|
||||
next_token(p);
|
||||
// TODO(bill): Calcuate values as you go!
|
||||
expr, pos := parse_unary_expr(p);
|
||||
|
||||
#partial switch e in expr {
|
||||
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;
|
||||
}
|
||||
|
||||
return expr, op.pos;
|
||||
|
||||
case .Not:
|
||||
next_token(p);
|
||||
expr, _ := parse_unary_expr(p);
|
||||
if v, ok := expr.(bool); ok {
|
||||
return !v, op.pos;
|
||||
}
|
||||
error(p, op.pos, "Unary operator %s can only be used on booleans", op.lit);
|
||||
return nil, op.pos;
|
||||
}
|
||||
|
||||
return parse_atom_expr(p, parse_operand(p));
|
||||
}
|
||||
|
||||
|
||||
value_order :: proc(v: Value) -> int {
|
||||
#partial switch _ in v {
|
||||
case bool, string:
|
||||
return 1;
|
||||
case i64:
|
||||
return 2;
|
||||
case f64:
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
match_values :: proc(left, right: ^Value) -> bool {
|
||||
if value_order(right^) < value_order(left^) {
|
||||
return match_values(right, left);
|
||||
}
|
||||
|
||||
#partial switch x in left^ {
|
||||
case:
|
||||
right^ = left^;
|
||||
case bool, string:
|
||||
return true;
|
||||
case i64:
|
||||
#partial switch y in right^ {
|
||||
case i64:
|
||||
return true;
|
||||
case f64:
|
||||
left^ = f64(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
case f64:
|
||||
#partial switch y in right {
|
||||
case f64:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
calculate_binary_value :: proc(p: ^Parser, op: Kind, a_, b_: Value) -> (Value, bool) {
|
||||
// TODO(bill): Calculate value as you go!
|
||||
x, y := a_, b_;
|
||||
match_values(&x, &y);
|
||||
|
||||
|
||||
#partial switch a in x {
|
||||
case: return x, true;
|
||||
|
||||
case bool:
|
||||
b, ok := y.(bool);
|
||||
if !ok { return nil, false; }
|
||||
#partial switch op {
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .And: return a && b, true;
|
||||
case .Or: return a || b, true;
|
||||
}
|
||||
|
||||
case i64:
|
||||
b, ok := y.(i64);
|
||||
if !ok { return nil, false; }
|
||||
#partial switch op {
|
||||
case .Add: return a + b, true;
|
||||
case .Sub: return a - b, true;
|
||||
case .Mul: return a * b, true;
|
||||
case .Quo: return a / b, true;
|
||||
case .Rem: return a % b, true;
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .Lt: return a < b, true;
|
||||
case .Gt: return a > b, true;
|
||||
case .LtEq: return a <= b, true;
|
||||
case .GtEq: return a >= b, true;
|
||||
}
|
||||
|
||||
case f64:
|
||||
b, ok := y.(f64);
|
||||
if !ok { return nil, false; }
|
||||
|
||||
#partial switch op {
|
||||
case .Add: return a + b, true;
|
||||
case .Sub: return a - b, true;
|
||||
case .Mul: return a * b, true;
|
||||
case .Quo: return a / b, true;
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .Lt: return a < b, true;
|
||||
case .Gt: return a > b, true;
|
||||
case .LtEq: return a <= b, true;
|
||||
case .GtEq: return a >= b, true;
|
||||
}
|
||||
|
||||
case string:
|
||||
b, ok := y.(string);
|
||||
if !ok { return nil, false; }
|
||||
|
||||
#partial switch op {
|
||||
case .Add:
|
||||
n := len(a) + len(b);
|
||||
data := make([]byte, n);
|
||||
copy(data[:], a);
|
||||
copy(data[len(a):], b);
|
||||
s := string(data);
|
||||
append(&p.allocated_strings, s);
|
||||
return s, true;
|
||||
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .Lt: return a < b, true;
|
||||
case .Gt: return a > b, true;
|
||||
case .LtEq: return a <= b, true;
|
||||
case .GtEq: return a >= b, true;
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
parse_binary_expr :: proc(p: ^Parser, prec_in: int) -> (Value, Pos) {
|
||||
expr, pos := parse_unary_expr(p);
|
||||
for prec := precedence(p.curr_token.kind); prec >= prec_in; prec -= 1 {
|
||||
for {
|
||||
op := p.curr_token;
|
||||
op_prec := precedence(op.kind);
|
||||
if op_prec != prec {
|
||||
break;
|
||||
}
|
||||
expect_operator(p);
|
||||
|
||||
if op.kind == .Question {
|
||||
cond := expr;
|
||||
x, _ := parse_expr(p);
|
||||
expect_token(p, .Colon);
|
||||
y, _ := parse_expr(p);
|
||||
|
||||
if t, ok := cond.(bool); ok {
|
||||
expr = t ? x : y;
|
||||
} else {
|
||||
error(p, pos, "Condition must be a boolean");
|
||||
}
|
||||
|
||||
} else {
|
||||
right, right_pos := parse_binary_expr(p, prec+1);
|
||||
if right == nil {
|
||||
error(p, right_pos, "Expected expression on the right-hand side of the binary operator %s", op.lit);
|
||||
}
|
||||
left := expr;
|
||||
ok: bool;
|
||||
expr, ok = calculate_binary_value(p, op.kind, left, right);
|
||||
if !ok {
|
||||
error(p, pos, "Invalid binary operation");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return expr, pos;
|
||||
}
|
||||
|
||||
parse_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
return parse_binary_expr(p, 1);
|
||||
}
|
||||
|
||||
expect_semicolon :: proc(p: ^Parser) {
|
||||
kind := p.curr_token.kind;
|
||||
|
||||
#partial switch kind {
|
||||
case .Comma:
|
||||
error(p, p.curr_token.pos, "Expected ';', got ','");
|
||||
next_token(p);
|
||||
case .Semicolon:
|
||||
next_token(p);
|
||||
case .EOF:
|
||||
// okay
|
||||
case:
|
||||
error(p, p.curr_token.pos, "Expected ';', got %s", p.curr_token.lit);
|
||||
fix_advance(p);
|
||||
}
|
||||
}
|
||||
|
||||
parse_assignment :: proc(p: ^Parser) -> bool {
|
||||
top_dict :: proc(p: ^Parser) -> ^Dict {
|
||||
assert(len(p.dict_stack) > 0);
|
||||
return p.dict_stack[len(p.dict_stack)-1];
|
||||
}
|
||||
|
||||
if p.curr_token.kind == .Semicolon {
|
||||
next_token(p);
|
||||
return true;
|
||||
}
|
||||
if p.curr_token.kind == .EOF {
|
||||
return false;
|
||||
}
|
||||
|
||||
tok := p.curr_token;
|
||||
if allow_token(p, .Ident) || allow_token(p, .String) {
|
||||
expect_token(p, .Assign);
|
||||
name, ok := unquote_string(p, tok);
|
||||
if !ok { error(p, tok.pos, "Unable to unquote string"); }
|
||||
expr, _ := parse_expr(p);
|
||||
d := top_dict(p);
|
||||
if _, ok2 := d[name]; ok2 {
|
||||
error(p, tok.pos, "Previous declaration of %s", name);
|
||||
} else {
|
||||
d[name] = expr;
|
||||
}
|
||||
expect_semicolon(p);
|
||||
return true;
|
||||
}
|
||||
error(p, tok.pos, "Expected an assignment, got %s", kind_to_string[tok.kind]);
|
||||
fix_advance(p);
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
package cel
|
||||
|
||||
sample := `
|
||||
x = 123;
|
||||
y = 321.456;
|
||||
z = x * (y - 1) / 2;
|
||||
w = "foo" + "bar";
|
||||
|
||||
# This is a comment
|
||||
|
||||
asd = "Semicolons are optional"
|
||||
|
||||
a = {id = {b = 123}} # Dict
|
||||
b = a.id.b
|
||||
|
||||
f = [1, 4, 9] # Array
|
||||
g = f[2]
|
||||
|
||||
h = x < y and w == "foobar"
|
||||
i = h ? 123 : "google"
|
||||
|
||||
j = nil
|
||||
|
||||
"127.0.0.1" = "value" # Keys can be strings
|
||||
|
||||
"foo" = {
|
||||
"bar" = {
|
||||
"baz" = 123, # optional commas if newline is present
|
||||
"zab" = 456,
|
||||
"abz" = 789,
|
||||
},
|
||||
};
|
||||
|
||||
bar = @"foo"["bar"].baz
|
||||
`;
|
||||
|
||||
|
||||
main :: proc() {
|
||||
p, ok := create_from_string(sample);
|
||||
if !ok {
|
||||
return;
|
||||
}
|
||||
defer destroy(p);
|
||||
|
||||
if p.error_count == 0 {
|
||||
print(p);
|
||||
}
|
||||
}
|
||||
*/
|
||||
package cel
|
||||
@@ -0,0 +1,523 @@
|
||||
package cel
|
||||
|
||||
import "core:fmt"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
using Kind :: enum {
|
||||
Illegal,
|
||||
EOF,
|
||||
Comment,
|
||||
|
||||
_literal_start,
|
||||
Ident,
|
||||
Integer,
|
||||
Float,
|
||||
Char,
|
||||
String,
|
||||
_literal_end,
|
||||
|
||||
_keyword_start,
|
||||
True, // true
|
||||
False, // false
|
||||
Nil, // nil
|
||||
_keyword_end,
|
||||
|
||||
|
||||
_operator_start,
|
||||
Question, // ?
|
||||
|
||||
And, // and
|
||||
Or, // or
|
||||
|
||||
Add, // +
|
||||
Sub, // -
|
||||
Mul, // *
|
||||
Quo, // /
|
||||
Rem, // %
|
||||
|
||||
Not, // !
|
||||
|
||||
Eq, // ==
|
||||
NotEq, // !=
|
||||
Lt, // <
|
||||
Gt, // >
|
||||
LtEq, // <=
|
||||
GtEq, // >=
|
||||
|
||||
At, // @
|
||||
_operator_end,
|
||||
|
||||
_punc_start,
|
||||
Assign, // =
|
||||
|
||||
Open_Paren, // (
|
||||
Close_Paren, // )
|
||||
Open_Bracket, // [
|
||||
Close_Bracket, // ]
|
||||
Open_Brace, // {
|
||||
Close_Brace, // }
|
||||
|
||||
Colon, // :
|
||||
Semicolon, // ;
|
||||
Comma, // ,
|
||||
Period, // .
|
||||
_punc_end,
|
||||
}
|
||||
|
||||
|
||||
Pos :: struct {
|
||||
file: string,
|
||||
line: int,
|
||||
column: int,
|
||||
}
|
||||
|
||||
Token :: struct {
|
||||
kind: Kind,
|
||||
using pos: Pos,
|
||||
lit: string,
|
||||
}
|
||||
|
||||
Tokenizer :: struct {
|
||||
src: []byte,
|
||||
|
||||
file: string, // May not be used
|
||||
|
||||
curr_rune: rune,
|
||||
offset: int,
|
||||
read_offset: int,
|
||||
line_offset: int,
|
||||
line_count: int,
|
||||
|
||||
insert_semi: bool,
|
||||
|
||||
error_count: int,
|
||||
}
|
||||
|
||||
|
||||
keywords := map[string]Kind{
|
||||
"true" = True,
|
||||
"false" = False,
|
||||
"nil" = Nil,
|
||||
"and" = And,
|
||||
"or" = Or,
|
||||
};
|
||||
|
||||
kind_to_string := [len(Kind)]string{
|
||||
"illegal",
|
||||
"EOF",
|
||||
"comment",
|
||||
|
||||
"",
|
||||
"identifier",
|
||||
"integer",
|
||||
"float",
|
||||
"character",
|
||||
"string",
|
||||
"",
|
||||
|
||||
"",
|
||||
"true", "false", "nil",
|
||||
"",
|
||||
|
||||
"",
|
||||
"?", "and", "or",
|
||||
"+", "-", "*", "/", "%",
|
||||
"!",
|
||||
"==", "!=", "<", ">", "<=", ">=",
|
||||
"@",
|
||||
"",
|
||||
|
||||
"",
|
||||
"=",
|
||||
"(", ")",
|
||||
"[", "]",
|
||||
"{", "}",
|
||||
":", ";", ",", ".",
|
||||
"",
|
||||
};
|
||||
|
||||
precedence :: proc(op: Kind) -> int {
|
||||
#partial switch op {
|
||||
case Question:
|
||||
return 1;
|
||||
case Or:
|
||||
return 2;
|
||||
case And:
|
||||
return 3;
|
||||
case Eq, NotEq, Lt, Gt, LtEq, GtEq:
|
||||
return 4;
|
||||
case Add, Sub:
|
||||
return 5;
|
||||
case Mul, Quo, Rem:
|
||||
return 6;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
token_lookup :: proc(ident: string) -> Kind {
|
||||
if tok, is_keyword := keywords[ident]; is_keyword {
|
||||
return tok;
|
||||
}
|
||||
return Ident;
|
||||
}
|
||||
|
||||
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 := "") {
|
||||
t.src = src;
|
||||
t.file = file;
|
||||
t.curr_rune = ' ';
|
||||
t.offset = 0;
|
||||
t.read_offset = 0;
|
||||
t.line_offset = 0;
|
||||
t.line_count = 1;
|
||||
|
||||
advance_to_next_rune(t);
|
||||
if t.curr_rune == utf8.RUNE_BOM {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
}
|
||||
|
||||
token_error :: proc(t: ^Tokenizer, msg: string, args: ..any) {
|
||||
fmt.eprintf("%s(%d:%d) Error: ", t.file, t.line_count, t.read_offset-t.line_offset+1);
|
||||
fmt.eprintf(msg, ..args);
|
||||
fmt.eprintln();
|
||||
t.error_count += 1;
|
||||
}
|
||||
|
||||
advance_to_next_rune :: proc(t: ^Tokenizer) {
|
||||
if t.read_offset < len(t.src) {
|
||||
t.offset = t.read_offset;
|
||||
if t.curr_rune == '\n' {
|
||||
t.line_offset = t.offset;
|
||||
t.line_count += 1;
|
||||
}
|
||||
r, w := rune(t.src[t.read_offset]), 1;
|
||||
switch {
|
||||
case r == 0:
|
||||
token_error(t, "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 {
|
||||
token_error(t, "Illegal utf-8 encoding");
|
||||
} else if r == utf8.RUNE_BOM && t.offset > 0 {
|
||||
token_error(t, "Illegal byte order mark");
|
||||
}
|
||||
}
|
||||
|
||||
t.read_offset += w;
|
||||
t.curr_rune = r;
|
||||
} else {
|
||||
t.offset = len(t.src);
|
||||
if t.curr_rune == '\n' {
|
||||
t.line_offset = t.offset;
|
||||
t.line_count += 1;
|
||||
}
|
||||
t.curr_rune = utf8.RUNE_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
get_pos :: proc(t: ^Tokenizer) -> Pos {
|
||||
return Pos {
|
||||
file = t.file,
|
||||
line = t.line_count,
|
||||
column = t.offset - t.line_offset + 1,
|
||||
};
|
||||
}
|
||||
|
||||
is_letter :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case 'a'..'z', 'A'..'Z', '_':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
is_digit :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case '0'..'9':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||
loop: for {
|
||||
switch t.curr_rune {
|
||||
case '\n':
|
||||
if t.insert_semi {
|
||||
break loop;
|
||||
}
|
||||
fallthrough;
|
||||
case ' ', '\t', '\r', '\v', '\f':
|
||||
advance_to_next_rune(t);
|
||||
|
||||
case:
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan_identifier :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset;
|
||||
for is_letter(t.curr_rune) || is_digit(t.curr_rune) {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
return string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) {
|
||||
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
|
||||
for digit_value(t.curr_rune) < base || t.curr_rune == '_' {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
}
|
||||
scan_exponent :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
|
||||
kind = tok;
|
||||
if t.curr_rune == 'e' || t.curr_rune == 'E' {
|
||||
kind = Float;
|
||||
advance_to_next_rune(t);
|
||||
if t.curr_rune == '-' || t.curr_rune == '+' {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
if digit_value(t.curr_rune) < 10 {
|
||||
scan_mantissa(t, 10);
|
||||
} else {
|
||||
token_error(t, "Illegal floating point exponent");
|
||||
}
|
||||
}
|
||||
text = string(t.src[offset : t.offset]);
|
||||
return;
|
||||
}
|
||||
scan_fraction :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
|
||||
kind = tok;
|
||||
if t.curr_rune == '.' {
|
||||
kind = Float;
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 10);
|
||||
}
|
||||
|
||||
return scan_exponent(t, kind, offset);
|
||||
}
|
||||
|
||||
offset := t.offset;
|
||||
tok := Integer;
|
||||
|
||||
if seen_decimal_point {
|
||||
offset -= 1;
|
||||
tok = Float;
|
||||
scan_mantissa(t, 10);
|
||||
return scan_exponent(t, tok, offset);
|
||||
}
|
||||
|
||||
if t.curr_rune == '0' {
|
||||
offset = t.offset;
|
||||
advance_to_next_rune(t);
|
||||
switch t.curr_rune {
|
||||
case 'b', 'B':
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 2);
|
||||
if t.offset - offset <= 2 {
|
||||
token_error(t, "Illegal binary number");
|
||||
}
|
||||
case 'o', 'O':
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 8);
|
||||
if t.offset - offset <= 2 {
|
||||
token_error(t, "Illegal octal number");
|
||||
}
|
||||
case 'x', 'X':
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 16);
|
||||
if t.offset - offset <= 2 {
|
||||
token_error(t, "Illegal hexadecimal number");
|
||||
}
|
||||
case:
|
||||
scan_mantissa(t, 10);
|
||||
switch t.curr_rune {
|
||||
case '.', 'e', 'E':
|
||||
return scan_fraction(t, tok, offset);
|
||||
}
|
||||
}
|
||||
|
||||
return tok, string(t.src[offset:t.offset]);
|
||||
}
|
||||
|
||||
scan_mantissa(t, 10);
|
||||
|
||||
return scan_fraction(t, tok, offset);
|
||||
}
|
||||
|
||||
scan :: proc(t: ^Tokenizer) -> Token {
|
||||
skip_whitespace(t);
|
||||
|
||||
offset := t.offset;
|
||||
|
||||
tok: Kind;
|
||||
pos := get_pos(t);
|
||||
lit: string;
|
||||
|
||||
insert_semi := false;
|
||||
|
||||
|
||||
switch r := t.curr_rune; {
|
||||
case is_letter(r):
|
||||
insert_semi = true;
|
||||
lit = scan_identifier(t);
|
||||
tok = Ident;
|
||||
if len(lit) > 1 {
|
||||
tok = token_lookup(lit);
|
||||
}
|
||||
|
||||
case '0' <= r && r <= '9':
|
||||
insert_semi = true;
|
||||
tok, lit = scan_number(t, false);
|
||||
|
||||
case:
|
||||
advance_to_next_rune(t);
|
||||
switch r {
|
||||
case -1:
|
||||
if t.insert_semi {
|
||||
t.insert_semi = false;
|
||||
return Token{Semicolon, pos, "\n"};
|
||||
}
|
||||
return Token{EOF, pos, "\n"};
|
||||
|
||||
case '\n':
|
||||
t.insert_semi = false;
|
||||
return Token{Semicolon, pos, "\n"};
|
||||
|
||||
case '"':
|
||||
insert_semi = true;
|
||||
quote := r;
|
||||
tok = String;
|
||||
for {
|
||||
this_r := t.curr_rune;
|
||||
if this_r == '\n' || r < 0 {
|
||||
token_error(t, "String literal not terminated");
|
||||
break;
|
||||
}
|
||||
advance_to_next_rune(t);
|
||||
if this_r == quote {
|
||||
break;
|
||||
}
|
||||
// TODO(bill); Handle properly
|
||||
if this_r == '\\' && t.curr_rune == quote {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
}
|
||||
|
||||
lit = string(t.src[offset+1:t.offset-1]);
|
||||
|
||||
|
||||
case '#':
|
||||
for t.curr_rune != '\n' && t.curr_rune >= 0 {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
if t.insert_semi {
|
||||
t.insert_semi = false;
|
||||
return Token{Semicolon, pos, "\n"};
|
||||
}
|
||||
// Recursive!
|
||||
return scan(t);
|
||||
|
||||
case '?': tok = Question;
|
||||
case ':': tok = Colon;
|
||||
case '@': tok = At;
|
||||
|
||||
case ';':
|
||||
tok = Semicolon;
|
||||
lit = ";";
|
||||
case ',': tok = Comma;
|
||||
|
||||
case '(':
|
||||
tok = Open_Paren;
|
||||
case ')':
|
||||
insert_semi = true;
|
||||
tok = Close_Paren;
|
||||
|
||||
case '[':
|
||||
tok = Open_Bracket;
|
||||
case ']':
|
||||
insert_semi = true;
|
||||
tok = Close_Bracket;
|
||||
|
||||
case '{':
|
||||
tok = Open_Brace;
|
||||
case '}':
|
||||
insert_semi = true;
|
||||
tok = Close_Brace;
|
||||
|
||||
case '+': tok = Add;
|
||||
case '-': tok = Sub;
|
||||
case '*': tok = Mul;
|
||||
case '/': tok = Quo;
|
||||
case '%': tok = Rem;
|
||||
|
||||
case '!':
|
||||
tok = Not;
|
||||
if t.curr_rune == '=' {
|
||||
advance_to_next_rune(t);
|
||||
tok = NotEq;
|
||||
}
|
||||
|
||||
case '=':
|
||||
tok = Assign;
|
||||
if t.curr_rune == '=' {
|
||||
advance_to_next_rune(t);
|
||||
tok = Eq;
|
||||
}
|
||||
|
||||
case '<':
|
||||
tok = Lt;
|
||||
if t.curr_rune == '=' {
|
||||
advance_to_next_rune(t);
|
||||
tok = LtEq;
|
||||
}
|
||||
|
||||
case '>':
|
||||
tok = Gt;
|
||||
if t.curr_rune == '=' {
|
||||
advance_to_next_rune(t);
|
||||
tok = GtEq;
|
||||
}
|
||||
|
||||
case '.':
|
||||
if '0' <= t.curr_rune && t.curr_rune <= '9' {
|
||||
insert_semi = true;
|
||||
tok, lit = scan_number(t, true);
|
||||
} else {
|
||||
tok = Period;
|
||||
}
|
||||
|
||||
case:
|
||||
if r != utf8.RUNE_BOM {
|
||||
token_error(t, "Illegal character '%r'", r);
|
||||
}
|
||||
insert_semi = t.insert_semi;
|
||||
tok = Illegal;
|
||||
}
|
||||
}
|
||||
|
||||
t.insert_semi = insert_semi;
|
||||
|
||||
if lit == "" {
|
||||
lit = string(t.src[offset:t.offset]);
|
||||
}
|
||||
|
||||
return Token{tok, pos, lit};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
package json
|
||||
|
||||
import "core:mem"
|
||||
import "core:math/bits"
|
||||
import "core:runtime"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
|
||||
Marshal_Error :: enum {
|
||||
None,
|
||||
Unsupported_Type,
|
||||
Invalid_Data,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
|
||||
b: strings.Builder;
|
||||
strings.init_builder(&b, allocator);
|
||||
|
||||
err := marshal_arg(&b, v);
|
||||
|
||||
if err != .None {
|
||||
strings.destroy_builder(&b);
|
||||
return nil, err;
|
||||
}
|
||||
if len(b.buf) == 0 {
|
||||
strings.destroy_builder(&b);
|
||||
return nil, err;
|
||||
}
|
||||
return b.buf[:], err;
|
||||
}
|
||||
|
||||
|
||||
marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
using strings;
|
||||
using runtime;
|
||||
if v == nil {
|
||||
write_string(b, "null");
|
||||
return .None;
|
||||
}
|
||||
|
||||
ti := type_info_base(type_info_of(v.id));
|
||||
a := any{v.data, ti.id};
|
||||
|
||||
switch info in ti.variant {
|
||||
case Type_Info_Named:
|
||||
unreachable();
|
||||
|
||||
case Type_Info_Integer:
|
||||
buf: [21]byte;
|
||||
u: u64;
|
||||
switch i in a {
|
||||
case i8: u = u64(i);
|
||||
case i16: u = u64(i);
|
||||
case i32: u = u64(i);
|
||||
case i64: u = u64(i);
|
||||
case int: u = u64(i);
|
||||
case u8: u = u64(i);
|
||||
case u16: u = u64(i);
|
||||
case u32: u = u64(i);
|
||||
case u64: u = u64(i);
|
||||
case uint: u = u64(i);
|
||||
case uintptr: u = u64(i);
|
||||
|
||||
case i16le: u = u64(i);
|
||||
case i32le: u = u64(i);
|
||||
case i64le: u = u64(i);
|
||||
case u16le: u = u64(i);
|
||||
case u32le: u = u64(i);
|
||||
case u64le: u = u64(i);
|
||||
|
||||
case i16be: u = u64(i);
|
||||
case i32be: u = u64(i);
|
||||
case i64be: u = u64(i);
|
||||
case u16be: u = u64(i);
|
||||
case u32be: u = u64(i);
|
||||
case u64be: u = u64(i);
|
||||
}
|
||||
|
||||
s := strconv.append_bits(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil);
|
||||
write_string(b, s);
|
||||
|
||||
|
||||
case Type_Info_Rune:
|
||||
r := a.(rune);
|
||||
write_byte(b, '"');
|
||||
write_escaped_rune(b, r, '"', true);
|
||||
write_byte(b, '"');
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
buf: [386]byte;
|
||||
|
||||
str := strconv.append_float(buf[1:], val, 'f', 2*ti.size, 8*ti.size);
|
||||
s := buf[:len(str)+1];
|
||||
if s[1] == '+' || s[1] == '-' {
|
||||
s = s[1:];
|
||||
} else {
|
||||
s[0] = '+';
|
||||
}
|
||||
if s[0] == '+' {
|
||||
s = s[1:];
|
||||
}
|
||||
|
||||
write_string(b, string(s));
|
||||
|
||||
case Type_Info_Complex:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Quaternion:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_String:
|
||||
switch s in a {
|
||||
case string: write_quoted_string(b, s);
|
||||
case cstring: write_quoted_string(b, string(s));
|
||||
}
|
||||
|
||||
case Type_Info_Boolean:
|
||||
val: bool;
|
||||
switch b in a {
|
||||
case bool: val = bool(b);
|
||||
case b8: val = bool(b);
|
||||
case b16: val = bool(b);
|
||||
case b32: val = bool(b);
|
||||
case b64: val = bool(b);
|
||||
}
|
||||
write_string_builder(b, val ? "true" : "false");
|
||||
|
||||
case Type_Info_Any:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Type_Id:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Pointer:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Procedure:
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Tuple:
|
||||
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 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
}
|
||||
write_byte(b, ']');
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
write_byte(b, '[');
|
||||
array := cast(^mem.Raw_Dynamic_Array)v.data;
|
||||
for i in 0..<array.len {
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(array.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
}
|
||||
write_byte(b, ']');
|
||||
|
||||
case Type_Info_Slice:
|
||||
write_byte(b, '[');
|
||||
slice := cast(^mem.Raw_Slice)v.data;
|
||||
for i in 0..<slice.len {
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(slice.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
}
|
||||
write_byte(b, ']');
|
||||
|
||||
case Type_Info_Map:
|
||||
m := (^mem.Raw_Map)(v.data);
|
||||
|
||||
write_byte(b, '{');
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
return .Unsupported_Type;
|
||||
}
|
||||
entries := &m.entries;
|
||||
gs := type_info_base(info.generated_struct).variant.(Type_Info_Struct);
|
||||
ed := type_info_base(gs.types[1]).variant.(Type_Info_Dynamic_Array);
|
||||
entry_type := ed.elem.variant.(Type_Info_Struct);
|
||||
entry_size := ed.elem_size;
|
||||
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size);
|
||||
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, ": ");
|
||||
marshal_arg(b, any{value, info.value.id});
|
||||
}
|
||||
}
|
||||
write_byte(b, '}');
|
||||
|
||||
case Type_Info_Struct:
|
||||
write_byte(b, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
write_quoted_string(b, name);
|
||||
write_string(b, ": ");
|
||||
|
||||
id := info.types[i].id;
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i]);
|
||||
marshal_arg(b, any{data, id});
|
||||
}
|
||||
write_byte(b, '}');
|
||||
|
||||
case Type_Info_Union:
|
||||
tag_ptr := uintptr(v.data) + info.tag_offset;
|
||||
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
|
||||
|
||||
tag: i64 = -1;
|
||||
switch i in tag_any {
|
||||
case u8: tag = i64(i);
|
||||
case i8: tag = i64(i);
|
||||
case u16: tag = i64(i);
|
||||
case i16: tag = i64(i);
|
||||
case u32: tag = i64(i);
|
||||
case i32: tag = i64(i);
|
||||
case u64: tag = i64(i);
|
||||
case i64: tag = i64(i);
|
||||
case: panic("Invalid union tag type");
|
||||
}
|
||||
|
||||
if v.data == nil || tag == 0 {
|
||||
write_string(b, "null");
|
||||
} else {
|
||||
id := info.variants[tag-1].id;
|
||||
marshal_arg(b, any{v.data, id});
|
||||
}
|
||||
|
||||
case Type_Info_Enum:
|
||||
return marshal_arg(b, any{v.data, info.base.id});
|
||||
|
||||
case Type_Info_Bit_Set:
|
||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||
if ti == nil {
|
||||
return false;
|
||||
}
|
||||
t := runtime.type_info_base(ti);
|
||||
#partial switch info in t.variant {
|
||||
case runtime.Type_Info_Integer:
|
||||
switch info.endianness {
|
||||
case .Platform: return false;
|
||||
case .Little: return ODIN_ENDIAN != "little";
|
||||
case .Big: return ODIN_ENDIAN != "big";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bit_data: u64;
|
||||
bit_size := u64(8*ti.size);
|
||||
|
||||
do_byte_swap := is_bit_set_different_endian_to_platform(info.underlying);
|
||||
|
||||
switch bit_size {
|
||||
case 0: bit_data = 0;
|
||||
case 8:
|
||||
x := (^u8)(v.data)^;
|
||||
bit_data = u64(x);
|
||||
case 16:
|
||||
x := (^u16)(v.data)^;
|
||||
if do_byte_swap {
|
||||
x = bits.byte_swap(x);
|
||||
}
|
||||
bit_data = u64(x);
|
||||
case 32:
|
||||
x := (^u32)(v.data)^;
|
||||
if do_byte_swap {
|
||||
x = bits.byte_swap(x);
|
||||
}
|
||||
bit_data = u64(x);
|
||||
case 64:
|
||||
x := (^u64)(v.data)^;
|
||||
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 .Unsupported_Type;
|
||||
}
|
||||
|
||||
return .None;
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
package json
|
||||
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
import "core:strconv"
|
||||
|
||||
Parser :: struct {
|
||||
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, parse_integers := false, allocator := context.allocator) -> Parser {
|
||||
p: Parser;
|
||||
p.tok = make_tokenizer(data, spec, parse_integers);
|
||||
p.spec = spec;
|
||||
p.allocator = allocator;
|
||||
assert(p.allocator.procedure != nil);
|
||||
advance_token(&p);
|
||||
return p;
|
||||
}
|
||||
|
||||
parse :: proc(data: []byte, spec := Specification.JSON, parse_integers := false, allocator := context.allocator) -> (Value, Error) {
|
||||
context.allocator = allocator;
|
||||
p := make_parser(data, spec, parse_integers, allocator);
|
||||
|
||||
if p.spec == Specification.JSON5 {
|
||||
return parse_value(&p);
|
||||
}
|
||||
return parse_object(&p);
|
||||
}
|
||||
|
||||
token_end_pos :: proc(tok: Token) -> Pos {
|
||||
end := tok.pos;
|
||||
end.offset += len(tok.text);
|
||||
return end;
|
||||
}
|
||||
|
||||
advance_token :: proc(p: ^Parser) -> (Token, Error) {
|
||||
err: Error;
|
||||
p.prev_token = p.curr_token;
|
||||
p.curr_token, err = get_token(&p.tok);
|
||||
return p.prev_token, err;
|
||||
}
|
||||
|
||||
|
||||
allow_token :: proc(p: ^Parser, kind: Token_Kind) -> bool {
|
||||
if p.curr_token.kind == kind {
|
||||
advance_token(p);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
|
||||
prev := p.curr_token;
|
||||
advance_token(p);
|
||||
if prev.kind == kind {
|
||||
return .None;
|
||||
}
|
||||
return .Unexpected_Token;
|
||||
}
|
||||
|
||||
|
||||
|
||||
parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
value.pos = p.curr_token.pos;
|
||||
defer value.end = token_end_pos(p.prev_token);
|
||||
|
||||
token := p.curr_token;
|
||||
#partial switch token.kind {
|
||||
case .Null:
|
||||
value.value = Null{};
|
||||
advance_token(p);
|
||||
return;
|
||||
case .False:
|
||||
value.value = Boolean(false);
|
||||
advance_token(p);
|
||||
return;
|
||||
case .True:
|
||||
value.value = Boolean(true);
|
||||
advance_token(p);
|
||||
return;
|
||||
|
||||
case .Integer:
|
||||
i, _ := strconv.parse_i64(token.text);
|
||||
value.value = Integer(i);
|
||||
advance_token(p);
|
||||
return;
|
||||
case .Float:
|
||||
f, _ := strconv.parse_f64(token.text);
|
||||
value.value = Float(f);
|
||||
advance_token(p);
|
||||
return;
|
||||
case .String:
|
||||
value.value = String(unquote_string(token, p.spec, p.allocator));
|
||||
advance_token(p);
|
||||
return;
|
||||
|
||||
case .Open_Brace:
|
||||
return parse_object(p);
|
||||
|
||||
case .Open_Bracket:
|
||||
return parse_array(p);
|
||||
|
||||
case:
|
||||
if p.spec == Specification.JSON5 {
|
||||
#partial switch token.kind {
|
||||
case .Infinity:
|
||||
inf: u64 = 0x7ff0000000000000;
|
||||
if token.text[0] == '-' {
|
||||
inf = 0xfff0000000000000;
|
||||
}
|
||||
value.value = transmute(f64)inf;
|
||||
advance_token(p);
|
||||
return;
|
||||
case .NaN:
|
||||
nan: u64 = 0x7ff7ffffffffffff;
|
||||
if token.text[0] == '-' {
|
||||
nan = 0xfff7ffffffffffff;
|
||||
}
|
||||
value.value = transmute(f64)nan;
|
||||
advance_token(p);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = .Unexpected_Token;
|
||||
advance_token(p);
|
||||
return;
|
||||
}
|
||||
|
||||
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, .Open_Bracket); err != .None {
|
||||
return;
|
||||
}
|
||||
|
||||
array: Array;
|
||||
array.allocator = p.allocator;
|
||||
defer if err != .None {
|
||||
for elem in array {
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(array);
|
||||
}
|
||||
|
||||
for p.curr_token.kind != .Close_Bracket {
|
||||
elem, elem_err := parse_value(p);
|
||||
if elem_err != .None {
|
||||
err = elem_err;
|
||||
return;
|
||||
}
|
||||
append(&array, elem);
|
||||
|
||||
// Disallow trailing commas for the time being
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if err = expect_token(p, .Close_Bracket); err != .None {
|
||||
return;
|
||||
}
|
||||
|
||||
value.value = array;
|
||||
return;
|
||||
}
|
||||
|
||||
clone_string :: proc(s: string, allocator: mem.Allocator) -> string {
|
||||
n := len(s);
|
||||
b := make([]byte, n+1, allocator);
|
||||
copy(b, s);
|
||||
b[n] = 0;
|
||||
return string(b[:n]);
|
||||
}
|
||||
|
||||
parse_object_key :: proc(p: ^Parser) -> (key: string, err: Error) {
|
||||
tok := p.curr_token;
|
||||
if p.spec == Specification.JSON5 {
|
||||
if tok.kind == .String {
|
||||
expect_token(p, .String);
|
||||
key = unquote_string(tok, p.spec, p.allocator);
|
||||
return;
|
||||
} else if tok.kind == .Ident {
|
||||
expect_token(p, .Ident);
|
||||
key = clone_string(tok.text, p.allocator);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
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, .Open_Brace); err != .None {
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
obj: Object;
|
||||
obj.allocator = p.allocator;
|
||||
defer if err != .None {
|
||||
for key, elem in obj {
|
||||
delete(key, p.allocator);
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(obj);
|
||||
}
|
||||
|
||||
for p.curr_token.kind != .Close_Brace {
|
||||
key: string;
|
||||
key, err = parse_object_key(p);
|
||||
if err != .None {
|
||||
delete(key, p.allocator);
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
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 != .None {
|
||||
err = elem_err;
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
if key in obj {
|
||||
err = .Duplicate_Object_Key;
|
||||
value.pos = p.curr_token.pos;
|
||||
delete(key, p.allocator);
|
||||
return;
|
||||
}
|
||||
|
||||
obj[key] = elem;
|
||||
|
||||
if p.spec == Specification.JSON5 {
|
||||
// Allow trailing commas
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Disallow trailing commas
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err = expect_token(p, .Close_Brace); err != .None {
|
||||
value.pos = p.curr_token.pos;
|
||||
return;
|
||||
}
|
||||
|
||||
value.value = obj;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill): unquote_string assumes a mostly valid string
|
||||
unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator) -> string {
|
||||
get_u2_rune :: proc(s: string) -> rune {
|
||||
if len(s) < 4 || s[0] != '\\' || s[1] != 'x' {
|
||||
return -1;
|
||||
}
|
||||
|
||||
r: rune;
|
||||
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: return -1;
|
||||
}
|
||||
r = r*16 + x;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
get_u4_rune :: proc(s: string) -> rune {
|
||||
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
|
||||
return -1;
|
||||
}
|
||||
|
||||
r: rune;
|
||||
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: return -1;
|
||||
}
|
||||
r = r*16 + x;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
if token.kind != .String {
|
||||
return "";
|
||||
}
|
||||
s := token.text;
|
||||
if len(s) <= 2 {
|
||||
return "";
|
||||
}
|
||||
quote := s[0];
|
||||
if s[0] != s[len(s)-1] {
|
||||
// Invalid string
|
||||
return "";
|
||||
}
|
||||
s = s[1:len(s)-1];
|
||||
|
||||
i := 0;
|
||||
for i < len(s) {
|
||||
c := s[i];
|
||||
if c == '\\' || c == quote || c < ' ' {
|
||||
break;
|
||||
}
|
||||
if c < utf8.RUNE_SELF {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
r, w := utf8.decode_rune_in_string(s);
|
||||
if r == utf8.RUNE_ERROR && w == 1 {
|
||||
break;
|
||||
}
|
||||
i += w;
|
||||
}
|
||||
if i == len(s) {
|
||||
return clone_string(s, allocator);
|
||||
}
|
||||
|
||||
b := make([]byte, len(s) + 2*utf8.UTF_MAX, allocator);
|
||||
w := copy(b, s[0:i]);
|
||||
loop: for i < len(s) {
|
||||
c := s[i];
|
||||
switch {
|
||||
case c == '\\':
|
||||
i += 1;
|
||||
if i >= len(s) {
|
||||
break loop;
|
||||
}
|
||||
switch s[i] {
|
||||
case: break loop;
|
||||
case '"', '\'', '\\', '/':
|
||||
b[w] = s[i];
|
||||
i += 1;
|
||||
w += 1;
|
||||
|
||||
case 'b':
|
||||
b[w] = '\b';
|
||||
i += 1;
|
||||
w += 1;
|
||||
case 'f':
|
||||
b[w] = '\f';
|
||||
i += 1;
|
||||
w += 1;
|
||||
case 'r':
|
||||
b[w] = '\r';
|
||||
i += 1;
|
||||
w += 1;
|
||||
case 't':
|
||||
b[w] = '\t';
|
||||
i += 1;
|
||||
w += 1;
|
||||
case 'n':
|
||||
b[w] = '\n';
|
||||
i += 1;
|
||||
w += 1;
|
||||
case 'u':
|
||||
i -= 1; // Include the \u in the check for sanity sake
|
||||
r := get_u4_rune(s[i:]);
|
||||
if r < 0 {
|
||||
break loop;
|
||||
}
|
||||
i += 6;
|
||||
|
||||
buf, buf_width := utf8.encode_rune(r);
|
||||
copy(b[w:], buf[:buf_width]);
|
||||
w += buf_width;
|
||||
|
||||
|
||||
case '0':
|
||||
if spec == Specification.JSON5 {
|
||||
b[w] = '\x00';
|
||||
i += 1;
|
||||
w += 1;
|
||||
} else {
|
||||
break loop;
|
||||
}
|
||||
case 'v':
|
||||
if spec == Specification.JSON5 {
|
||||
b[w] = '\v';
|
||||
i += 1;
|
||||
w += 1;
|
||||
} else {
|
||||
break loop;
|
||||
}
|
||||
|
||||
case 'x':
|
||||
if spec == Specification.JSON5 {
|
||||
i -= 1; // Include the \x in the check for sanity sake
|
||||
r := get_u2_rune(s[i:]);
|
||||
if r < 0 {
|
||||
break loop;
|
||||
}
|
||||
i += 4;
|
||||
|
||||
buf, buf_width := utf8.encode_rune(r);
|
||||
copy(b[w:], buf[:buf_width]);
|
||||
w += buf_width;
|
||||
} else {
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
|
||||
case c == quote, c < ' ':
|
||||
break loop;
|
||||
|
||||
case c < utf8.RUNE_SELF:
|
||||
b[w] = c;
|
||||
i += 1;
|
||||
w += 1;
|
||||
|
||||
case:
|
||||
r, width := utf8.decode_rune_in_string(s[i:]);
|
||||
i += width;
|
||||
|
||||
buf, buf_width := utf8.encode_rune(r);
|
||||
assert(buf_width <= width);
|
||||
copy(b[w:], buf[:buf_width]);
|
||||
w += buf_width;
|
||||
}
|
||||
}
|
||||
|
||||
return string(b[:w]);
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
package json
|
||||
|
||||
import "core:unicode/utf8"
|
||||
|
||||
Token :: struct {
|
||||
using pos: Pos,
|
||||
kind: Token_Kind,
|
||||
text: string,
|
||||
}
|
||||
|
||||
Token_Kind :: enum {
|
||||
Invalid,
|
||||
EOF,
|
||||
|
||||
Null,
|
||||
False,
|
||||
True,
|
||||
|
||||
Infinity,
|
||||
NaN,
|
||||
|
||||
Ident,
|
||||
|
||||
Integer,
|
||||
Float,
|
||||
String,
|
||||
|
||||
Colon,
|
||||
Comma,
|
||||
|
||||
Open_Brace,
|
||||
Close_Brace,
|
||||
|
||||
Open_Bracket,
|
||||
Close_Bracket,
|
||||
}
|
||||
|
||||
Tokenizer :: struct {
|
||||
using pos: Pos,
|
||||
data: []byte,
|
||||
r: rune, // current rune
|
||||
w: int, // current rune width in bytes
|
||||
curr_line_offset: int,
|
||||
spec: Specification,
|
||||
parse_integers: bool,
|
||||
}
|
||||
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
next_rune :: proc(t: ^Tokenizer) -> rune #no_bounds_check {
|
||||
if t.offset >= len(t.data) {
|
||||
return utf8.RUNE_EOF;
|
||||
}
|
||||
t.offset += t.w;
|
||||
t.r, t.w = utf8.decode_rune(t.data[t.offset:]);
|
||||
t.pos.column = t.offset - t.curr_line_offset;
|
||||
return t.r;
|
||||
}
|
||||
|
||||
|
||||
get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
skip_digits :: proc(t: ^Tokenizer) {
|
||||
for t.offset < len(t.data) {
|
||||
if '0' <= t.r && t.r <= '9' {
|
||||
// Okay
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
next_rune(t);
|
||||
}
|
||||
}
|
||||
skip_hex_digits :: proc(t: ^Tokenizer) {
|
||||
for t.offset < len(t.data) {
|
||||
next_rune(t);
|
||||
switch t.r {
|
||||
case '0'..'9', 'a'..'f', 'A'..'F':
|
||||
// Okay
|
||||
case:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan_espace :: proc(t: ^Tokenizer) -> bool {
|
||||
switch t.r {
|
||||
case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f':
|
||||
next_rune(t);
|
||||
return true;
|
||||
case 'u':
|
||||
// Expect 4 hexadecimal digits
|
||||
for i := 0; i < 4; i += 1 {
|
||||
r := next_rune(t);
|
||||
switch r {
|
||||
case '0'..'9', 'a'..'f', 'A'..'F':
|
||||
// Okay
|
||||
case:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case:
|
||||
// Ignore the next rune regardless
|
||||
next_rune(t);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
skip_whitespace :: proc(t: ^Tokenizer) -> rune {
|
||||
loop: for t.offset < len(t.data) {
|
||||
switch t.r {
|
||||
case ' ', '\t', '\v', '\f', '\r':
|
||||
next_rune(t);
|
||||
case '\n':
|
||||
t.line += 1;
|
||||
t.curr_line_offset = t.offset;
|
||||
t.pos.column = 1;
|
||||
next_rune(t);
|
||||
case:
|
||||
if t.spec == .JSON5 {
|
||||
switch t.r {
|
||||
case 0x2028, 0x2029, 0xFEFF:
|
||||
next_rune(t);
|
||||
continue loop;
|
||||
}
|
||||
}
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
return t.r;
|
||||
}
|
||||
|
||||
skip_to_next_line :: proc(t: ^Tokenizer) {
|
||||
for t.offset < len(t.data) {
|
||||
r := next_rune(t);
|
||||
if r == '\n' {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skip_alphanum :: proc(t: ^Tokenizer) {
|
||||
for t.offset < len(t.data) {
|
||||
switch next_rune(t) {
|
||||
case 'A'..'Z', 'a'..'z', '0'..'9', '_':
|
||||
continue;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
skip_whitespace(t);
|
||||
|
||||
token.pos = t.pos;
|
||||
|
||||
token.kind = .Invalid;
|
||||
|
||||
curr_rune := t.r;
|
||||
next_rune(t);
|
||||
|
||||
block: switch curr_rune {
|
||||
case utf8.RUNE_ERROR:
|
||||
err = .Illegal_Character;
|
||||
case utf8.RUNE_EOF, '\x00':
|
||||
token.kind = .EOF;
|
||||
err = .EOF;
|
||||
|
||||
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 = .Null;
|
||||
case "false": token.kind = .False;
|
||||
case "true": token.kind = .True;
|
||||
case:
|
||||
if t.spec == .JSON5 {
|
||||
switch str {
|
||||
case "Infinity": token.kind = .Infinity;
|
||||
case "NaN": token.kind = .NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case '+':
|
||||
err = .Illegal_Character;
|
||||
if t.spec != .JSON5 {
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
|
||||
case '-':
|
||||
switch t.r {
|
||||
case '0'..'9':
|
||||
// Okay
|
||||
case:
|
||||
// Illegal use of +/-
|
||||
err = .Illegal_Character;
|
||||
|
||||
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 = .Infinity;
|
||||
case "-NaN": token.kind = .NaN;
|
||||
}
|
||||
}
|
||||
break block;
|
||||
}
|
||||
fallthrough;
|
||||
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
skip_digits(t);
|
||||
|
||||
if t.r == '.' {
|
||||
token.kind = .Float;
|
||||
next_rune(t);
|
||||
skip_digits(t);
|
||||
}
|
||||
if t.r == 'e' || t.r == 'E' {
|
||||
switch r := next_rune(t); r {
|
||||
case '+', '-':
|
||||
next_rune(t);
|
||||
}
|
||||
skip_digits(t);
|
||||
}
|
||||
|
||||
str := string(t.data[token.offset:t.offset]);
|
||||
if !is_valid_number(str, t.spec) {
|
||||
err = .Invalid_Number;
|
||||
}
|
||||
|
||||
case '.':
|
||||
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 {
|
||||
case '+', '-':
|
||||
next_rune(t);
|
||||
}
|
||||
skip_digits(t);
|
||||
}
|
||||
str := string(t.data[token.offset:t.offset]);
|
||||
if !is_valid_number(str, t.spec) {
|
||||
err = .Invalid_Number;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case '\'':
|
||||
err = .Illegal_Character;
|
||||
if t.spec != .JSON5 {
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
case '"':
|
||||
token.kind = .String;
|
||||
quote := curr_rune;
|
||||
for t.offset < len(t.data) {
|
||||
r := t.r;
|
||||
if r == '\n' || r < 0 {
|
||||
err = .String_Not_Terminated;
|
||||
break;
|
||||
}
|
||||
next_rune(t);
|
||||
if r == quote {
|
||||
break;
|
||||
}
|
||||
if r == '\\' {
|
||||
scan_espace(t);
|
||||
}
|
||||
}
|
||||
|
||||
str := string(t.data[token.offset : t.offset]);
|
||||
if !is_valid_string_literal(str, t.spec) {
|
||||
err = .Invalid_String;
|
||||
}
|
||||
|
||||
|
||||
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 = .Illegal_Character;
|
||||
if t.spec == .JSON5 {
|
||||
switch t.r {
|
||||
case '/':
|
||||
// Single-line comments
|
||||
skip_to_next_line(t);
|
||||
return get_token(t);
|
||||
case '*':
|
||||
// None-nested multi-line comments
|
||||
for t.offset < len(t.data) {
|
||||
next_rune(t);
|
||||
if t.r == '*' {
|
||||
next_rune(t);
|
||||
if t.r == '/' {
|
||||
next_rune(t);
|
||||
return get_token(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
err = .EOF;
|
||||
}
|
||||
}
|
||||
|
||||
case: err = .Illegal_Character;
|
||||
}
|
||||
|
||||
token.text = string(t.data[token.offset : t.offset]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
s := str;
|
||||
if s == "" {
|
||||
return false;
|
||||
}
|
||||
|
||||
if s[0] == '-' {
|
||||
s = s[1:];
|
||||
if s == "" {
|
||||
return false;
|
||||
}
|
||||
} else if spec == .JSON5 {
|
||||
if s[0] == '+' { // Allow positive sign
|
||||
s = s[1:];
|
||||
if s == "" {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch s[0] {
|
||||
case '0':
|
||||
s = s[1:];
|
||||
case '1'..'9':
|
||||
s = s[1:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:];
|
||||
}
|
||||
case '.':
|
||||
if spec == .JSON5 { // Allow leading decimal point
|
||||
s = s[1:];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
case:
|
||||
return false;
|
||||
}
|
||||
|
||||
if spec == .JSON5 {
|
||||
if len(s) == 1 && s[0] == '.' { // Allow trailing decimal point
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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' {
|
||||
s = s[1:];
|
||||
}
|
||||
}
|
||||
|
||||
if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
|
||||
s = s[1:];
|
||||
switch s[0] {
|
||||
case '+', '-':
|
||||
s = s[1:];
|
||||
if s == "" {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:];
|
||||
}
|
||||
}
|
||||
|
||||
// The string should be empty now to be valid
|
||||
return s == "";
|
||||
}
|
||||
|
||||
is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
|
||||
s := str;
|
||||
if len(s) < 2 {
|
||||
return false;
|
||||
}
|
||||
quote := s[0];
|
||||
if s[0] != s[len(s)-1] {
|
||||
return false;
|
||||
}
|
||||
if s[0] != '"' || s[len(s)-1] != '"' {
|
||||
if spec == .JSON5 {
|
||||
if s[0] != '\'' || s[len(s)-1] != '\'' {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
s = s[1 : len(s)-1];
|
||||
|
||||
i := 0;
|
||||
for i < len(s) {
|
||||
c := s[i];
|
||||
switch {
|
||||
case c == '\\':
|
||||
i += 1;
|
||||
if i >= len(s) {
|
||||
return false;
|
||||
}
|
||||
switch s[i] {
|
||||
case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f':
|
||||
i += 1;
|
||||
case 'u':
|
||||
if i >= len(s) {
|
||||
return false;
|
||||
}
|
||||
hex := s[i+1:];
|
||||
if len(hex) < 4 {
|
||||
return false;
|
||||
}
|
||||
hex = hex[:4];
|
||||
i += 5;
|
||||
|
||||
for j := 0; j < 4; j += 1 {
|
||||
c2 := hex[j];
|
||||
switch c2 {
|
||||
case '0'..'9', 'a'..'z', 'A'..'Z':
|
||||
// Okay
|
||||
case:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
case: return false;
|
||||
}
|
||||
|
||||
case c == quote, c < ' ':
|
||||
return false;
|
||||
|
||||
case c < utf8.RUNE_SELF:
|
||||
i += 1;
|
||||
|
||||
case:
|
||||
r, width := utf8.decode_rune_in_string(s[i:]);
|
||||
if r == utf8.RUNE_ERROR && width == 1 {
|
||||
return false;
|
||||
}
|
||||
i += width;
|
||||
}
|
||||
}
|
||||
if i == len(s) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package json
|
||||
|
||||
Specification :: enum {
|
||||
JSON,
|
||||
JSON5, // https://json5.org/
|
||||
// MJSON, // http://bitsquid.blogspot.com/2009/09/json-configuration-data.html
|
||||
}
|
||||
|
||||
Null :: distinct rawptr;
|
||||
Integer :: i64;
|
||||
Float :: f64;
|
||||
Boolean :: bool;
|
||||
String :: string;
|
||||
Array :: distinct [dynamic]Value;
|
||||
Object :: distinct map[string]Value;
|
||||
|
||||
Value :: struct {
|
||||
pos, end: Pos,
|
||||
value: union {
|
||||
Null,
|
||||
Integer,
|
||||
Float,
|
||||
Boolean,
|
||||
String,
|
||||
Array,
|
||||
Object,
|
||||
},
|
||||
}
|
||||
|
||||
Pos :: struct {
|
||||
offset: int,
|
||||
line: int,
|
||||
column: int,
|
||||
}
|
||||
|
||||
|
||||
Error :: enum {
|
||||
None,
|
||||
|
||||
EOF, // Not necessarily an error
|
||||
|
||||
// Tokenizing Errors
|
||||
Illegal_Character,
|
||||
Invalid_Number,
|
||||
String_Not_Terminated,
|
||||
Invalid_String,
|
||||
|
||||
|
||||
// Parsing Errors
|
||||
Unexpected_Token,
|
||||
Expected_String_For_Object_Key,
|
||||
Duplicate_Object_Key,
|
||||
Expected_Colon_After_Key,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
destroy_value :: proc(value: Value) {
|
||||
#partial switch v in value.value {
|
||||
case Object:
|
||||
for key, elem in v {
|
||||
delete(key);
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(v);
|
||||
case Array:
|
||||
for elem in v {
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(v);
|
||||
case String:
|
||||
delete(v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
package json
|
||||
|
||||
import "core:mem"
|
||||
|
||||
// NOTE(bill): is_valid will not check for duplicate keys
|
||||
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);
|
||||
}
|
||||
return validate_object(&p);
|
||||
}
|
||||
|
||||
validate_object_key :: proc(p: ^Parser) -> bool {
|
||||
tok := p.curr_token;
|
||||
if p.spec == Specification.JSON5 {
|
||||
if tok.kind == .String {
|
||||
expect_token(p, .String);
|
||||
return true;
|
||||
} else if tok.kind == .Ident {
|
||||
expect_token(p, .Ident);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
err := expect_token(p, .String);
|
||||
return err == Error.None;
|
||||
}
|
||||
validate_object :: proc(p: ^Parser) -> bool {
|
||||
if err := expect_token(p, .Open_Brace); err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
for p.curr_token.kind != .Close_Brace {
|
||||
if !validate_object_key(p) {
|
||||
return false;
|
||||
}
|
||||
if colon_err := expect_token(p, .Colon); colon_err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !validate_value(p) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if p.spec == Specification.JSON5 {
|
||||
// Allow trailing commas
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Disallow trailing commas
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, .Open_Bracket); err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
for p.curr_token.kind != .Close_Bracket {
|
||||
if !validate_value(p) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disallow trailing commas for the time being
|
||||
if allow_token(p, .Comma) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if err := expect_token(p, .Close_Bracket); err != Error.None {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
validate_value :: proc(p: ^Parser) -> bool {
|
||||
token := p.curr_token;
|
||||
|
||||
#partial switch token.kind {
|
||||
case .Null, .False, .True:
|
||||
advance_token(p);
|
||||
return true;
|
||||
case .Integer, .Float:
|
||||
advance_token(p);
|
||||
return true;
|
||||
case .String:
|
||||
advance_token(p);
|
||||
return is_valid_string_literal(token.text, p.spec);
|
||||
|
||||
case .Open_Brace:
|
||||
return validate_object(p);
|
||||
|
||||
case .Open_Bracket:
|
||||
return validate_array(p);
|
||||
|
||||
case:
|
||||
if p.spec == Specification.JSON5 {
|
||||
#partial switch token.kind {
|
||||
case .Infinity, .NaN:
|
||||
advance_token(p);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
-1109
File diff suppressed because it is too large
Load Diff
+2089
File diff suppressed because it is too large
Load Diff
@@ -1,208 +1,21 @@
|
||||
crc32 :: proc(data: []byte) -> u32 {
|
||||
result := ~cast(u32)0;
|
||||
package hash
|
||||
|
||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
result := ~u32(seed);
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc32_table[(result ~ cast(u32)b) & 0xff];
|
||||
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
crc64 :: proc(data: []byte) -> u64 {
|
||||
result := ~cast(u64)0;
|
||||
crc64 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check {
|
||||
result := ~u64(seed);
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc64_table[(result ~ cast(u64)b) & 0xff];
|
||||
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
|
||||
fnv32 :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ cast(u32)b;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64 :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ cast(u64)b;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv32a :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h ~ cast(u32)b) * 0x01000193;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64a :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h ~ cast(u64)b) * 0x100000001b3;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
murmur32 :: proc(data: []byte) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51;
|
||||
c2_32: u32 : 0x1b873593;
|
||||
|
||||
h1: u32 = 0;
|
||||
nblocks := data.count/4;
|
||||
p := data.data;
|
||||
p1 := p + 4*nblocks;
|
||||
|
||||
for ; p < p1; p += 4 {
|
||||
k1 := (cast(^u32)p)^;
|
||||
|
||||
k1 *= c1_32;
|
||||
k1 = (k1 << 15) | (k1 >> 17);
|
||||
k1 *= c2_32;
|
||||
|
||||
h1 ~= k1;
|
||||
h1 = (h1 << 13) | (h1 >> 19);
|
||||
h1 = h1*5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
tail := data[nblocks*4:];
|
||||
|
||||
k1: u32;
|
||||
match tail.count&3 {
|
||||
case 3:
|
||||
k1 ~= cast(u32)tail[2] << 16;
|
||||
fallthrough;
|
||||
case 2:
|
||||
k1 ~= cast(u32)tail[2] << 8;
|
||||
fallthrough;
|
||||
case 1:
|
||||
k1 ~= cast(u32)tail[0];
|
||||
k1 *= c1_32;
|
||||
k1 = (k1 << 15) | (k1 >> 17) ;
|
||||
k1 *= c2_32;
|
||||
h1 ~= k1;
|
||||
}
|
||||
|
||||
h1 ~= cast(u32)data.count;
|
||||
|
||||
h1 ~= h1 >> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
h1 ~= h1 >> 13;
|
||||
h1 *= 0xc2b2ae35;
|
||||
h1 ~= h1 >> 16;
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
murmur64 :: proc(data: []byte) -> u64 {
|
||||
SEED :: 0x9747b28c;
|
||||
|
||||
when false && size_of(int) == 8 {
|
||||
m :: 0xc6a4a7935bd1e995;
|
||||
r :: 47;
|
||||
|
||||
h: u64 = SEED ~ (cast(u64)data.count * m);
|
||||
data64 := slice_ptr(cast(^u64)^data[0], data.count/size_of(u64));
|
||||
|
||||
for _, i in data64 {
|
||||
k := data64[i];
|
||||
|
||||
k *= m;
|
||||
k ~= k>>r;
|
||||
k *= m;
|
||||
|
||||
h ~= k;
|
||||
h *= m;
|
||||
}
|
||||
|
||||
match data.count&7 {
|
||||
case 7: h ~= cast(u64)data[6] << 48; fallthrough;
|
||||
case 6: h ~= cast(u64)data[5] << 40; fallthrough;
|
||||
case 5: h ~= cast(u64)data[4] << 32; fallthrough;
|
||||
case 4: h ~= cast(u64)data[3] << 24; fallthrough;
|
||||
case 3: h ~= cast(u64)data[2] << 16; fallthrough;
|
||||
case 2: h ~= cast(u64)data[1] << 8; fallthrough;
|
||||
case 1:
|
||||
h ~= cast(u64)data[0];
|
||||
h *= m;
|
||||
}
|
||||
|
||||
h ~= h>>r;
|
||||
h *= m;
|
||||
h ~= h>>r;
|
||||
|
||||
return h;
|
||||
} else {
|
||||
m :: 0x5bd1e995;
|
||||
r :: 24;
|
||||
|
||||
h1: u32 = cast(u32)SEED ~ cast(u32)data.count;
|
||||
h2: u32 = SEED >> 32;
|
||||
|
||||
data32 := slice_ptr(cast(^u32)^data[0], data.count/size_of(u32));
|
||||
len := data.count;
|
||||
|
||||
i := 0;
|
||||
for len >= 8 {
|
||||
k1, k2: u32;
|
||||
k1 = data32[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
h1 *= m;
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
|
||||
k2 = data32[i]; i += 1;
|
||||
k2 *= m;
|
||||
k2 ~= k2>>r;
|
||||
k2 *= m;
|
||||
h2 *= m;
|
||||
h2 ~= k2;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
if len >= 4 {
|
||||
k1: u32;
|
||||
k1 = data32[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
h1 *= m;
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
data8 := slice_to_bytes(data32[i:])[:3];
|
||||
match len {
|
||||
case 3:
|
||||
h2 ~= cast(u32)data8[2] << 16;
|
||||
fallthrough;
|
||||
case 2:
|
||||
h2 ~= cast(u32)data8[1] << 8;
|
||||
fallthrough;
|
||||
case 1:
|
||||
h2 ~= cast(u32)data8[0];
|
||||
h2 *= m;
|
||||
}
|
||||
|
||||
h1 ~= h2>>18;
|
||||
h1 *= m;
|
||||
h2 ~= h1>>22;
|
||||
h2 *= m;
|
||||
h1 ~= h2>>17;
|
||||
h1 *= m;
|
||||
h2 ~= h1>>19;
|
||||
h2 *= m;
|
||||
|
||||
h := cast(u64)(h1)<<32 | cast(u64)(h2);
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
immutable _crc32_table := [256]u32{
|
||||
@private _crc32_table := [256]u32{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
@@ -268,7 +81,7 @@ immutable _crc32_table := [256]u32{
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
};
|
||||
immutable _crc64_table := [256]u64{
|
||||
@private _crc64_table := [256]u64{
|
||||
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
||||
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
||||
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
|
||||
@@ -0,0 +1,229 @@
|
||||
package hash
|
||||
|
||||
import "core:mem"
|
||||
|
||||
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 {
|
||||
ADLER_CONST :: 65521;
|
||||
a, b: u32 = seed & 0xFFFF, seed >> 16;
|
||||
for x in data {
|
||||
a = (a + u32(x)) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
}
|
||||
return (b << 16) | a;
|
||||
}
|
||||
|
||||
djb2 :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 5381;
|
||||
for b in data {
|
||||
hash = (hash << 5) + hash + u32(b); // hash * 33 + u32(b)
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
fnv32 :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64 :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv32a :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h ~ u32(b)) * 0x01000193;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64a :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h ~ u64(b)) * 0x100000001b3;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
murmur32 :: proc(data: []byte) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51;
|
||||
c2_32: u32 : 0x1b873593;
|
||||
|
||||
h1: u32 = 0;
|
||||
nblocks := len(data)/4;
|
||||
p := raw_data(data);
|
||||
p1 := mem.ptr_offset(p, 4*nblocks);
|
||||
|
||||
for ; p < p1; p = mem.ptr_offset(p, 4) {
|
||||
k1 := (cast(^u32)p)^;
|
||||
|
||||
k1 *= c1_32;
|
||||
k1 = (k1 << 15) | (k1 >> 17);
|
||||
k1 *= c2_32;
|
||||
|
||||
h1 ~= k1;
|
||||
h1 = (h1 << 13) | (h1 >> 19);
|
||||
h1 = h1*5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
tail := data[nblocks*4:];
|
||||
k1: u32;
|
||||
switch len(tail)&3 {
|
||||
case 3:
|
||||
k1 ~= u32(tail[2]) << 16;
|
||||
fallthrough;
|
||||
case 2:
|
||||
k1 ~= u32(tail[2]) << 8;
|
||||
fallthrough;
|
||||
case 1:
|
||||
k1 ~= u32(tail[0]);
|
||||
k1 *= c1_32;
|
||||
k1 = (k1 << 15) | (k1 >> 17) ;
|
||||
k1 *= c2_32;
|
||||
h1 ~= k1;
|
||||
}
|
||||
|
||||
h1 ~= u32(len(data));
|
||||
|
||||
h1 ~= h1 >> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
h1 ~= h1 >> 13;
|
||||
h1 *= 0xc2b2ae35;
|
||||
h1 ~= h1 >> 16;
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
murmur64 :: proc(data: []byte) -> u64 {
|
||||
SEED :: 0x9747b28c;
|
||||
|
||||
when size_of(int) == 8 {
|
||||
m :: 0xc6a4a7935bd1e995;
|
||||
r :: 47;
|
||||
|
||||
h: u64 = SEED ~ (u64(len(data)) * m);
|
||||
data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64));
|
||||
|
||||
for _, i in data64 {
|
||||
k := data64[i];
|
||||
|
||||
k *= m;
|
||||
k ~= k>>r;
|
||||
k *= m;
|
||||
|
||||
h ~= k;
|
||||
h *= m;
|
||||
}
|
||||
|
||||
switch len(data)&7 {
|
||||
case 7: h ~= u64(data[6]) << 48; fallthrough;
|
||||
case 6: h ~= u64(data[5]) << 40; fallthrough;
|
||||
case 5: h ~= u64(data[4]) << 32; fallthrough;
|
||||
case 4: h ~= u64(data[3]) << 24; fallthrough;
|
||||
case 3: h ~= u64(data[2]) << 16; fallthrough;
|
||||
case 2: h ~= u64(data[1]) << 8; fallthrough;
|
||||
case 1:
|
||||
h ~= u64(data[0]);
|
||||
h *= m;
|
||||
}
|
||||
|
||||
h ~= h>>r;
|
||||
h *= m;
|
||||
h ~= h>>r;
|
||||
|
||||
return h;
|
||||
} else {
|
||||
m :: 0x5bd1e995;
|
||||
r :: 24;
|
||||
|
||||
h1 := u32(SEED) ~ u32(len(data));
|
||||
h2 := u32(SEED) >> 32;
|
||||
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32));
|
||||
len := len(data);
|
||||
i := 0;
|
||||
|
||||
for len >= 8 {
|
||||
k1, k2: u32;
|
||||
k1 = data32[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
h1 *= m;
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
|
||||
k2 = data32[i]; i += 1;
|
||||
k2 *= m;
|
||||
k2 ~= k2>>r;
|
||||
k2 *= m;
|
||||
h2 *= m;
|
||||
h2 ~= k2;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
if len >= 4 {
|
||||
k1: u32;
|
||||
k1 = data32[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
h1 *= m;
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
// TODO(bill): Fix this
|
||||
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3];
|
||||
switch len {
|
||||
case 3:
|
||||
h2 ~= u32(data8[2]) << 16;
|
||||
fallthrough;
|
||||
case 2:
|
||||
h2 ~= u32(data8[1]) << 8;
|
||||
fallthrough;
|
||||
case 1:
|
||||
h2 ~= u32(data8[0]);
|
||||
h2 *= m;
|
||||
}
|
||||
|
||||
h1 ~= h2>>18;
|
||||
h1 *= m;
|
||||
h2 ~= h1>>22;
|
||||
h2 *= m;
|
||||
h1 ~= h2>>17;
|
||||
h1 *= m;
|
||||
h2 ~= h1>>19;
|
||||
h2 *= m;
|
||||
|
||||
return u64(h1)<<32 | u64(h2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,163 @@
|
||||
// This is purely for documentation
|
||||
//+ignore
|
||||
package intrinsics
|
||||
|
||||
// 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() -> ! ---
|
||||
|
||||
// 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_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_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) ---
|
||||
|
||||
// Instructions
|
||||
|
||||
alloca :: proc(size, align: int) -> ^u8 ---
|
||||
cpu_relax :: proc() ---
|
||||
read_cycle_counter :: proc() -> i64 ---
|
||||
|
||||
|
||||
// Compiler Hints
|
||||
expect :: proc(val, expected_val: T) -> T ---
|
||||
|
||||
|
||||
// Constant type tests
|
||||
|
||||
type_base_type :: proc($T: typeid) -> type ---
|
||||
type_core_type :: proc($T: typeid) -> type ---
|
||||
type_elem_type :: proc($T: typeid) -> type ---
|
||||
|
||||
type_is_boolean :: proc($T: typeid) -> bool ---
|
||||
type_is_integer :: proc($T: typeid) -> bool ---
|
||||
type_is_rune :: proc($T: typeid) -> bool ---
|
||||
type_is_float :: proc($T: typeid) -> bool ---
|
||||
type_is_complex :: proc($T: typeid) -> bool ---
|
||||
type_is_quaternion :: proc($T: typeid) -> bool ---
|
||||
type_is_string :: proc($T: typeid) -> bool ---
|
||||
type_is_typeid :: proc($T: typeid) -> bool ---
|
||||
type_is_any :: 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 ---
|
||||
type_is_numeric :: proc($T: typeid) -> bool ---
|
||||
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_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_array :: proc($T: typeid) -> bool ---
|
||||
type_is_enumerated_array :: proc($T: typeid) -> bool ---
|
||||
type_is_slice :: proc($T: typeid) -> bool ---
|
||||
type_is_dynamic_array :: proc($T: typeid) -> bool ---
|
||||
type_is_map :: proc($T: typeid) -> bool ---
|
||||
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_set :: proc($T: typeid) -> bool ---
|
||||
type_is_simd_vector :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_has_nil :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
||||
|
||||
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) ---
|
||||
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) ---
|
||||
@@ -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;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
package log
|
||||
|
||||
import "core:fmt";
|
||||
import "core:strings";
|
||||
import "core:os";
|
||||
import "core:time";
|
||||
|
||||
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,
|
||||
} | Full_Timestamp_Opts;
|
||||
|
||||
Default_File_Logger_Opts :: Options{
|
||||
.Level,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
} | Full_Timestamp_Opts;
|
||||
|
||||
|
||||
File_Console_Logger_Data :: struct {
|
||||
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.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 {
|
||||
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.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);
|
||||
}
|
||||
|
||||
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;
|
||||
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[:]);
|
||||
|
||||
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, "] ");
|
||||
}
|
||||
}
|
||||
|
||||
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 != "" {
|
||||
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) {
|
||||
|
||||
RESET :: "\x1b[0m";
|
||||
RED :: "\x1b[31m";
|
||||
YELLOW :: "\x1b[33m";
|
||||
DARK_GREY :: "\x1b[90m";
|
||||
|
||||
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 {
|
||||
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 {
|
||||
return;
|
||||
}
|
||||
fmt.sbprint(buf, "[");
|
||||
|
||||
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 {
|
||||
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 | {.Line}) & opts != nil {
|
||||
fmt.sbprint(buf, ":");
|
||||
}
|
||||
fmt.sbprintf(buf, "%s()", location.procedure);
|
||||
}
|
||||
|
||||
fmt.sbprint(buf, "] ");
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package log
|
||||
|
||||
import "core:runtime"
|
||||
import "core:fmt"
|
||||
|
||||
|
||||
// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle.
|
||||
|
||||
Level :: runtime.Logger_Level;
|
||||
/*
|
||||
Logger_Level :: enum {
|
||||
Debug = 0,
|
||||
Info = 10,
|
||||
Warning = 20,
|
||||
Error = 30,
|
||||
Fatal = 40,
|
||||
}
|
||||
*/
|
||||
|
||||
Option :: runtime.Logger_Option;
|
||||
/*
|
||||
Option :: enum {
|
||||
Level,
|
||||
Date,
|
||||
Time,
|
||||
Short_File_Path,
|
||||
Long_File_Path,
|
||||
Line,
|
||||
Procedure,
|
||||
Terminal_Color
|
||||
}
|
||||
*/
|
||||
|
||||
Options :: runtime.Logger_Options;
|
||||
/*
|
||||
Options :: bit_set[Option];
|
||||
*/
|
||||
|
||||
Full_Timestamp_Opts :: Options{
|
||||
.Date,
|
||||
.Time,
|
||||
};
|
||||
Location_Header_Opts :: Options{
|
||||
.Short_File_Path,
|
||||
.Long_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
};
|
||||
Location_File_Opts :: Options{
|
||||
.Short_File_Path,
|
||||
.Long_File_Path,
|
||||
};
|
||||
|
||||
|
||||
Logger_Proc :: runtime.Logger_Proc;
|
||||
/*
|
||||
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
|
||||
*/
|
||||
|
||||
Logger :: runtime.Logger;
|
||||
/*
|
||||
Logger :: struct {
|
||||
procedure: Logger_Proc,
|
||||
data: rawptr,
|
||||
lowest_level: Level,
|
||||
options: Logger_Options,
|
||||
}
|
||||
*/
|
||||
|
||||
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, Level.Debug, nil};
|
||||
}
|
||||
|
||||
// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
|
||||
debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Debug, fmt_str=fmt_str, args=args, location=location);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
-372
@@ -1,372 +0,0 @@
|
||||
TAU :: 6.28318530717958647692528676655900576;
|
||||
PI :: 3.14159265358979323846264338327950288;
|
||||
ONE_OVER_TAU :: 0.636619772367581343075535053490057448;
|
||||
ONE_OVER_PI :: 0.159154943091895335768883763372514362;
|
||||
|
||||
E :: 2.71828182845904523536;
|
||||
SQRT_TWO :: 1.41421356237309504880168872420969808;
|
||||
SQRT_THREE :: 1.73205080756887729352744634150587236;
|
||||
SQRT_FIVE :: 2.23606797749978969640917366873127623;
|
||||
|
||||
LOG_TWO :: 0.693147180559945309417232121458176568;
|
||||
LOG_TEN :: 2.30258509299404568401799145468436421;
|
||||
|
||||
EPSILON :: 1.19209290e-7;
|
||||
|
||||
τ :: TAU;
|
||||
π :: PI;
|
||||
|
||||
Vec2 :: [vector 2]f32;
|
||||
Vec3 :: [vector 3]f32;
|
||||
Vec4 :: [vector 4]f32;
|
||||
|
||||
Mat2 :: [2]Vec2;
|
||||
Mat3 :: [3]Vec3;
|
||||
Mat4 :: [4]Vec4;
|
||||
|
||||
sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
|
||||
sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
|
||||
|
||||
sin :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
|
||||
sin :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
|
||||
|
||||
cos :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
|
||||
cos :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
|
||||
|
||||
tan :: proc(x: f32) -> f32 #inline { return sin(x)/cos(x); }
|
||||
tan :: proc(x: f64) -> f64 #inline { return sin(x)/cos(x); }
|
||||
|
||||
lerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; }
|
||||
lerp :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t; }
|
||||
|
||||
sign :: proc(x: f32) -> f32 { if x >= 0 { return +1; } return -1; }
|
||||
sign :: proc(x: f64) -> f64 { if x >= 0 { return +1; } return -1; }
|
||||
|
||||
bit_reverse :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bitreverse.i16";
|
||||
bit_reverse :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bitreverse.i32";
|
||||
bit_reverse :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bitreverse.i64";
|
||||
|
||||
fmuladd :: proc(a, b, c: f32) -> f32 #foreign __llvm_core "llvm.fmuladd.f32";
|
||||
fmuladd :: proc(a, b, c: f64) -> f64 #foreign __llvm_core "llvm.fmuladd.f64";
|
||||
|
||||
|
||||
copy_sign :: proc(x, y: f32) -> f32 {
|
||||
ix := transmute(u32)x;
|
||||
iy := transmute(u32)y;
|
||||
ix &= 0x7fffffff;
|
||||
ix |= iy & 0x80000000;
|
||||
return transmute(f32)ix;
|
||||
}
|
||||
round :: proc(x: f32) -> f32 {
|
||||
if x >= 0 {
|
||||
return floor(x + 0.5);
|
||||
}
|
||||
return ceil(x - 0.5);
|
||||
}
|
||||
floor :: proc(x: f32) -> f32 {
|
||||
if x >= 0 {
|
||||
return cast(f32)cast(int)x;
|
||||
}
|
||||
return cast(f32)cast(int)(x-0.5);
|
||||
}
|
||||
ceil :: proc(x: f32) -> f32 {
|
||||
if x < 0 {
|
||||
return cast(f32)cast(int)x;
|
||||
}
|
||||
return cast(f32)cast(int)(x+1);
|
||||
}
|
||||
|
||||
remainder32 :: proc(x, y: f32) -> f32 {
|
||||
return x - round(x/y) * y;
|
||||
}
|
||||
|
||||
fmod32 :: proc(x, y: f32) -> f32 {
|
||||
y = abs(y);
|
||||
result := remainder32(abs(x), y);
|
||||
if sign(result) < 0 {
|
||||
result += y;
|
||||
}
|
||||
return copy_sign(result, x);
|
||||
}
|
||||
|
||||
|
||||
to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; }
|
||||
to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; }
|
||||
|
||||
|
||||
|
||||
|
||||
dot :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y; }
|
||||
dot :: proc(a, b: Vec3) -> f32 { c := a*b; return c.x + c.y + c.z; }
|
||||
dot :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w; }
|
||||
|
||||
cross :: proc(x, y: Vec3) -> Vec3 {
|
||||
a := swizzle(x, 1, 2, 0) * swizzle(y, 2, 0, 1);
|
||||
b := swizzle(x, 2, 0, 1) * swizzle(y, 1, 2, 0);
|
||||
return a - b;
|
||||
}
|
||||
|
||||
|
||||
mag :: proc(v: Vec2) -> f32 { return sqrt(dot(v, v)); }
|
||||
mag :: proc(v: Vec3) -> f32 { return sqrt(dot(v, v)); }
|
||||
mag :: proc(v: Vec4) -> f32 { return sqrt(dot(v, v)); }
|
||||
|
||||
norm :: proc(v: Vec2) -> Vec2 { return v / Vec2{mag(v)}; }
|
||||
norm :: proc(v: Vec3) -> Vec3 { return v / Vec3{mag(v)}; }
|
||||
norm :: proc(v: Vec4) -> Vec4 { return v / Vec4{mag(v)}; }
|
||||
|
||||
norm0 :: proc(v: Vec2) -> Vec2 {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return Vec2{0};
|
||||
}
|
||||
return v / Vec2{m};
|
||||
}
|
||||
|
||||
norm0 :: proc(v: Vec3) -> Vec3 {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return Vec3{0};
|
||||
}
|
||||
return v / Vec3{m};
|
||||
}
|
||||
|
||||
norm0 :: proc(v: Vec4) -> Vec4 {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return Vec4{0};
|
||||
}
|
||||
return v / Vec4{m};
|
||||
}
|
||||
|
||||
|
||||
|
||||
mat4_identity :: proc() -> Mat4 {
|
||||
return Mat4{
|
||||
{1, 0, 0, 0},
|
||||
{0, 1, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 0, 1},
|
||||
};
|
||||
}
|
||||
|
||||
mat4_transpose :: proc(m: Mat4) -> Mat4 {
|
||||
for j in 0..<4 {
|
||||
for i in 0..<4 {
|
||||
m[i][j], m[j][i] = m[j][i], m[i][j];
|
||||
}
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
mul :: proc(a, b: Mat4) -> Mat4 {
|
||||
c: Mat4;
|
||||
for j in 0..<4 {
|
||||
for i in 0..<4 {
|
||||
c[j][i] = a[0][i]*b[j][0] +
|
||||
a[1][i]*b[j][1] +
|
||||
a[2][i]*b[j][2] +
|
||||
a[3][i]*b[j][3];
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
mul :: proc(m: Mat4, v: Vec4) -> Vec4 {
|
||||
return Vec4{
|
||||
m[0][0]*v.x + m[1][0]*v.y + m[2][0]*v.z + m[3][0]*v.w,
|
||||
m[0][1]*v.x + m[1][1]*v.y + m[2][1]*v.z + m[3][1]*v.w,
|
||||
m[0][2]*v.x + m[1][2]*v.y + m[2][2]*v.z + m[3][2]*v.w,
|
||||
m[0][3]*v.x + m[1][3]*v.y + m[2][3]*v.z + m[3][3]*v.w,
|
||||
};
|
||||
}
|
||||
|
||||
inverse :: proc(m: Mat4) -> Mat4 {
|
||||
o: Mat4;
|
||||
|
||||
sf00 := m[2][2] * m[3][3] - m[3][2] * m[2][3];
|
||||
sf01 := m[2][1] * m[3][3] - m[3][1] * m[2][3];
|
||||
sf02 := m[2][1] * m[3][2] - m[3][1] * m[2][2];
|
||||
sf03 := m[2][0] * m[3][3] - m[3][0] * m[2][3];
|
||||
sf04 := m[2][0] * m[3][2] - m[3][0] * m[2][2];
|
||||
sf05 := m[2][0] * m[3][1] - m[3][0] * m[2][1];
|
||||
sf06 := m[1][2] * m[3][3] - m[3][2] * m[1][3];
|
||||
sf07 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
|
||||
sf08 := m[1][1] * m[3][2] - m[3][1] * m[1][2];
|
||||
sf09 := m[1][0] * m[3][3] - m[3][0] * m[1][3];
|
||||
sf10 := m[1][0] * m[3][2] - m[3][0] * m[1][2];
|
||||
sf11 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
|
||||
sf12 := m[1][0] * m[3][1] - m[3][0] * m[1][1];
|
||||
sf13 := m[1][2] * m[2][3] - m[2][2] * m[1][3];
|
||||
sf14 := m[1][1] * m[2][3] - m[2][1] * m[1][3];
|
||||
sf15 := m[1][1] * m[2][2] - m[2][1] * m[1][2];
|
||||
sf16 := m[1][0] * m[2][3] - m[2][0] * m[1][3];
|
||||
sf17 := m[1][0] * m[2][2] - m[2][0] * m[1][2];
|
||||
sf18 := m[1][0] * m[2][1] - m[2][0] * m[1][1];
|
||||
|
||||
o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02);
|
||||
o[0][1] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04);
|
||||
o[0][2] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05);
|
||||
o[0][3] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05);
|
||||
|
||||
o[1][0] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02);
|
||||
o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04);
|
||||
o[1][2] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05);
|
||||
o[1][3] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05);
|
||||
|
||||
o[2][0] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08);
|
||||
o[2][1] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10);
|
||||
o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12);
|
||||
o[2][3] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12);
|
||||
|
||||
o[3][0] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15);
|
||||
o[3][1] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17);
|
||||
o[3][2] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18);
|
||||
o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18);
|
||||
|
||||
ood := 1.0 / (m[0][0] * o[0][0] +
|
||||
m[0][1] * o[0][1] +
|
||||
m[0][2] * o[0][2] +
|
||||
m[0][3] * o[0][3]);
|
||||
|
||||
o[0][0] *= ood;
|
||||
o[0][1] *= ood;
|
||||
o[0][2] *= ood;
|
||||
o[0][3] *= ood;
|
||||
o[1][0] *= ood;
|
||||
o[1][1] *= ood;
|
||||
o[1][2] *= ood;
|
||||
o[1][3] *= ood;
|
||||
o[2][0] *= ood;
|
||||
o[2][1] *= ood;
|
||||
o[2][2] *= ood;
|
||||
o[2][3] *= ood;
|
||||
o[3][0] *= ood;
|
||||
o[3][1] *= ood;
|
||||
o[3][2] *= ood;
|
||||
o[3][3] *= ood;
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
mat4_translate :: proc(v: Vec3) -> Mat4 {
|
||||
m := mat4_identity();
|
||||
m[3][0] = v.x;
|
||||
m[3][1] = v.y;
|
||||
m[3][2] = v.z;
|
||||
m[3][3] = 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
|
||||
c := cos(angle_radians);
|
||||
s := sin(angle_radians);
|
||||
|
||||
a := norm(v);
|
||||
t := a * Vec3{1-c};
|
||||
|
||||
rot := mat4_identity();
|
||||
|
||||
rot[0][0] = c + t.x*a.x;
|
||||
rot[0][1] = 0 + t.x*a.y + s*a.z;
|
||||
rot[0][2] = 0 + t.x*a.z - s*a.y;
|
||||
rot[0][3] = 0;
|
||||
|
||||
rot[1][0] = 0 + t.y*a.x - s*a.z;
|
||||
rot[1][1] = c + t.y*a.y;
|
||||
rot[1][2] = 0 + t.y*a.z + s*a.x;
|
||||
rot[1][3] = 0;
|
||||
|
||||
rot[2][0] = 0 + t.z*a.x + s*a.y;
|
||||
rot[2][1] = 0 + t.z*a.y - s*a.x;
|
||||
rot[2][2] = c + t.z*a.z;
|
||||
rot[2][3] = 0;
|
||||
|
||||
return rot;
|
||||
}
|
||||
|
||||
scale :: proc(m: Mat4, v: Vec3) -> Mat4 {
|
||||
m[0][0] *= v.x;
|
||||
m[1][1] *= v.y;
|
||||
m[2][2] *= v.z;
|
||||
return m;
|
||||
}
|
||||
|
||||
scale :: proc(m: Mat4, s: f32) -> Mat4 {
|
||||
m[0][0] *= s;
|
||||
m[1][1] *= s;
|
||||
m[2][2] *= s;
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
|
||||
f := norm(centre - eye);
|
||||
s := norm(cross(f, up));
|
||||
u := cross(s, f);
|
||||
|
||||
m: Mat4;
|
||||
|
||||
m[0] = Vec4{+s.x, +s.y, +s.z, 0};
|
||||
m[1] = Vec4{+u.x, +u.y, +u.z, 0};
|
||||
m[2] = Vec4{-f.x, -f.y, -f.z, 0};
|
||||
m[3] = Vec4{dot(s, eye), dot(u, eye), dot(f, eye), 1};
|
||||
|
||||
return m;
|
||||
}
|
||||
perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
|
||||
m: Mat4;
|
||||
tan_half_fovy := tan(0.5 * fovy);
|
||||
m[0][0] = 1.0 / (aspect*tan_half_fovy);
|
||||
m[1][1] = 1.0 / (tan_half_fovy);
|
||||
m[2][2] = -(far + near) / (far - near);
|
||||
m[2][3] = -1.0;
|
||||
m[3][2] = -2.0*far*near / (far - near);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 {
|
||||
m := mat4_identity();
|
||||
m[0][0] = +2.0 / (right - left);
|
||||
m[1][1] = +2.0 / (top - bottom);
|
||||
m[2][2] = -2.0 / (far - near);
|
||||
m[3][0] = -(right + left) / (right - left);
|
||||
m[3][1] = -(top + bottom) / (top - bottom);
|
||||
m[3][2] = -(far + near) / (far - near);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
F32_DIG :: 6;
|
||||
F32_EPSILON :: 1.192092896e-07;
|
||||
F32_GUARD :: 0;
|
||||
F32_MANT_DIG :: 24;
|
||||
F32_MAX :: 3.402823466e+38;
|
||||
F32_MAX_10_EXP :: 38;
|
||||
F32_MAX_EXP :: 128;
|
||||
F32_MIN :: 1.175494351e-38;
|
||||
F32_MIN_10_EXP :: -37;
|
||||
F32_MIN_EXP :: -125;
|
||||
F32_NORMALIZE :: 0;
|
||||
F32_RADIX :: 2;
|
||||
F32_ROUNDS :: 1;
|
||||
|
||||
F64_DIG :: 15; // # of decimal digits of precision
|
||||
F64_EPSILON :: 2.2204460492503131e-016; // smallest such that 1.0+F64_EPSILON != 1.0
|
||||
F64_MANT_DIG :: 53; // # of bits in mantissa
|
||||
F64_MAX :: 1.7976931348623158e+308; // max value
|
||||
F64_MAX_10_EXP :: 308; // max decimal exponent
|
||||
F64_MAX_EXP :: 1024; // max binary exponent
|
||||
F64_MIN :: 2.2250738585072014e-308; // min positive value
|
||||
F64_MIN_10_EXP :: -307; // min decimal exponent
|
||||
F64_MIN_EXP :: -1021; // min binary exponent
|
||||
F64_RADIX :: 2; // exponent radix
|
||||
F64_ROUNDS :: 1; // addition rounding: near
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,656 @@
|
||||
package math_bits
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
U8_MIN :: 0;
|
||||
U16_MIN :: 0;
|
||||
U32_MIN :: 0;
|
||||
U64_MIN :: 0;
|
||||
|
||||
U8_MAX :: 1 << 8 - 1;
|
||||
U16_MAX :: 1 << 16 - 1;
|
||||
U32_MAX :: 1 << 32 - 1;
|
||||
U64_MAX :: 1 << 64 - 1;
|
||||
|
||||
I8_MIN :: - 1 << 7;
|
||||
I16_MIN :: - 1 << 15;
|
||||
I32_MIN :: - 1 << 31;
|
||||
I64_MIN :: - 1 << 63;
|
||||
|
||||
I8_MAX :: 1 << 7 - 1;
|
||||
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.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 ---
|
||||
|
||||
@(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 ---
|
||||
}
|
||||
|
||||
|
||||
trailing_zeros_uint :: proc(i: uint) -> uint {
|
||||
when size_of(uint) == size_of(u64) {
|
||||
return uint(trailing_zeros64(u64(i)));
|
||||
} else {
|
||||
return uint(trailing_zeros32(u32(i)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
leading_zeros_u8 :: proc(i: u8) -> int {
|
||||
return 8*size_of(i) - len_u8(i);
|
||||
}
|
||||
leading_zeros_u16 :: proc(i: u16) -> int {
|
||||
return 8*size_of(i) - len_u16(i);
|
||||
}
|
||||
leading_zeros_u32 :: proc(i: u32) -> int {
|
||||
return 8*size_of(i) - len_u32(i);
|
||||
}
|
||||
leading_zeros_u64 :: proc(i: u64) -> int {
|
||||
return 8*size_of(i) - len_u64(i);
|
||||
}
|
||||
|
||||
|
||||
byte_swap_u16 :: proc(x: u16) -> u16 {
|
||||
return runtime.bswap_16(x);
|
||||
}
|
||||
byte_swap_u32 :: proc(x: u32) -> u32 {
|
||||
return runtime.bswap_32(x);
|
||||
}
|
||||
byte_swap_u64 :: proc(x: u64) -> u64 {
|
||||
return runtime.bswap_64(x);
|
||||
}
|
||||
byte_swap_i16 :: proc(x: i16) -> i16 {
|
||||
return i16(runtime.bswap_16(u16(x)));
|
||||
}
|
||||
byte_swap_i32 :: proc(x: i32) -> i32 {
|
||||
return i32(runtime.bswap_32(u32(x)));
|
||||
}
|
||||
byte_swap_i64 :: proc(x: i64) -> i64 {
|
||||
return i64(runtime.bswap_64(u64(x)));
|
||||
}
|
||||
byte_swap_u128 :: proc(x: u128) -> u128 {
|
||||
return runtime.bswap_128(x);
|
||||
}
|
||||
byte_swap_i128 :: proc(x: i128) -> i128 {
|
||||
return i128(runtime.bswap_128(u128(x)));
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
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(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);
|
||||
}
|
||||
|
||||
rotate_left :: proc(x: uint, k: int) -> uint {
|
||||
n :: 8*size_of(uint);
|
||||
s := uint(k) & (n-1);
|
||||
return x <<s | x>>(n-s);
|
||||
}
|
||||
|
||||
from_be_u8 :: proc(i: u8) -> u8 { return 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 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 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 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) ---
|
||||
}
|
||||
|
||||
overflowing_add_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_add_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
@(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) ---
|
||||
}
|
||||
overflowing_sub_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_sub_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
a, b := mul_u32(u32(x), u32(y));
|
||||
} else {
|
||||
#assert(size_of(uint) == size_of(u64));
|
||||
a, b := mul_u64(u64(x), u64(y));
|
||||
}
|
||||
return uint(a), uint(b);
|
||||
}
|
||||
|
||||
mul :: proc{mul_u32, mul_u64, mul_uint};
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
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(leading_zeros_u64(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) {
|
||||
a, b := div_u32(u32(hi), u32(lo), u32(y));
|
||||
} else {
|
||||
#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};
|
||||
|
||||
|
||||
|
||||
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; }
|
||||
is_power_of_two_u16 :: proc(i: u16) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i16 :: proc(i: i16) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_u32 :: proc(i: u32) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i32 :: proc(i: i32) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_u64 :: proc(i: u64) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_i64 :: proc(i: i64) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_uint :: proc(i: uint) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two_int :: proc(i: int) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
|
||||
is_power_of_two :: proc{
|
||||
is_power_of_two_u8, is_power_of_two_i8,
|
||||
is_power_of_two_u16, is_power_of_two_i16,
|
||||
is_power_of_two_u32, is_power_of_two_i32,
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
package linalg
|
||||
|
||||
import "core:math"
|
||||
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;
|
||||
}
|
||||
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{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];
|
||||
}
|
||||
|
||||
vector_cross3 :: proc(a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) {
|
||||
c[0] = a[1]*b[2] - b[1]*a[2];
|
||||
c[1] = a[2]*b[0] - b[2]*a[0];
|
||||
c[2] = a[0]*b[1] - b[0]*a[1];
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return q/abs(q);
|
||||
}
|
||||
normalize :: proc{vector_normalize, quaternion_normalize};
|
||||
|
||||
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
m := length(v);
|
||||
return 0 if m == 0 else v/m;
|
||||
}
|
||||
quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
m := abs(q);
|
||||
return 0 if m == 0 else q/m;
|
||||
}
|
||||
normalize0 :: proc{vector_normalize0, quaternion_normalize0};
|
||||
|
||||
|
||||
vector_length :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
return math.sqrt(dot(v, v));
|
||||
}
|
||||
|
||||
vector_length2 :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
return dot(v, v);
|
||||
}
|
||||
|
||||
quaternion_length :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return abs(q);
|
||||
}
|
||||
|
||||
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};
|
||||
|
||||
projection :: proc(x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
return dot(x, normal) / dot(normal, normal) * normal;
|
||||
}
|
||||
|
||||
identity :: proc($T: typeid/[$N][N]$E) -> (m: T) {
|
||||
for i in 0..<N {
|
||||
m[i][i] = E(1);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
trace :: proc(m: $T/[$N][N]$E) -> (tr: E) {
|
||||
for i in 0..<N {
|
||||
tr += m[i][i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
transpose :: proc(a: $T/[$N][$M]$E) -> (m: T) {
|
||||
for j in 0..<M {
|
||||
for i in 0..<N {
|
||||
m[j][i] = a[i][j];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E) {
|
||||
for i in 0..<N {
|
||||
for k in 0..<N {
|
||||
for j in 0..<N {
|
||||
c[k][i] += a[j][i] * b[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
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),
|
||||
I != K {
|
||||
for k in 0..<K {
|
||||
for j in 0..<J {
|
||||
for i in 0..<I {
|
||||
c[k][i] += a[j][i] * b[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E) {
|
||||
for i in 0..<I {
|
||||
for j in 0..<J {
|
||||
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};
|
||||
|
||||
q := transmute(Raw_Quaternion)q;
|
||||
v := transmute([3]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};
|
||||
|
||||
q := transmute(Raw_Quaternion)q;
|
||||
v := transmute([3]f64)v;
|
||||
|
||||
t := cross(2*q.xyz, v);
|
||||
return V(v + q.r*t + cross(q.xyz, t));
|
||||
}
|
||||
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), N > 0 #no_bounds_check {
|
||||
return &v[0];
|
||||
}
|
||||
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); }
|
||||
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,
|
||||
};
|
||||
+1027
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user