mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 03:01:38 -07:00
Compare commits
751 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 |
@@ -7,26 +7,31 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM
|
||||
run: sudo apt-get install 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
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm
|
||||
echo ::add-path::/usr/local/opt/llvm/bin
|
||||
echo ::set-env name=CPATH::`xcrun --show-sdk-path`/usr/include
|
||||
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
|
||||
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:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
name: Nightly
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: 0 20 * * *
|
||||
|
||||
@@ -48,9 +49,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: (Linux) Download LLVM
|
||||
run: sudo apt-get install llvm
|
||||
run: sudo apt-get install llvm-11 clang-11 llvm
|
||||
- name: build odin
|
||||
run: make release
|
||||
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
|
||||
@@ -72,10 +75,13 @@ jobs:
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm
|
||||
echo ::add-path::/usr/local/opt/llvm/bin
|
||||
echo ::set-env name=CPATH::`xcrun --show-sdk-path`/usr/include
|
||||
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
|
||||
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
|
||||
@@ -95,21 +101,29 @@ jobs:
|
||||
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: sudo pip install --upgrade b2
|
||||
|
||||
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:
|
||||
|
||||
@@ -21,6 +21,8 @@ bld/
|
||||
![Cc]ore/[Ll]og/
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Visual Studio Code options directory
|
||||
.vscode/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
demo
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
GIT_SHA=$(shell git rev-parse --short HEAD)
|
||||
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined
|
||||
LDFLAGS=-pthread -ldl -lm -lstdc++
|
||||
CFLAGS=-std=c++11 -DGIT_SHA=\"$(GIT_SHA)\"
|
||||
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
|
||||
@@ -22,7 +28,7 @@ release:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
|
||||
|
||||
nightly:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 -march=native $(LDFLAGS) -o odin
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ The proposal process is the process for reviewing a proposal and reaching a deci
|
||||
* Accept proposal
|
||||
* Decline proposal
|
||||
|
||||
After the proposal is accepted or declined, implementation of the proprosal proceeds in the same way as any other contribution to the project.
|
||||
After the proposal is accepted or declined, implementation of the proposal proceeds in the same way as any other contribution to the project.
|
||||
|
||||
## Design Documents
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ main :: proc() {
|
||||
|
||||
#### [Getting Started](https://odin-lang.org/docs/install)
|
||||
|
||||
Instructions for downloading and install the Odin compiler and libraries.
|
||||
Instructions for downloading and installing the Odin compiler and libraries.
|
||||
|
||||
### Learning Odin
|
||||
|
||||
@@ -94,7 +94,7 @@ The official blog of the Odin programming language, featuring announcements, new
|
||||
|
||||
## Setup
|
||||
|
||||
Odin only supports x86-64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
Odin only supports x86-64/amd64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
|
||||
In addition, the following platform-specific steps are necessary:
|
||||
|
||||
@@ -126,27 +126,34 @@ Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#g
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin).
|
||||
|
||||
- Windows
|
||||
* x86-64
|
||||
* x86-64/amd64
|
||||
* MSVC 2010 installed (C++11 support)
|
||||
* [LLVM binaries](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) for `opt.exe`, `llc.exe`, and `lld-link.exe`
|
||||
* Requires MSVC's link.exe as the linker
|
||||
* run `vcvarsall.bat` to setup the path
|
||||
|
||||
- MacOS
|
||||
* x86-64
|
||||
* x86-64/amd64
|
||||
* LLVM explicitly installed (`brew install llvm`)
|
||||
* XCode installed (for the linker)
|
||||
|
||||
- GNU/Linux
|
||||
* x86-64
|
||||
* x86-64/amd64
|
||||
* Build tools (ld)
|
||||
* LLVM installed
|
||||
* Clang installed (temporary - this is Calling the linker for now)
|
||||
|
||||
- FreeBSD
|
||||
* x86-64/amd64
|
||||
* Build tools (ld)
|
||||
* LLVM installed
|
||||
* Clang installed (temporary - this is Calling the linker for now)
|
||||
|
||||
Other platforms may be supported but are experimental for the time being.
|
||||
|
||||
## Warnings
|
||||
|
||||
* This is still highly in development and the language's design is quite volatile.
|
||||
* Syntax is not fixed.
|
||||
* The Odin compiler is still in development.
|
||||
|
||||
## Demonstrations:
|
||||
* First Talk & Demo
|
||||
|
||||
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
|
||||
@@ -20,7 +20,7 @@ if "%2" == "1" (
|
||||
)
|
||||
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||
set compiler_defines= -DLLVM_BACKEND_SUPPORT
|
||||
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%\"
|
||||
|
||||
@@ -23,6 +23,8 @@ if [[ "$(uname)" == "Darwin" ]]; then
|
||||
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,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);
|
||||
},
|
||||
};
|
||||
@@ -43,6 +43,7 @@ complex32 :: complex32;
|
||||
complex64 :: complex64;
|
||||
complex128 :: complex128;
|
||||
|
||||
quaternion64 :: quaternion64;
|
||||
quaternion128 :: quaternion128;
|
||||
quaternion256 :: quaternion256;
|
||||
|
||||
@@ -76,6 +77,17 @@ u64be :: u64be;
|
||||
i128be :: i128be;
|
||||
u128be :: u128be;
|
||||
|
||||
|
||||
f16le :: f16le;
|
||||
f32le :: f32le;
|
||||
f64le :: f64le;
|
||||
|
||||
f16be :: f16be;
|
||||
f32be :: f32be;
|
||||
f64be :: f64be;
|
||||
|
||||
|
||||
|
||||
// Procedures
|
||||
len :: proc(array: Array_Type) -> int ---
|
||||
cap :: proc(array: Array_Type) -> int ---
|
||||
@@ -103,3 +115,6 @@ min :: proc(values: ..T) -> T ---
|
||||
max :: proc(values: ..T) -> T ---
|
||||
abs :: proc(value: T) -> T ---
|
||||
clamp :: proc(value, minimum, maximum: T) -> T ---
|
||||
|
||||
soa_zip :: proc(slices: ...) -> #soa[]Struct ---
|
||||
soa_unzip :: proc(value: $S/#soa[]$E) -> (slices: ...) ---
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
package bytes
|
||||
|
||||
import "core:io"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
MIN_READ :: 512;
|
||||
|
||||
@(private)
|
||||
SMALL_BUFFER_SIZE :: 64;
|
||||
|
||||
// A Buffer is a variable-sized buffer of bytes with a io.Stream interface
|
||||
// The zero value for Buffer is an empty buffer ready to use.
|
||||
Buffer :: struct {
|
||||
buf: [dynamic]byte,
|
||||
off: int,
|
||||
last_read: Read_Op,
|
||||
}
|
||||
|
||||
@(private)
|
||||
Read_Op :: enum i8 {
|
||||
Read = -1,
|
||||
Invalid = 0,
|
||||
Read_Rune1 = 1,
|
||||
Read_Rune2 = 2,
|
||||
Read_Rune3 = 3,
|
||||
Read_Rune4 = 4,
|
||||
}
|
||||
|
||||
|
||||
buffer_init :: proc(b: ^Buffer, buf: []byte) {
|
||||
resize(&b.buf, len(buf));
|
||||
copy(b.buf[:], buf);
|
||||
}
|
||||
|
||||
buffer_init_string :: proc(b: ^Buffer, s: string) {
|
||||
resize(&b.buf, len(s));
|
||||
copy(b.buf[:], s);
|
||||
}
|
||||
|
||||
buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) {
|
||||
b.buf.allocator = allocator;
|
||||
reserve(&b.buf, cap);
|
||||
resize(&b.buf, len);
|
||||
}
|
||||
|
||||
buffer_destroy :: proc(b: ^Buffer) {
|
||||
delete(b.buf);
|
||||
buffer_reset(b);
|
||||
}
|
||||
|
||||
buffer_to_bytes :: proc(b: ^Buffer) -> []byte {
|
||||
return b.buf[b.off:];
|
||||
}
|
||||
|
||||
buffer_to_string :: proc(b: ^Buffer) -> string {
|
||||
if b == nil {
|
||||
return "<nil>";
|
||||
}
|
||||
return string(b.buf[b.off:]);
|
||||
}
|
||||
|
||||
buffer_is_empty :: proc(b: ^Buffer) -> bool {
|
||||
return len(b.buf) <= b.off;
|
||||
}
|
||||
|
||||
buffer_length :: proc(b: ^Buffer) -> int {
|
||||
return len(b.buf) - b.off;
|
||||
}
|
||||
|
||||
buffer_capacity :: proc(b: ^Buffer) -> int {
|
||||
return cap(b.buf);
|
||||
}
|
||||
|
||||
buffer_reset :: proc(b: ^Buffer) {
|
||||
clear(&b.buf);
|
||||
b.off = 0;
|
||||
b.last_read = .Invalid;
|
||||
}
|
||||
|
||||
|
||||
buffer_truncate :: proc(b: ^Buffer, n: int) {
|
||||
if n == 0 {
|
||||
buffer_reset(b);
|
||||
return;
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
if n < 0 || n > buffer_length(b) {
|
||||
panic("bytes.truncate: truncation out of range");
|
||||
}
|
||||
resize(&b.buf, b.off+n);
|
||||
}
|
||||
|
||||
@(private)
|
||||
_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) {
|
||||
if l := len(b.buf); n <= cap(b.buf)-l {
|
||||
resize(&b.buf, l+n);
|
||||
return l, true;
|
||||
}
|
||||
return 0, false;
|
||||
}
|
||||
|
||||
@(private)
|
||||
_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
|
||||
m := buffer_length(b);
|
||||
if m == 0 && b.off != 0 {
|
||||
buffer_reset(b);
|
||||
}
|
||||
if i, ok := _buffer_try_grow(b, n); ok {
|
||||
return i;
|
||||
}
|
||||
if b.buf == nil && n <= SMALL_BUFFER_SIZE {
|
||||
b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
c := cap(b.buf);
|
||||
if n <= c/2 - m {
|
||||
copy(b.buf[:], b.buf[b.off:]);
|
||||
} else if c > max(int) - c - n {
|
||||
panic("bytes.Buffer: too large");
|
||||
} else {
|
||||
resize(&b.buf, 2*c + n);
|
||||
copy(b.buf[:], b.buf[b.off:]);
|
||||
}
|
||||
b.off = 0;
|
||||
resize(&b.buf, m+n);
|
||||
return m;
|
||||
}
|
||||
|
||||
buffer_grow :: proc(b: ^Buffer, n: int) {
|
||||
if n < 0 {
|
||||
panic("bytes.buffer_grow: negative count");
|
||||
}
|
||||
m := _buffer_grow(b, n);
|
||||
resize(&b.buf, m);
|
||||
}
|
||||
|
||||
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
if offset < 0 {
|
||||
err = .Invalid_Offset;
|
||||
return;
|
||||
}
|
||||
_, ok := _buffer_try_grow(b, offset+len(p));
|
||||
if !ok {
|
||||
_ = _buffer_grow(b, offset+len(p));
|
||||
}
|
||||
if len(b.buf) <= offset {
|
||||
return 0, .Short_Write;
|
||||
}
|
||||
return copy(b.buf[offset:], p), nil;
|
||||
}
|
||||
|
||||
|
||||
buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, len(p));
|
||||
if !ok {
|
||||
m = _buffer_grow(b, len(p));
|
||||
}
|
||||
return copy(b.buf[m:], p), nil;
|
||||
}
|
||||
|
||||
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, len(s));
|
||||
if !ok {
|
||||
m = _buffer_grow(b, len(s));
|
||||
}
|
||||
return copy(b.buf[m:], s), nil;
|
||||
}
|
||||
|
||||
buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error {
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, 1);
|
||||
if !ok {
|
||||
m = _buffer_grow(b, 1);
|
||||
}
|
||||
b.buf[m] = c;
|
||||
return nil;
|
||||
}
|
||||
|
||||
buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) {
|
||||
if r < utf8.RUNE_SELF {
|
||||
buffer_write_byte(b, byte(r));
|
||||
return 1, nil;
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, utf8.UTF_MAX);
|
||||
if !ok {
|
||||
m = _buffer_grow(b, utf8.UTF_MAX);
|
||||
}
|
||||
res: [4]byte;
|
||||
res, n = utf8.encode_rune(r);
|
||||
copy(b.buf[m:][:utf8.UTF_MAX], res[:n]);
|
||||
resize(&b.buf, m+n);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_next :: proc(b: ^Buffer, n: int) -> []byte {
|
||||
n := n;
|
||||
b.last_read = .Invalid;
|
||||
m := buffer_length(b);
|
||||
if n > m {
|
||||
n = m;
|
||||
}
|
||||
data := b.buf[b.off : b.off + n];
|
||||
b.off += n;
|
||||
if n > 0 {
|
||||
b.last_read = .Read;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
if buffer_is_empty(b) {
|
||||
buffer_reset(b);
|
||||
if len(p) == 0 {
|
||||
return 0, nil;
|
||||
}
|
||||
return 0, .EOF;
|
||||
}
|
||||
n = copy(p, b.buf[b.off:]);
|
||||
b.off += n;
|
||||
if n > 0 {
|
||||
b.last_read = .Read;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
|
||||
if offset < 0 || offset >= len(b.buf) {
|
||||
err = .Invalid_Offset;
|
||||
return;
|
||||
}
|
||||
|
||||
if 0 <= offset && offset < len(b.buf) {
|
||||
n = copy(p, b.buf[offset:]);
|
||||
}
|
||||
if n > 0 {
|
||||
b.last_read = .Read;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
buffer_read_byte :: proc(b: ^Buffer) -> (byte, io.Error) {
|
||||
if buffer_is_empty(b) {
|
||||
buffer_reset(b);
|
||||
return 0, .EOF;
|
||||
}
|
||||
c := b.buf[b.off];
|
||||
b.off += 1;
|
||||
b.last_read = .Read;
|
||||
return c, nil;
|
||||
}
|
||||
|
||||
buffer_read_rune :: proc(b: ^Buffer) -> (r: rune, size: int, err: io.Error) {
|
||||
if buffer_is_empty(b) {
|
||||
buffer_reset(b);
|
||||
return 0, 0, .EOF;
|
||||
}
|
||||
c := b.buf[b.off];
|
||||
if c < utf8.RUNE_SELF {
|
||||
b.off += 1;
|
||||
b.last_read = .Read_Rune1;
|
||||
return rune(c), 1, nil;
|
||||
}
|
||||
r, size = utf8.decode_rune(b.buf[b.off:]);
|
||||
b.off += size;
|
||||
b.last_read = Read_Op(i8(size));
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_unread_byte :: proc(b: ^Buffer) -> io.Error {
|
||||
if b.last_read == .Invalid {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
if b.off > 0 {
|
||||
b.off -= 1;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
|
||||
if b.last_read <= .Invalid {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
if b.off >= int(b.last_read) {
|
||||
b.off -= int(i8(b.last_read));
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
|
||||
i := index_byte(b.buf[b.off:], delim);
|
||||
end := b.off + i + 1;
|
||||
if i < 0 {
|
||||
end = len(b.buf);
|
||||
err = .EOF;
|
||||
}
|
||||
line = b.buf[b.off:end];
|
||||
b.off = end;
|
||||
b.last_read = .Read;
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Error) {
|
||||
slice: []byte;
|
||||
slice, err = buffer_read_bytes(b, delim);
|
||||
return string(slice), err;
|
||||
}
|
||||
|
||||
buffer_write_to :: proc(b: ^Buffer, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
if byte_count := buffer_length(b); byte_count > 0 {
|
||||
m, e := io.write(w, b.buf[b.off:]);
|
||||
if m > byte_count {
|
||||
panic("bytes.buffer_write_to: invalid io.write count");
|
||||
}
|
||||
b.off += m;
|
||||
n = i64(m);
|
||||
if e != nil {
|
||||
err = e;
|
||||
return;
|
||||
}
|
||||
if m != byte_count {
|
||||
err = .Short_Write;
|
||||
return;
|
||||
}
|
||||
}
|
||||
buffer_reset(b);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #no_bounds_check {
|
||||
b.last_read = .Invalid;
|
||||
for {
|
||||
i := _buffer_grow(b, MIN_READ);
|
||||
resize(&b.buf, i);
|
||||
m, e := io.read(r, b.buf[i:cap(b.buf)]);
|
||||
if m < 0 {
|
||||
err = .Negative_Read;
|
||||
return;
|
||||
}
|
||||
|
||||
resize(&b.buf, i+m);
|
||||
n += i64(m);
|
||||
if e == .EOF {
|
||||
return;
|
||||
}
|
||||
if e != nil {
|
||||
err = e;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
|
||||
s.stream_data = b;
|
||||
s.stream_vtable = _buffer_vtable;
|
||||
return;
|
||||
}
|
||||
|
||||
@(private)
|
||||
_buffer_vtable := &io.Stream_VTable{
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return i64(buffer_capacity(b));
|
||||
},
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read(b, p);
|
||||
},
|
||||
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_at(b, p, int(offset));
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_byte(b);
|
||||
},
|
||||
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_rune(b);
|
||||
},
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write(b, p);
|
||||
},
|
||||
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_at(b, p, int(offset));
|
||||
},
|
||||
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_byte(b, c);
|
||||
},
|
||||
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_rune(b, r);
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_unread_byte(b);
|
||||
},
|
||||
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_unread_rune(b);
|
||||
},
|
||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
buffer_destroy(b);
|
||||
return nil;
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_to(b, w);
|
||||
},
|
||||
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_from(b, r);
|
||||
},
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,177 @@
|
||||
package bytes
|
||||
|
||||
import "core:io"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
Reader :: struct {
|
||||
s: []byte, // read-only buffer
|
||||
i: i64, // current reading index
|
||||
prev_rune: int, // previous reading index of rune or < 0
|
||||
}
|
||||
|
||||
reader_init :: proc(r: ^Reader, s: []byte) {
|
||||
r.s = s;
|
||||
r.i = 0;
|
||||
r.prev_rune = -1;
|
||||
}
|
||||
|
||||
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
||||
s.stream_data = r;
|
||||
s.stream_vtable = _reader_vtable;
|
||||
return;
|
||||
}
|
||||
|
||||
reader_length :: proc(r: ^Reader) -> int {
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0;
|
||||
}
|
||||
return int(i64(len(r.s)) - r.i);
|
||||
}
|
||||
|
||||
reader_size :: proc(r: ^Reader) -> i64 {
|
||||
return i64(len(r.s));
|
||||
}
|
||||
|
||||
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0, .EOF;
|
||||
}
|
||||
r.prev_rune = -1;
|
||||
n = copy(p, r.s[r.i:]);
|
||||
r.i += i64(n);
|
||||
return;
|
||||
}
|
||||
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
||||
if off < 0 {
|
||||
return 0, .Invalid_Offset;
|
||||
}
|
||||
if off >= i64(len(r.s)) {
|
||||
return 0, .EOF;
|
||||
}
|
||||
n = copy(p, r.s[off:]);
|
||||
if n < len(p) {
|
||||
err = .EOF;
|
||||
}
|
||||
return;
|
||||
}
|
||||
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
|
||||
r.prev_rune = -1;
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0, .EOF;
|
||||
}
|
||||
b := r.s[r.i];
|
||||
r.i += 1;
|
||||
return b, nil;
|
||||
}
|
||||
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
|
||||
if r.i <= 0 {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
r.prev_rune = -1;
|
||||
r.i -= 1;
|
||||
return nil;
|
||||
}
|
||||
reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
|
||||
if r.i >= i64(len(r.s)) {
|
||||
r.prev_rune = -1;
|
||||
return 0, 0, .EOF;
|
||||
}
|
||||
r.prev_rune = int(r.i);
|
||||
if c := r.s[r.i]; c < utf8.RUNE_SELF {
|
||||
r.i += 1;
|
||||
return rune(c), 1, nil;
|
||||
}
|
||||
ch, size = utf8.decode_rune(r.s[r.i:]);
|
||||
r.i += i64(size);
|
||||
return;
|
||||
}
|
||||
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
|
||||
if r.i <= 0 {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
if r.prev_rune < 0 {
|
||||
return .Invalid_Unread;
|
||||
}
|
||||
r.i = i64(r.prev_rune);
|
||||
r.prev_rune = -1;
|
||||
return nil;
|
||||
}
|
||||
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||
r.prev_rune = -1;
|
||||
abs: i64;
|
||||
switch whence {
|
||||
case .Start:
|
||||
abs = offset;
|
||||
case .Current:
|
||||
abs = r.i + offset;
|
||||
case .End:
|
||||
abs = i64(len(r.s)) + offset;
|
||||
case:
|
||||
return 0, .Invalid_Whence;
|
||||
}
|
||||
|
||||
if abs < 0 {
|
||||
return 0, .Invalid_Offset;
|
||||
}
|
||||
r.i = abs;
|
||||
return abs, nil;
|
||||
}
|
||||
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
r.prev_rune = -1;
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0, nil;
|
||||
}
|
||||
s := r.s[r.i:];
|
||||
m: int;
|
||||
m, err = io.write(w, s);
|
||||
if m > len(s) {
|
||||
panic("bytes.Reader.write_to: invalid io.write_string count");
|
||||
}
|
||||
r.i += i64(m);
|
||||
n = i64(m);
|
||||
if m != len(s) && err == nil {
|
||||
err = .Short_Write;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_reader_vtable := &io.Stream_VTable{
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_size(r);
|
||||
},
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read(r, p);
|
||||
},
|
||||
impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read_at(r, p, off);
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read_byte(r);
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_unread_byte(r);
|
||||
},
|
||||
impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read_rune(r);
|
||||
},
|
||||
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_unread_rune(r);
|
||||
},
|
||||
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_seek(r, offset, whence);
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_write_to(r, w);
|
||||
},
|
||||
};
|
||||
@@ -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,
|
||||
};
|
||||
@@ -10,6 +10,8 @@ Array :: struct(T: typeid) {
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
ARRAY_DEFAULT_CAPACITY :: 16;
|
||||
|
||||
/*
|
||||
array_init :: proc {
|
||||
array_init_none,
|
||||
@@ -42,11 +44,12 @@ array_set_capacity
|
||||
array_grow
|
||||
*/
|
||||
|
||||
|
||||
array_init_none :: proc(a: ^$A/Array, allocator := context.allocator) {
|
||||
array_init_len(a, 0, 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, 0, 16, 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;
|
||||
|
||||
+52
-38
@@ -1,14 +1,18 @@
|
||||
package container
|
||||
|
||||
import "intrinsics"
|
||||
_ :: intrinsics;
|
||||
|
||||
Map :: struct(Value: typeid) {
|
||||
|
||||
Map :: struct(Key, Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
hash: Array(int),
|
||||
entries: Array(Map_Entry(Value)),
|
||||
entries: Array(Map_Entry(Key, Value)),
|
||||
}
|
||||
|
||||
Map_Entry :: struct(Value: typeid) {
|
||||
key: u64,
|
||||
Map_Entry :: struct(Key, Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
hash: uintptr,
|
||||
next: int,
|
||||
key: Key,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
@@ -47,28 +51,28 @@ multi_map_remove_all
|
||||
|
||||
map_init :: proc{map_init_none, map_init_cap};
|
||||
|
||||
map_init_none :: proc(m: ^$M/Map($Value), allocator := context.allocator) {
|
||||
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($Value), cap: int, allocator := context.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($Value)) {
|
||||
map_delete :: proc(m: $M/Map($Key, $Value)) {
|
||||
array_delete(m.hash);
|
||||
array_delete(m.entries);
|
||||
}
|
||||
|
||||
|
||||
map_has :: proc(m: $M/Map($Value), key: u64) -> bool {
|
||||
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($Value), key: u64) -> (res: Value, ok: bool) #optional_ok {
|
||||
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;
|
||||
@@ -76,7 +80,7 @@ map_get :: proc(m: $M/Map($Value), key: u64) -> (res: Value, ok: bool) #optional
|
||||
return array_get(m.entries, i).value, true;
|
||||
}
|
||||
|
||||
map_get_default :: proc(m: $M/Map($Value), key: u64, default: Value) -> (res: Value, ok: bool) #optional_ok {
|
||||
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;
|
||||
@@ -84,7 +88,7 @@ map_get_default :: proc(m: $M/Map($Value), key: u64, default: Value) -> (res: Va
|
||||
return array_get(m.entries, i).value, true;
|
||||
}
|
||||
|
||||
map_get_ptr :: proc(m: $M/Map($Value), key: u64) -> ^Value {
|
||||
map_get_ptr :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Value {
|
||||
i := _map_find_or_fail(m, key);
|
||||
if i < 0 {
|
||||
return nil;
|
||||
@@ -92,7 +96,7 @@ map_get_ptr :: proc(m: $M/Map($Value), key: u64) -> ^Value {
|
||||
return array_get_ptr(m.entries, i).value;
|
||||
}
|
||||
|
||||
map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
|
||||
map_set :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
|
||||
if array_len(m.hash) == 0 {
|
||||
_map_grow(m);
|
||||
}
|
||||
@@ -104,7 +108,7 @@ map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
|
||||
}
|
||||
}
|
||||
|
||||
map_remove :: proc(m: ^$M/Map($Value), key: u64) {
|
||||
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);
|
||||
@@ -112,7 +116,7 @@ map_remove :: proc(m: ^$M/Map($Value), key: u64) {
|
||||
}
|
||||
|
||||
|
||||
map_reserve :: proc(m: ^$M/Map($Value), new_size: int) {
|
||||
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);
|
||||
@@ -130,14 +134,14 @@ map_reserve :: proc(m: ^$M/Map($Value), new_size: int) {
|
||||
m^ = nm;
|
||||
}
|
||||
|
||||
map_clear :: proc(m: ^$M/Map($Value)) {
|
||||
map_clear :: proc(m: ^$M/Map($Key, $Value)) {
|
||||
array_clear(&m.hash);
|
||||
array_clear(&m.entries);
|
||||
}
|
||||
|
||||
|
||||
|
||||
multi_map_find_first :: proc(m: $M/Map($Value), key: u64) -> ^Map_Entry(Value) {
|
||||
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;
|
||||
@@ -145,11 +149,11 @@ multi_map_find_first :: proc(m: $M/Map($Value), key: u64) -> ^Map_Entry(Value) {
|
||||
return array_get_ptr(m.entries, i);
|
||||
}
|
||||
|
||||
multi_map_find_next :: proc(m: $M/Map($Value), e: ^Map_Entry(Value)) -> ^Map_Entry(Value) {
|
||||
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.key == e.key {
|
||||
if it.hash == e.hash && it.key == e.key {
|
||||
return it;
|
||||
}
|
||||
i = it.next;
|
||||
@@ -157,7 +161,7 @@ multi_map_find_next :: proc(m: $M/Map($Value), e: ^Map_Entry(Value)) -> ^Map_Ent
|
||||
return nil;
|
||||
}
|
||||
|
||||
multi_map_count :: proc(m: $M/Map($Value), key: u64) -> int {
|
||||
multi_map_count :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
|
||||
n := 0;
|
||||
e := multi_map_find_first(m, key);
|
||||
for e != nil {
|
||||
@@ -169,8 +173,10 @@ multi_map_count :: proc(m: $M/Map($Value), key: u64) -> int {
|
||||
|
||||
multi_map_get :: proc{multi_map_get_array, multi_map_get_slice};
|
||||
|
||||
multi_map_get_array :: proc(m: $M/Map($Value), key: u64, items: ^Array(Value)) {
|
||||
if items == nil do return;
|
||||
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);
|
||||
@@ -178,7 +184,7 @@ multi_map_get_array :: proc(m: $M/Map($Value), key: u64, items: ^Array(Value)) {
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_get_slice :: proc(m: $M/Map($Value), key: u64, items: []Value) {
|
||||
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) {
|
||||
@@ -188,7 +194,7 @@ multi_map_get_slice :: proc(m: $M/Map($Value), key: u64, items: []Value) {
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_get_as_slice :: proc(m: $M/Map($Value), key: u64) -> []Value {
|
||||
multi_map_get_as_slice :: proc(m: $M/Map($Key, $Value), key: Key) -> []Value {
|
||||
items: Array(Value);
|
||||
array_init(&items, 0);
|
||||
|
||||
@@ -202,7 +208,7 @@ multi_map_get_as_slice :: proc(m: $M/Map($Value), key: u64) -> []Value {
|
||||
}
|
||||
|
||||
|
||||
multi_map_insert :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
|
||||
multi_map_insert :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
|
||||
if array_len(m.hash) == 0 {
|
||||
_map_grow(m);
|
||||
}
|
||||
@@ -214,14 +220,14 @@ multi_map_insert :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_remove :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) {
|
||||
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($Value), key: u64) {
|
||||
multi_map_remove_all :: proc(m: ^$M/Map($Key, $Value), key: Key) {
|
||||
for map_exist(m^, key) {
|
||||
map_remove(m, key);
|
||||
}
|
||||
@@ -237,9 +243,12 @@ Map_Find_Result :: struct {
|
||||
entry_index: int,
|
||||
}
|
||||
|
||||
_map_add_entry :: proc(m: ^$M/Map($Value), key: u64) -> int {
|
||||
e: Map_Entry(Value);
|
||||
_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);
|
||||
@@ -269,7 +278,7 @@ _map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) {
|
||||
}
|
||||
|
||||
|
||||
_map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result {
|
||||
_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;
|
||||
@@ -279,11 +288,16 @@ _map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result {
|
||||
return fr;
|
||||
}
|
||||
|
||||
fr.hash_index = int(key % u64(array_len(m.hash)));
|
||||
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.key == key {
|
||||
if it.hash == hash && it.key == key {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
@@ -292,7 +306,7 @@ _map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result {
|
||||
return fr;
|
||||
}
|
||||
|
||||
_map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Result {
|
||||
_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;
|
||||
@@ -302,7 +316,7 @@ _map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Re
|
||||
return fr;
|
||||
}
|
||||
|
||||
fr.hash_index = int(e.key % u64(array_len(m.hash)));
|
||||
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);
|
||||
@@ -315,10 +329,10 @@ _map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Re
|
||||
return fr;
|
||||
}
|
||||
|
||||
_map_find_or_fail :: proc(m: $M/Map($Value), key: u64) -> int {
|
||||
_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($Value), key: u64) -> int {
|
||||
_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;
|
||||
@@ -334,7 +348,7 @@ _map_find_or_make :: proc(m: ^$M/Map($Value), key: u64) -> int {
|
||||
}
|
||||
|
||||
|
||||
_map_make :: proc(m: ^$M/Map($Value), key: u64) -> int {
|
||||
_map_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
|
||||
fr := _map_find_key(m^, key);
|
||||
i := _map_add_entry(m, key);
|
||||
|
||||
@@ -350,12 +364,12 @@ _map_make :: proc(m: ^$M/Map($Value), key: u64) -> int {
|
||||
}
|
||||
|
||||
|
||||
_map_full :: proc(m: $M/Map($Value)) -> bool {
|
||||
_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($Value)) {
|
||||
_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];
|
||||
}
|
||||
@@ -115,6 +115,9 @@ queue_pop_front :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ ring_len :: proc(r: ^$R/Ring) -> int {
|
||||
n := 0;
|
||||
if r != nil {
|
||||
n = 1;
|
||||
for p := ring_next(p); p != r; p = p.next {
|
||||
for p := ring_next(&p); p != r; p = p.next {
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ small_array_pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location
|
||||
return item;
|
||||
}
|
||||
|
||||
small_array_pop_font :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
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);
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package dynlib
|
||||
|
||||
Library :: opaque rawptr;
|
||||
Library :: distinct rawptr;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// +build linux, darwin
|
||||
// +build linux, darwin, freebsd
|
||||
package dynlib
|
||||
|
||||
import "core:os"
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
flags := os.RTLD_NOW;
|
||||
if global_symbols do flags |= os.RTLD_GLOBAL;
|
||||
if global_symbols {
|
||||
flags |= os.RTLD_GLOBAL;
|
||||
}
|
||||
lib := os.dlopen(path, flags);
|
||||
return Library(lib), lib != nil;
|
||||
}
|
||||
@@ -18,4 +20,4 @@ symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found:
|
||||
ptr = os.dlsym(rawptr(library), symbol);
|
||||
found = ptr != nil;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ 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'
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7',
|
||||
};
|
||||
|
||||
PADDING :: '=';
|
||||
@@ -30,7 +30,7 @@ DEC_TABLE := [?]u8 {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
|
||||
@@ -92,10 +92,11 @@ _encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.al
|
||||
}
|
||||
|
||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
|
||||
if len(data) == 0 do return []byte{};
|
||||
if len(data) == 0 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
outi := 0;
|
||||
olen := len(data);
|
||||
data := data;
|
||||
|
||||
out := make([]byte, len(data) / 8 * 5, allocator);
|
||||
@@ -113,7 +114,9 @@ decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocato
|
||||
data = data[1:];
|
||||
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
|
||||
assert(!(len(data) + j < 8 - 1), "Corrupted input");
|
||||
for k := 0; k < 8-1-j; k +=1 do assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input");
|
||||
for k := 0; k < 8-1-j; k +=1 {
|
||||
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input");
|
||||
}
|
||||
dlen, end = j, true;
|
||||
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input");
|
||||
break;
|
||||
|
||||
@@ -2,46 +2,48 @@ package base64
|
||||
|
||||
// @note(zh): Encoding utility for Base64
|
||||
// A secondary param can be used to supply a custom alphabet to
|
||||
// @link(encode) and a matching decoding table to @link(decode).
|
||||
// @link(encode) and a matching decoding table to @link(decode).
|
||||
// If none is supplied it just uses the standard Base64 alphabet.
|
||||
// Incase your specific version does not use padding, you may
|
||||
// truncate it from the encoded output.
|
||||
|
||||
ENC_TABLE := [64]byte {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/',
|
||||
};
|
||||
|
||||
PADDING :: '=';
|
||||
|
||||
DEC_TABLE := [128]int {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, -1, -1, -1, -1, -1
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, -1, -1, -1, -1, -1,
|
||||
};
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check {
|
||||
length := len(data);
|
||||
if length == 0 do return "";
|
||||
if length == 0 {
|
||||
return "";
|
||||
}
|
||||
|
||||
out_length := ((4 * length / 3) + 3) &~ 3;
|
||||
out := make([]byte, out_length, allocator);
|
||||
@@ -51,8 +53,8 @@ encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocato
|
||||
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 do c1 = int(data[i + 1]);
|
||||
if i + 2 < length do c2 = int(data[i + 2]);
|
||||
if i + 1 < length { c1 = int(data[i + 1]); }
|
||||
if i + 2 < length { c2 = int(data[i + 2]); }
|
||||
|
||||
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0);
|
||||
|
||||
@@ -66,7 +68,9 @@ encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocato
|
||||
|
||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check {
|
||||
length := len(data);
|
||||
if length == 0 do return []byte{};
|
||||
if length == 0 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0;
|
||||
out_length := ((length * 6) >> 3) - pad_count;
|
||||
|
||||
+35
-25
@@ -32,7 +32,9 @@ Parser :: struct {
|
||||
|
||||
print_value :: proc(value: Value, pretty := true, indent := 0) {
|
||||
print_indent :: proc(indent: int) {
|
||||
for _ in 0..<indent do fmt.print("\t");
|
||||
for _ in 0..<indent {
|
||||
fmt.print("\t");
|
||||
}
|
||||
}
|
||||
|
||||
switch v in value {
|
||||
@@ -42,22 +44,22 @@ print_value :: proc(value: Value, pretty := true, indent := 0) {
|
||||
case string: fmt.print(v);
|
||||
case Array:
|
||||
fmt.print("[");
|
||||
if pretty do fmt.println();
|
||||
if pretty { fmt.println(); }
|
||||
for e, i in v {
|
||||
if pretty {
|
||||
print_indent(indent+1);
|
||||
print_value(e, pretty, indent+1);
|
||||
fmt.println(",");
|
||||
} else {
|
||||
if i > 0 do fmt.print(", ");
|
||||
if i > 0 { fmt.print(", "); }
|
||||
print_value(e);
|
||||
}
|
||||
}
|
||||
if pretty do print_indent(indent);
|
||||
if pretty { print_indent(indent); }
|
||||
fmt.print("]");
|
||||
case Dict:
|
||||
fmt.print("{");
|
||||
if pretty do fmt.println();
|
||||
if pretty { fmt.println(); }
|
||||
|
||||
i := 0;
|
||||
for name, val in v {
|
||||
@@ -67,14 +69,14 @@ print_value :: proc(value: Value, pretty := true, indent := 0) {
|
||||
print_value(val, pretty, indent+1);
|
||||
fmt.println(",");
|
||||
} else {
|
||||
if i > 0 do fmt.print(", ");
|
||||
if i > 0 { fmt.print(", "); }
|
||||
fmt.printf("%s = ", name);
|
||||
print_value(val, pretty, indent+1);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if pretty do print_indent(indent);
|
||||
if pretty { print_indent(indent); }
|
||||
fmt.print("}");
|
||||
case:
|
||||
fmt.print("nil");
|
||||
@@ -149,17 +151,23 @@ destroy :: proc(p: ^Parser) {
|
||||
destroy_value :: proc(value: Value) {
|
||||
#partial switch v in value {
|
||||
case Array:
|
||||
for elem in v do destroy_value(elem);
|
||||
for elem in v {
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(v);
|
||||
|
||||
case Dict:
|
||||
for _, dv in v do destroy_value(dv);
|
||||
for _, dv in v {
|
||||
destroy_value(dv);
|
||||
}
|
||||
delete(v);
|
||||
}
|
||||
}
|
||||
|
||||
delete(p.tokens);
|
||||
for s in p.allocated_strings do delete(s);
|
||||
for s in p.allocated_strings {
|
||||
delete(s);
|
||||
}
|
||||
delete(p.allocated_strings);
|
||||
delete(p.dict_stack);
|
||||
|
||||
@@ -348,7 +356,9 @@ expect_token :: proc(p: ^Parser, kind: Kind) -> Token {
|
||||
prev := p.curr_token;
|
||||
if prev.kind != kind {
|
||||
got := prev.lit;
|
||||
if got == "\n" do got = ";";
|
||||
if got == "\n" {
|
||||
got = ";";
|
||||
}
|
||||
error(p, prev.pos, "Expected %s, got %s", kind_to_string[kind], got);
|
||||
}
|
||||
next_token(p);
|
||||
@@ -411,7 +421,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
case .Ident:
|
||||
next_token(p);
|
||||
v, ok := lookup_value(p, tok.lit);
|
||||
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
|
||||
if !ok { error(p, tok.pos, "Undeclared identifier %s", tok.lit); }
|
||||
return v, tok.pos;
|
||||
|
||||
case .True:
|
||||
@@ -438,7 +448,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
case .String:
|
||||
next_token(p);
|
||||
str, ok := unquote_string(p, tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
if !ok { error(p, tok.pos, "Unable to unquote string"); }
|
||||
return string(str), tok.pos;
|
||||
|
||||
case .Open_Paren:
|
||||
@@ -480,7 +490,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
}
|
||||
|
||||
name, ok := unquote_string(p, name_tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
if !ok { error(p, tok.pos, "Unable to unquote string"); }
|
||||
expect_token(p, .Assign);
|
||||
elem, _ := parse_expr(p);
|
||||
|
||||
@@ -520,7 +530,7 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
|
||||
continue;
|
||||
}
|
||||
name, usok := unquote_string(p, tok);
|
||||
if !usok do error(p, tok.pos, "Unable to unquote string");
|
||||
if !usok { error(p, tok.pos, "Unable to unquote string"); }
|
||||
val, found := d[name];
|
||||
if !found {
|
||||
error(p, tok.pos, "Field %s not found in dictionary", name);
|
||||
@@ -594,7 +604,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
next_token(p);
|
||||
tok := expect_token(p, .String);
|
||||
v, ok := lookup_value(p, tok.lit);
|
||||
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
|
||||
if !ok { error(p, tok.pos, "Undeclared identifier %s", tok.lit); }
|
||||
return parse_atom_expr(p, v, tok.pos);
|
||||
|
||||
case .Add, .Sub:
|
||||
@@ -603,8 +613,8 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
expr, pos := parse_unary_expr(p);
|
||||
|
||||
#partial switch e in expr {
|
||||
case i64: if op.kind == .Sub do return -e, pos;
|
||||
case f64: if op.kind == .Sub do return -e, pos;
|
||||
case i64: if op.kind == .Sub { return -e, pos; }
|
||||
case f64: if op.kind == .Sub { return -e, pos; }
|
||||
case:
|
||||
error(p, op.pos, "Unary operator %s can only be used on integers or floats", op.lit);
|
||||
return nil, op.pos;
|
||||
@@ -667,9 +677,9 @@ match_values :: proc(left, right: ^Value) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, bool) {
|
||||
calculate_binary_value :: proc(p: ^Parser, op: Kind, a_, b_: Value) -> (Value, bool) {
|
||||
// TODO(bill): Calculate value as you go!
|
||||
x, y := a, b;
|
||||
x, y := a_, b_;
|
||||
match_values(&x, &y);
|
||||
|
||||
|
||||
@@ -678,7 +688,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
|
||||
|
||||
case bool:
|
||||
b, ok := y.(bool);
|
||||
if !ok do return nil, false;
|
||||
if !ok { return nil, false; }
|
||||
#partial switch op {
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
@@ -688,7 +698,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
|
||||
|
||||
case i64:
|
||||
b, ok := y.(i64);
|
||||
if !ok do return nil, false;
|
||||
if !ok { return nil, false; }
|
||||
#partial switch op {
|
||||
case .Add: return a + b, true;
|
||||
case .Sub: return a - b, true;
|
||||
@@ -705,7 +715,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
|
||||
|
||||
case f64:
|
||||
b, ok := y.(f64);
|
||||
if !ok do return nil, false;
|
||||
if !ok { return nil, false; }
|
||||
|
||||
#partial switch op {
|
||||
case .Add: return a + b, true;
|
||||
@@ -722,7 +732,7 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
|
||||
|
||||
case string:
|
||||
b, ok := y.(string);
|
||||
if !ok do return nil, false;
|
||||
if !ok { return nil, false; }
|
||||
|
||||
#partial switch op {
|
||||
case .Add:
|
||||
@@ -825,7 +835,7 @@ parse_assignment :: proc(p: ^Parser) -> bool {
|
||||
if allow_token(p, .Ident) || allow_token(p, .String) {
|
||||
expect_token(p, .Assign);
|
||||
name, ok := unquote_string(p, tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
if !ok { error(p, tok.pos, "Unable to unquote string"); }
|
||||
expr, _ := parse_expr(p);
|
||||
d := top_dict(p);
|
||||
if _, ok2 := d[name]; ok2 {
|
||||
|
||||
@@ -162,9 +162,9 @@ token_lookup :: proc(ident: string) -> Kind {
|
||||
return Ident;
|
||||
}
|
||||
|
||||
is_literal :: proc(tok: Kind) -> bool do return _literal_start < tok && tok < _literal_end;
|
||||
is_operator :: proc(tok: Kind) -> bool do return _operator_start < tok && tok < _operator_end;
|
||||
is_keyword :: proc(tok: Kind) -> bool do return _keyword_start < tok && tok < _keyword_end;
|
||||
is_literal :: proc(tok: Kind) -> bool { return _literal_start < tok && tok < _literal_end; }
|
||||
is_operator :: proc(tok: Kind) -> bool { return _operator_start < tok && tok < _operator_end; }
|
||||
is_keyword :: proc(tok: Kind) -> bool { return _keyword_start < tok && tok < _keyword_end; }
|
||||
|
||||
|
||||
tokenizer_init :: proc(t: ^Tokenizer, src: []byte, file := "") {
|
||||
|
||||
@@ -0,0 +1,406 @@
|
||||
// package csv reads and writes comma-separated values (CSV) files.
|
||||
// This package supports the format described in RFC 4180 <https://tools.ietf.org/html/rfc4180.html>
|
||||
package csv
|
||||
|
||||
import "core:bufio"
|
||||
import "core:bytes"
|
||||
import "core:io"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
// Reader is a data structure used for reading records from a CSV-encoded file
|
||||
//
|
||||
// The associated procedures for Reader expects its input to conform to RFC 4180.
|
||||
Reader :: struct {
|
||||
// comma is the field delimiter
|
||||
// reader_init will set it to be ','
|
||||
// A "comma" must be a valid rune, nor can it be \r, \n, or the Unicode replacement character (0xfffd)
|
||||
comma: rune,
|
||||
|
||||
// comment, if not 0, is the comment character
|
||||
// Lines beginning with the comment character without a preceding whitespace are ignored
|
||||
comment: rune,
|
||||
|
||||
// fields_per_record is the number of expected fields per record
|
||||
// if fields_per_record is >0, 'read' requires each record to have that field count
|
||||
// if fields_per_record is 0, 'read' sets it to the field count in the first record
|
||||
// if fields_per_record is <0, no check is made and records may have a variable field count
|
||||
fields_per_record: int,
|
||||
|
||||
// If trim_leading_space is true, leading whitespace in a field is ignored
|
||||
// This is done even if the field delimiter (comma), is whitespace
|
||||
trim_leading_space: bool,
|
||||
|
||||
// If lazy_quotes is true, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field
|
||||
lazy_quotes: bool,
|
||||
|
||||
// reuse_record controls whether calls to 'read' may return a slice using the backing buffer
|
||||
// for performance
|
||||
// By default, each call to 'read' returns a newly allocated slice
|
||||
reuse_record: bool,
|
||||
|
||||
// reuse_record_buffer controls whether calls to 'read' clone the strings of each field or uses
|
||||
// the data stored in record buffer for performance
|
||||
// By default, each call to 'read' clones the strings of each field
|
||||
reuse_record_buffer: bool,
|
||||
|
||||
|
||||
// internal buffers
|
||||
r: bufio.Reader,
|
||||
line_count: int, // current line being read in the CSV file
|
||||
raw_buffer: [dynamic]byte,
|
||||
record_buffer: [dynamic]byte,
|
||||
field_indices: [dynamic]int,
|
||||
last_record: [dynamic]string,
|
||||
sr: strings.Reader, // used by reader_init_with_string
|
||||
}
|
||||
|
||||
|
||||
Reader_Error_Kind :: enum {
|
||||
Bare_Quote,
|
||||
Quote,
|
||||
Field_Count,
|
||||
Invalid_Delim,
|
||||
}
|
||||
|
||||
reader_error_kind_string := [Reader_Error_Kind]string{
|
||||
.Bare_Quote = "bare \" in non-quoted field",
|
||||
.Quote = "extra or missing \" in quoted field",
|
||||
.Field_Count = "wrong field count",
|
||||
.Invalid_Delim = "invalid delimiter",
|
||||
};
|
||||
|
||||
Reader_Error :: struct {
|
||||
kind: Reader_Error_Kind,
|
||||
start_line: int,
|
||||
line: int,
|
||||
column: int,
|
||||
expected, got: int, // used by .Field_Count
|
||||
}
|
||||
|
||||
Error :: union {
|
||||
Reader_Error,
|
||||
io.Error,
|
||||
}
|
||||
|
||||
DEFAULT_RECORD_BUFFER_CAPACITY :: 256;
|
||||
|
||||
// reader_init initializes a new Reader from r
|
||||
reader_init :: proc(reader: ^Reader, r: io.Reader, buffer_allocator := context.allocator) {
|
||||
reader.comma = ',';
|
||||
|
||||
context.allocator = buffer_allocator;
|
||||
reserve(&reader.record_buffer, DEFAULT_RECORD_BUFFER_CAPACITY);
|
||||
reserve(&reader.raw_buffer, 0);
|
||||
reserve(&reader.field_indices, 0);
|
||||
reserve(&reader.last_record, 0);
|
||||
bufio.reader_init(&reader.r, r);
|
||||
}
|
||||
|
||||
|
||||
// reader_init_with_string initializes a new Reader from s
|
||||
reader_init_with_string :: proc(reader: ^Reader, s: string, buffer_allocator := context.allocator) {
|
||||
strings.reader_init(&reader.sr, s);
|
||||
r, _ := io.to_reader(strings.reader_to_stream(&reader.sr));
|
||||
reader_init(reader, r, buffer_allocator);
|
||||
}
|
||||
|
||||
// reader_destroy destroys a Reader
|
||||
reader_destroy :: proc(r: ^Reader) {
|
||||
delete(r.raw_buffer);
|
||||
delete(r.record_buffer);
|
||||
delete(r.field_indices);
|
||||
delete(r.last_record);
|
||||
bufio.reader_destroy(&r.r);
|
||||
}
|
||||
|
||||
// read reads a single record (a slice of fields) from r
|
||||
//
|
||||
// All \r\n sequences are normalized to \n, including multi-line field
|
||||
read :: proc(r: ^Reader, allocator := context.allocator) -> (record: []string, err: Error) {
|
||||
if r.reuse_record {
|
||||
record, err = _read_record(r, &r.last_record, allocator);
|
||||
resize(&r.last_record, len(record));
|
||||
copy(r.last_record[:], record);
|
||||
} else {
|
||||
record, err = _read_record(r, nil, allocator);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// is_io_error checks where an Error is a specific io.Error kind
|
||||
is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
|
||||
if v, ok := err.(io.Error); ok {
|
||||
return v == io_err;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// read_all reads all the remaining records from r.
|
||||
// Each record is a slice of fields.
|
||||
// read_all is defined to read until an EOF, and does not treat, and does not treat EOF as an error
|
||||
read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Error) {
|
||||
context.allocator = allocator;
|
||||
records: [dynamic][]string;
|
||||
for {
|
||||
record, rerr := _read_record(r, nil, allocator);
|
||||
if is_io_error(rerr, .EOF) {
|
||||
return records[:], nil;
|
||||
}
|
||||
if rerr != nil {
|
||||
return nil, rerr;
|
||||
}
|
||||
append(&records, record);
|
||||
}
|
||||
}
|
||||
|
||||
// read reads a single record (a slice of fields) from the provided input.
|
||||
read_from_string :: proc(input: string, record_allocator := context.allocator, buffer_allocator := context.allocator) -> (record: []string, n: int, err: Error) {
|
||||
ir: strings.Reader;
|
||||
strings.reader_init(&ir, input);
|
||||
input_reader, _ := io.to_reader(strings.reader_to_stream(&ir));
|
||||
|
||||
r: Reader;
|
||||
reader_init(&r, input_reader, buffer_allocator);
|
||||
defer reader_destroy(&r);
|
||||
record, err = read(&r, record_allocator);
|
||||
n = int(r.r.r);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// read_all reads all the remaining records from the provided input.
|
||||
read_all_from_string :: proc(input: string, records_allocator := context.allocator, buffer_allocator := context.allocator) -> ([][]string, Error) {
|
||||
ir: strings.Reader;
|
||||
strings.reader_init(&ir, input);
|
||||
input_reader, _ := io.to_reader(strings.reader_to_stream(&ir));
|
||||
|
||||
r: Reader;
|
||||
reader_init(&r, input_reader, buffer_allocator);
|
||||
defer reader_destroy(&r);
|
||||
return read_all(&r, records_allocator);
|
||||
}
|
||||
|
||||
@private
|
||||
is_valid_delim :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case 0, '"', '\r', '\n', utf8.RUNE_ERROR:
|
||||
return false;
|
||||
}
|
||||
return utf8.valid_rune(r);
|
||||
}
|
||||
|
||||
@private
|
||||
_read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) {
|
||||
read_line :: proc(r: ^Reader) -> ([]byte, io.Error) {
|
||||
line, err := bufio.reader_read_slice(&r.r, '\n');
|
||||
if err == .Buffer_Full {
|
||||
clear(&r.raw_buffer);
|
||||
append(&r.raw_buffer, ..line);
|
||||
for err == .Buffer_Full {
|
||||
line, err = bufio.reader_read_slice(&r.r, '\n');
|
||||
append(&r.raw_buffer, ..line);
|
||||
}
|
||||
line = r.raw_buffer[:];
|
||||
}
|
||||
if len(line) > 0 && err == .EOF {
|
||||
err = nil;
|
||||
if line[len(line)-1] == '\r' {
|
||||
line = line[:len(line)-1];
|
||||
}
|
||||
}
|
||||
r.line_count += 1;
|
||||
|
||||
// normalize \r\n to \n
|
||||
n := len(line);
|
||||
for n >= 2 && string(line[n-2:]) == "\r\n" {
|
||||
line[n-2] = '\n';
|
||||
line = line[:n-1];
|
||||
}
|
||||
|
||||
return line, err;
|
||||
}
|
||||
|
||||
length_newline :: proc(b: []byte) -> int {
|
||||
if len(b) > 0 && b[len(b)-1] == '\n' {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
next_rune :: proc(b: []byte) -> rune {
|
||||
r, _ := utf8.decode_rune(b);
|
||||
return r;
|
||||
}
|
||||
|
||||
if r.comma == r.comment ||
|
||||
!is_valid_delim(r.comma) ||
|
||||
(r.comment != 0 && !is_valid_delim(r.comment)) {
|
||||
err := Reader_Error{
|
||||
kind = .Invalid_Delim,
|
||||
line = r.line_count,
|
||||
};
|
||||
return nil, err;
|
||||
}
|
||||
|
||||
line, full_line: []byte;
|
||||
err_read: io.Error;
|
||||
for err_read == nil {
|
||||
line, err_read = read_line(r);
|
||||
if r.comment != 0 && next_rune(line) == r.comment {
|
||||
line = nil;
|
||||
continue;
|
||||
}
|
||||
if err_read == nil && len(line) == length_newline(line) {
|
||||
line = nil;
|
||||
continue;
|
||||
}
|
||||
full_line = line;
|
||||
break;
|
||||
}
|
||||
|
||||
if is_io_error(err_read, .EOF) {
|
||||
return nil, err_read;
|
||||
}
|
||||
|
||||
err: Error;
|
||||
quote_len :: len(`"`);
|
||||
comma_len := utf8.rune_size(r.comma);
|
||||
record_line := r.line_count;
|
||||
clear(&r.record_buffer);
|
||||
clear(&r.field_indices);
|
||||
|
||||
parse_field: for {
|
||||
if r.trim_leading_space {
|
||||
line = bytes.trim_left_space(line);
|
||||
}
|
||||
if len(line) == 0 || line[0] != '"' {
|
||||
i := bytes.index_rune(line, r.comma);
|
||||
field := line;
|
||||
if i >= 0 {
|
||||
field = field[:i];
|
||||
} else {
|
||||
field = field[:len(field) - length_newline(field)];
|
||||
}
|
||||
|
||||
if !r.lazy_quotes {
|
||||
if j := bytes.index_byte(field, '"'); j >= 0 {
|
||||
column := utf8.rune_count(full_line[:len(full_line) - len(line[j:])]);
|
||||
err = Reader_Error{
|
||||
kind = .Bare_Quote,
|
||||
start_line = record_line,
|
||||
line = r.line_count,
|
||||
column = column,
|
||||
};
|
||||
break parse_field;
|
||||
}
|
||||
}
|
||||
append(&r.record_buffer, ..field);
|
||||
append(&r.field_indices, len(r.record_buffer));
|
||||
if i >= 0 {
|
||||
line = line[i+comma_len:];
|
||||
continue parse_field;
|
||||
}
|
||||
break parse_field;
|
||||
|
||||
} else {
|
||||
line = line[quote_len:];
|
||||
for {
|
||||
i := bytes.index_byte(line, '"');
|
||||
switch {
|
||||
case i >= 0:
|
||||
append(&r.record_buffer, ..line[:i]);
|
||||
line = line[i+quote_len:];
|
||||
switch ch := next_rune(line); {
|
||||
case ch == '"': // append quote
|
||||
append(&r.record_buffer, '"');
|
||||
line = line[quote_len:];
|
||||
case ch == r.comma: // end of field
|
||||
line = line[comma_len:];
|
||||
append(&r.field_indices, len(r.record_buffer));
|
||||
continue parse_field;
|
||||
case length_newline(line) == len(line): // end of line
|
||||
append(&r.field_indices, len(r.record_buffer));
|
||||
break parse_field;
|
||||
case r.lazy_quotes: // bare quote
|
||||
append(&r.record_buffer, '"');
|
||||
case: // invalid non-escaped quote
|
||||
column := utf8.rune_count(full_line[:len(full_line) - len(line) - quote_len]);
|
||||
err = Reader_Error{
|
||||
kind = .Quote,
|
||||
start_line = record_line,
|
||||
line = r.line_count,
|
||||
column = column,
|
||||
};
|
||||
break parse_field;
|
||||
}
|
||||
|
||||
case len(line) > 0:
|
||||
append(&r.record_buffer, ..line);
|
||||
if err_read != nil {
|
||||
break parse_field;
|
||||
}
|
||||
line, err_read = read_line(r);
|
||||
if is_io_error(err_read, .EOF) {
|
||||
err_read = nil;
|
||||
}
|
||||
full_line = line;
|
||||
|
||||
case:
|
||||
if !r.lazy_quotes && err_read == nil {
|
||||
column := utf8.rune_count(full_line);
|
||||
err = Reader_Error{
|
||||
kind = .Quote,
|
||||
start_line = record_line,
|
||||
line = r.line_count,
|
||||
column = column,
|
||||
};
|
||||
break parse_field;
|
||||
}
|
||||
append(&r.field_indices, len(r.record_buffer));
|
||||
break parse_field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && err_read != nil {
|
||||
err = err_read;
|
||||
}
|
||||
|
||||
context.allocator = allocator;
|
||||
dst := dst;
|
||||
str := string(r.record_buffer[:]);
|
||||
if dst == nil {
|
||||
// use local variable
|
||||
dst = &([dynamic]string){};
|
||||
}
|
||||
clear(dst);
|
||||
resize(dst, len(r.field_indices));
|
||||
pre_idx: int;
|
||||
for idx, i in r.field_indices {
|
||||
field := str[pre_idx:idx];
|
||||
if !r.reuse_record_buffer {
|
||||
field = strings.clone(field);
|
||||
}
|
||||
dst[i] = field;
|
||||
pre_idx = idx;
|
||||
}
|
||||
|
||||
if r.fields_per_record > 0 {
|
||||
if len(dst) != r.fields_per_record && err == nil {
|
||||
err = Reader_Error{
|
||||
kind = .Field_Count,
|
||||
start_line = record_line,
|
||||
line = r.line_count,
|
||||
expected = r.fields_per_record,
|
||||
got = len(dst),
|
||||
};
|
||||
}
|
||||
} else if r.fields_per_record == 0 {
|
||||
r.fields_per_record = len(dst);
|
||||
}
|
||||
return dst[:], err;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package csv
|
||||
|
||||
import "core:io"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
// Writer is a data structure used for writing records using a CSV-encoding.
|
||||
Writer :: struct {
|
||||
// Field delimiter (set to ',' with writer_init)
|
||||
comma: rune,
|
||||
|
||||
// if set to true, \r\n will be used as the line terminator
|
||||
use_crlf: bool,
|
||||
|
||||
w: io.Writer,
|
||||
}
|
||||
|
||||
// writer_init initializes a Writer that writes to w
|
||||
writer_init :: proc(writer: ^Writer, w: io.Writer) {
|
||||
writer.comma = ',';
|
||||
writer.w = w;
|
||||
}
|
||||
|
||||
// write writes a single CSV records to w with any of the necessarily quoting.
|
||||
// A record is a slice of strings, where each string is a single field.
|
||||
//
|
||||
// If the underlying io.Writer requires flushing, make sure to call io.flush
|
||||
write :: proc(w: ^Writer, record: []string) -> io.Error {
|
||||
CHAR_SET :: "\n\r\"";
|
||||
|
||||
field_needs_quoting :: proc(w: ^Writer, field: string) -> bool {
|
||||
switch {
|
||||
case field == "": // No need to quote empty strings
|
||||
return false;
|
||||
case field == `\.`: // Postgres is weird
|
||||
return true;
|
||||
case w.comma < utf8.RUNE_SELF: // ASCII optimization
|
||||
for i in 0..<len(field) {
|
||||
switch field[i] {
|
||||
case '\n', '\r', '"', byte(w.comma):
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case:
|
||||
if strings.contains_rune(field, w.comma) >= 0 {
|
||||
return true;
|
||||
}
|
||||
if strings.contains_any(field, CHAR_SET) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Leading spaces need quoting
|
||||
r, _ := utf8.decode_rune_in_string(field);
|
||||
return strings.is_space(r);
|
||||
}
|
||||
|
||||
if !is_valid_delim(w.comma) {
|
||||
return .No_Progress; // TODO(bill): Is this a good error?
|
||||
}
|
||||
|
||||
for _, field_idx in record {
|
||||
// NOTE(bill): declared like this so that the field can be modified later if necessary
|
||||
field := record[field_idx];
|
||||
|
||||
if field_idx > 0 {
|
||||
if _, err := io.write_rune(w.w, w.comma); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if !field_needs_quoting(w, field) {
|
||||
if _, err := io.write_string(w.w, field); err != nil {
|
||||
return err;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if err := io.write_byte(w.w, '"'); err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
for len(field) > 0 {
|
||||
i := strings.index_any(field, CHAR_SET);
|
||||
if i < 0 {
|
||||
i = len(field);
|
||||
}
|
||||
|
||||
if _, err := io.write_string(w.w, field[:i]); err != nil {
|
||||
return err;
|
||||
}
|
||||
field = field[i:];
|
||||
|
||||
if len(field) > 0 {
|
||||
switch field[0] {
|
||||
case '\r':
|
||||
if !w.use_crlf {
|
||||
if err := io.write_byte(w.w, '\r'); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
case '\n':
|
||||
if w.use_crlf {
|
||||
if _, err := io.write_string(w.w, "\r\n"); err != nil {
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
if err := io.write_byte(w.w, '\n'); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
case '"':
|
||||
if _, err := io.write_string(w.w, `""`); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
field = field[1:];
|
||||
}
|
||||
}
|
||||
if err := io.write_byte(w.w, '"'); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if w.use_crlf {
|
||||
_, err := io.write_string(w.w, "\r\n");
|
||||
return err;
|
||||
}
|
||||
return io.write_byte(w.w, '\n');
|
||||
}
|
||||
|
||||
// write_all writes multiple CSV records to w using write, and then flushes (if necessary).
|
||||
write_all :: proc(w: ^Writer, records: [][]string) -> io.Error {
|
||||
for record in records {
|
||||
err := write(w, record);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return writer_flush(w);
|
||||
}
|
||||
|
||||
// writer_flush flushes the underlying io.Writer.
|
||||
// If the underlying io.Writer does not support flush, nil is returned.
|
||||
writer_flush :: proc(w: ^Writer) -> io.Error {
|
||||
return io.flush(auto_cast w.w);
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import "core:math/bits"
|
||||
import "core:runtime"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
|
||||
Marshal_Error :: enum {
|
||||
None,
|
||||
@@ -14,7 +13,8 @@ Marshal_Error :: enum {
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
|
||||
b := strings.make_builder(allocator);
|
||||
b: strings.Builder;
|
||||
strings.init_builder(&b, allocator);
|
||||
|
||||
err := marshal_arg(&b, v);
|
||||
|
||||
@@ -89,6 +89,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
case Type_Info_Float:
|
||||
val: f64;
|
||||
switch f in a {
|
||||
case f16: val = f64(f);
|
||||
case f32: val = f64(f);
|
||||
case f64: val = f64(f);
|
||||
}
|
||||
@@ -129,7 +130,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
case b32: val = bool(b);
|
||||
case b64: val = bool(b);
|
||||
}
|
||||
write_string(b, val ? "true" : "false");
|
||||
write_string_builder(b, val ? "true" : "false");
|
||||
|
||||
case Type_Info_Any:
|
||||
return .Unsupported_Type;
|
||||
@@ -161,7 +162,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
case Type_Info_Array:
|
||||
write_byte(b, '[');
|
||||
for i in 0..<info.count {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
@@ -172,7 +173,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
write_byte(b, '[');
|
||||
array := cast(^mem.Raw_Dynamic_Array)v.data;
|
||||
for i in 0..<array.len {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(array.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
@@ -183,7 +184,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
write_byte(b, '[');
|
||||
slice := cast(^mem.Raw_Slice)v.data;
|
||||
for i in 0..<slice.len {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(slice.data) + uintptr(i*info.elem_size);
|
||||
marshal_arg(b, any{rawptr(data), info.elem.id});
|
||||
@@ -205,21 +206,15 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
entry_size := ed.elem_size;
|
||||
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size);
|
||||
header := cast(^Map_Entry_Header)data;
|
||||
|
||||
if reflect.is_string(info.key) {
|
||||
marshal_arg(b, header.key.str);
|
||||
} else {
|
||||
marshal_arg(b, any{rawptr(&header.key.hash), info.key.id});
|
||||
}
|
||||
key := rawptr(data + entry_type.offsets[2]);
|
||||
value := rawptr(data + entry_type.offsets[3]);
|
||||
|
||||
marshal_arg(b, any{key, info.key.id});
|
||||
write_string(b, ": ");
|
||||
|
||||
value := data + entry_type.offsets[2];
|
||||
marshal_arg(b, any{rawptr(value), info.value.id});
|
||||
marshal_arg(b, any{value, info.value.id});
|
||||
}
|
||||
}
|
||||
write_byte(b, '}');
|
||||
@@ -227,7 +222,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
case Type_Info_Struct:
|
||||
write_byte(b, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
if i > 0 { write_string(b, ", "); }
|
||||
write_quoted_string(b, name);
|
||||
write_string(b, ": ");
|
||||
|
||||
@@ -264,34 +259,6 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
case Type_Info_Enum:
|
||||
return marshal_arg(b, any{v.data, info.base.id});
|
||||
|
||||
case Type_Info_Bit_Field:
|
||||
data: u64 = 0;
|
||||
switch ti.size {
|
||||
case 1: data = cast(u64) (^u8)(v.data)^;
|
||||
case 2: data = cast(u64)(^u16)(v.data)^;
|
||||
case 4: data = cast(u64)(^u32)(v.data)^;
|
||||
case 8: data = cast(u64)(^u64)(v.data)^;
|
||||
}
|
||||
|
||||
write_byte(b, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(b, ", ");
|
||||
|
||||
bits := u64(info.bits[i]);
|
||||
offset := u64(info.offsets[i]);
|
||||
marshal_arg(b, name);
|
||||
write_string(b, ": ");
|
||||
|
||||
n := 8*u64(size_of(u64));
|
||||
sa := n - bits;
|
||||
u := data>>offset;
|
||||
u <<= sa;
|
||||
u >>= sa;
|
||||
|
||||
write_u64(b, u, 10);
|
||||
}
|
||||
write_byte(b, '}');
|
||||
|
||||
case Type_Info_Bit_Set:
|
||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||
if ti == nil {
|
||||
@@ -321,24 +288,27 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
bit_data = u64(x);
|
||||
case 16:
|
||||
x := (^u16)(v.data)^;
|
||||
if do_byte_swap do x = bits.byte_swap(x);
|
||||
if do_byte_swap {
|
||||
x = bits.byte_swap(x);
|
||||
}
|
||||
bit_data = u64(x);
|
||||
case 32:
|
||||
x := (^u32)(v.data)^;
|
||||
if do_byte_swap do x = bits.byte_swap(x);
|
||||
if do_byte_swap {
|
||||
x = bits.byte_swap(x);
|
||||
}
|
||||
bit_data = u64(x);
|
||||
case 64:
|
||||
x := (^u64)(v.data)^;
|
||||
if do_byte_swap do x = bits.byte_swap(x);
|
||||
if do_byte_swap {
|
||||
x = bits.byte_swap(x);
|
||||
}
|
||||
bit_data = u64(x);
|
||||
case: panic("unknown bit_size size");
|
||||
}
|
||||
write_u64(b, bit_data);
|
||||
|
||||
|
||||
return .Unsupported_Type;
|
||||
|
||||
case Type_Info_Opaque:
|
||||
return .Unsupported_Type;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,12 @@ Parser :: struct {
|
||||
spec: Specification,
|
||||
allocator: mem.Allocator,
|
||||
unmarshal_data: any,
|
||||
parse_integers: bool,
|
||||
}
|
||||
|
||||
make_parser :: proc(data: []byte, spec := Specification.JSON, allocator := context.allocator) -> Parser {
|
||||
make_parser :: proc(data: []byte, spec := Specification.JSON, parse_integers := false, allocator := context.allocator) -> Parser {
|
||||
p: Parser;
|
||||
p.tok = make_tokenizer(data, spec);
|
||||
p.tok = make_tokenizer(data, spec, parse_integers);
|
||||
p.spec = spec;
|
||||
p.allocator = allocator;
|
||||
assert(p.allocator.procedure != nil);
|
||||
@@ -23,9 +24,9 @@ make_parser :: proc(data: []byte, spec := Specification.JSON, allocator := conte
|
||||
return p;
|
||||
}
|
||||
|
||||
parse :: proc(data: []byte, spec := Specification.JSON, allocator := context.allocator) -> (Value, Error) {
|
||||
parse :: proc(data: []byte, spec := Specification.JSON, parse_integers := false, allocator := context.allocator) -> (Value, Error) {
|
||||
context.allocator = allocator;
|
||||
p := make_parser(data, spec, allocator);
|
||||
p := make_parser(data, spec, parse_integers, allocator);
|
||||
|
||||
if p.spec == Specification.JSON5 {
|
||||
return parse_value(&p);
|
||||
|
||||
@@ -42,12 +42,13 @@ Tokenizer :: struct {
|
||||
w: int, // current rune width in bytes
|
||||
curr_line_offset: int,
|
||||
spec: Specification,
|
||||
parse_integers: bool,
|
||||
}
|
||||
|
||||
|
||||
|
||||
make_tokenizer :: proc(data: []byte, spec := Specification.JSON) -> Tokenizer {
|
||||
t := Tokenizer{pos = {line=1}, data = data, spec = spec};
|
||||
make_tokenizer :: proc(data: []byte, spec := Specification.JSON, parse_integers := false) -> Tokenizer {
|
||||
t := Tokenizer{pos = {line=1}, data = data, spec = spec, parse_integers = parse_integers};
|
||||
next_rune(&t);
|
||||
if t.r == utf8.RUNE_BOM {
|
||||
next_rune(&t);
|
||||
@@ -182,9 +183,11 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
case "false": token.kind = .False;
|
||||
case "true": token.kind = .True;
|
||||
case:
|
||||
if t.spec == .JSON5 do switch str {
|
||||
case "Infinity": token.kind = .Infinity;
|
||||
case "NaN": token.kind = .NaN;
|
||||
if t.spec == .JSON5 {
|
||||
switch str {
|
||||
case "Infinity": token.kind = .Infinity;
|
||||
case "NaN": token.kind = .NaN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,7 +220,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
fallthrough;
|
||||
|
||||
case '0'..'9':
|
||||
token.kind = .Integer;
|
||||
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);
|
||||
@@ -360,7 +363,9 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
s = s[1:];
|
||||
case '1'..'9':
|
||||
s = s[1:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:];
|
||||
}
|
||||
case '.':
|
||||
if spec == .JSON5 { // Allow leading decimal point
|
||||
s = s[1:];
|
||||
@@ -379,7 +384,9 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
|
||||
if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
|
||||
s = s[2:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:];
|
||||
}
|
||||
}
|
||||
|
||||
if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
|
||||
@@ -391,7 +398,9 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:];
|
||||
}
|
||||
}
|
||||
|
||||
// The string should be empty now to be valid
|
||||
|
||||
@@ -24,7 +24,7 @@ Value :: struct {
|
||||
String,
|
||||
Array,
|
||||
Object,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Pos :: struct {
|
||||
@@ -65,7 +65,9 @@ destroy_value :: proc(value: Value) {
|
||||
}
|
||||
delete(v);
|
||||
case Array:
|
||||
for elem in v do destroy_value(elem);
|
||||
for elem in v {
|
||||
destroy_value(elem);
|
||||
}
|
||||
delete(v);
|
||||
case String:
|
||||
delete(v);
|
||||
|
||||
@@ -3,8 +3,8 @@ package json
|
||||
import "core:mem"
|
||||
|
||||
// NOTE(bill): is_valid will not check for duplicate keys
|
||||
is_valid :: proc(data: []byte, spec := Specification.JSON) -> bool {
|
||||
p := make_parser(data, spec, mem.nil_allocator());
|
||||
is_valid :: proc(data: []byte, spec := Specification.JSON, parse_integers := false) -> bool {
|
||||
p := make_parser(data, spec, parse_integers, mem.nil_allocator());
|
||||
if p.spec == Specification.JSON5 {
|
||||
return validate_value(&p);
|
||||
}
|
||||
|
||||
+590
-448
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -1,14 +1,14 @@
|
||||
package hash
|
||||
|
||||
crc32 :: proc(data: []byte) -> u32 {
|
||||
result := ~u32(0);
|
||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
result := ~u32(seed);
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
crc64 :: proc(data: []byte) -> u64 {
|
||||
result := ~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 ~ u64(b)) & 0xff];
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,9 +2,9 @@ package hash
|
||||
|
||||
import "core:mem"
|
||||
|
||||
adler32 :: proc(data: []byte) -> u32 {
|
||||
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 {
|
||||
ADLER_CONST :: 65521;
|
||||
a, b: u32 = 1, 0;
|
||||
a, b: u32 = seed & 0xFFFF, seed >> 16;
|
||||
for x in data {
|
||||
a = (a + u32(x)) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,67 +1,71 @@
|
||||
// This is purely for documentation
|
||||
//+ignore
|
||||
package intrinsics
|
||||
|
||||
// Types
|
||||
|
||||
x86_mmx :: x86_mmx; // Specialized SIMD Vector type
|
||||
|
||||
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_store :: proc(dst: ^$T, val: T) ---
|
||||
atomic_store_rel :: proc(dst: ^$T, val: T) ---
|
||||
atomic_store_relaxed :: proc(dst: ^$T, val: T) ---
|
||||
atomic_store_unordered :: proc(dst: ^$T, val: T) ---
|
||||
|
||||
atomic_load :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_acq :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_relaxed :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_unordered :: proc(dst: ^$T) -> T ---
|
||||
|
||||
atomic_add :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
|
||||
atomic_xchg :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
|
||||
atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
@@ -85,9 +89,14 @@ atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*opti
|
||||
|
||||
// Instructions
|
||||
|
||||
alloca :: proc(size, align: int) -> ^u8 ---
|
||||
alloca :: proc(size, align: int) -> ^u8 ---
|
||||
cpu_relax :: proc() ---
|
||||
read_cycle_counter :: proc() -> i64 ---
|
||||
|
||||
|
||||
// Compiler Hints
|
||||
expect :: proc(val, expected_val: T) -> T ---
|
||||
|
||||
cpu_relax :: proc() ---
|
||||
|
||||
// Constant type tests
|
||||
|
||||
@@ -114,13 +123,12 @@ 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
|
||||
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp (== and !=)
|
||||
type_is_dereferenceable :: proc($T: typeid) -> bool ---
|
||||
type_is_valid_map_key :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_named :: proc($T: typeid) -> bool ---
|
||||
type_is_pointer :: proc($T: typeid) -> bool ---
|
||||
type_is_opaque :: proc($T: typeid) -> bool ---
|
||||
type_is_array :: proc($T: typeid) -> bool ---
|
||||
type_is_enumerated_array :: proc($T: typeid) -> bool ---
|
||||
type_is_slice :: proc($T: typeid) -> bool ---
|
||||
@@ -130,8 +138,6 @@ type_is_struct :: proc($T: typeid) -> bool ---
|
||||
type_is_union :: proc($T: typeid) -> bool ---
|
||||
type_is_enum :: proc($T: typeid) -> bool ---
|
||||
type_is_proc :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_field :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_field_value :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_set :: proc($T: typeid) -> bool ---
|
||||
type_is_simd_vector :: proc($T: typeid) -> bool ---
|
||||
|
||||
@@ -139,6 +145,8 @@ 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) ---
|
||||
|
||||
@@ -147,3 +155,9 @@ type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_i
|
||||
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -43,7 +43,9 @@ create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_F
|
||||
|
||||
destroy_file_logger :: proc(log: ^Logger) {
|
||||
data := cast(^File_Console_Logger_Data)log.data;
|
||||
if data.file_handle != os.INVALID_HANDLE do os.close(data.file_handle);
|
||||
if data.file_handle != os.INVALID_HANDLE {
|
||||
os.close(data.file_handle);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
@@ -75,8 +77,8 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
|
||||
t := time.now();
|
||||
y, m, d := time.date(t);
|
||||
h, min, s := time.clock(t);
|
||||
if .Date in options do fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d);
|
||||
if .Time in options do fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s);
|
||||
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, "] ");
|
||||
}
|
||||
}
|
||||
@@ -89,7 +91,9 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
|
||||
fmt.sbprintf(&buf, "[{}] ", os.current_thread_id());
|
||||
}
|
||||
|
||||
if data.ident != "" do fmt.sbprintf(&buf, "[%s] ", data.ident);
|
||||
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);
|
||||
}
|
||||
@@ -110,14 +114,21 @@ do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
|
||||
}
|
||||
|
||||
if .Level in opts {
|
||||
if .Terminal_Color in opts do fmt.sbprint(str, col);
|
||||
if .Terminal_Color in opts {
|
||||
fmt.sbprint(str, col);
|
||||
}
|
||||
fmt.sbprint(str, Level_Headers[level]);
|
||||
if .Terminal_Color in opts do fmt.sbprint(str, RESET);
|
||||
if .Terminal_Color in opts {
|
||||
fmt.sbprint(str, RESET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_location_header :: proc(opts: Options, buf: ^strings.Builder, location := #caller_location) {
|
||||
if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return;
|
||||
if Location_Header_Opts & opts == nil {
|
||||
return;
|
||||
}
|
||||
fmt.sbprint(buf, "[");
|
||||
|
||||
file := location.file_path;
|
||||
if .Short_File_Path in opts {
|
||||
|
||||
+10
-5
@@ -2,7 +2,6 @@ package log
|
||||
|
||||
import "core:runtime"
|
||||
import "core:fmt"
|
||||
import "core:sync"
|
||||
|
||||
|
||||
// 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.
|
||||
@@ -39,7 +38,7 @@ Options :: bit_set[Option];
|
||||
|
||||
Full_Timestamp_Opts :: Options{
|
||||
.Date,
|
||||
.Time
|
||||
.Time,
|
||||
};
|
||||
Location_Header_Opts :: Options{
|
||||
.Short_File_Path,
|
||||
@@ -49,7 +48,7 @@ Location_Header_Opts :: Options{
|
||||
};
|
||||
Location_File_Opts :: Options{
|
||||
.Short_File_Path,
|
||||
.Long_File_Path
|
||||
.Long_File_Path,
|
||||
};
|
||||
|
||||
|
||||
@@ -111,11 +110,11 @@ fatal :: proc(args: ..any, sep := " ", location := #caller_location) {
|
||||
|
||||
panic :: proc(args: ..any, location := #caller_location) -> ! {
|
||||
log(level=.Fatal, args=args, location=location);
|
||||
runtime.panic("log.panic");
|
||||
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");
|
||||
runtime.panic("log.panicf", location);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +122,9 @@ panicf :: proc(fmt_str: string, args: ..any, location := #caller_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;
|
||||
}
|
||||
@@ -132,6 +134,9 @@ log :: proc(level: Level, args: ..any, sep := " ", location := #caller_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;
|
||||
}
|
||||
|
||||
+129
-4
@@ -65,13 +65,13 @@ leading_zeros_u64 :: proc(i: u64) -> int {
|
||||
|
||||
|
||||
byte_swap_u16 :: proc(x: u16) -> u16 {
|
||||
return u16(runtime.bswap_16(u16(x)));
|
||||
return runtime.bswap_16(x);
|
||||
}
|
||||
byte_swap_u32 :: proc(x: u32) -> u32 {
|
||||
return u32(runtime.bswap_32(u32(x)));
|
||||
return runtime.bswap_32(x);
|
||||
}
|
||||
byte_swap_u64 :: proc(x: u64) -> u64 {
|
||||
return u64(runtime.bswap_64(u64(x)));
|
||||
return runtime.bswap_64(x);
|
||||
}
|
||||
byte_swap_i16 :: proc(x: i16) -> i16 {
|
||||
return i16(runtime.bswap_16(u16(x)));
|
||||
@@ -83,7 +83,7 @@ byte_swap_i64 :: proc(x: i64) -> i64 {
|
||||
return i64(runtime.bswap_64(u64(x)));
|
||||
}
|
||||
byte_swap_u128 :: proc(x: u128) -> u128 {
|
||||
return u128(runtime.bswap_128(u128(x)));
|
||||
return runtime.bswap_128(x);
|
||||
}
|
||||
byte_swap_i128 :: proc(x: i128) -> i128 {
|
||||
return i128(runtime.bswap_128(u128(x)));
|
||||
@@ -529,3 +529,128 @@ len_u8_table := [256]u8{
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
+191
-224
@@ -5,17 +5,51 @@ import "intrinsics"
|
||||
|
||||
// Generic
|
||||
|
||||
TAU :: 6.28318530717958647692528676655900576;
|
||||
PI :: 3.14159265358979323846264338327950288;
|
||||
|
||||
E :: 2.71828182845904523536;
|
||||
|
||||
τ :: TAU;
|
||||
π :: PI;
|
||||
e :: E;
|
||||
|
||||
SQRT_TWO :: 1.41421356237309504880168872420969808;
|
||||
SQRT_THREE :: 1.73205080756887729352744634150587236;
|
||||
SQRT_FIVE :: 2.23606797749978969640917366873127623;
|
||||
|
||||
LN2 :: 0.693147180559945309417232121458176568;
|
||||
LN10 :: 2.30258509299404568401799145468436421;
|
||||
|
||||
MAX_F64_PRECISION :: 16; // Maximum number of meaningful digits after the decimal point for 'f64'
|
||||
MAX_F32_PRECISION :: 8; // Maximum number of meaningful digits after the decimal point for 'f32'
|
||||
|
||||
RAD_PER_DEG :: TAU/360.0;
|
||||
DEG_PER_RAD :: 360.0/TAU;
|
||||
|
||||
|
||||
|
||||
@private IS_NUMERIC :: intrinsics.type_is_numeric;
|
||||
@private IS_QUATERNION :: intrinsics.type_is_quaternion;
|
||||
@private IS_ARRAY :: intrinsics.type_is_array;
|
||||
@private IS_FLOAT :: intrinsics.type_is_float;
|
||||
@private BASE_TYPE :: intrinsics.type_base_type;
|
||||
@private ELEM_TYPE :: intrinsics.type_elem_type;
|
||||
|
||||
|
||||
scalar_dot :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
vector_dot :: proc(a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) {
|
||||
for i in 0..<N {
|
||||
c += a[i] * b[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
quaternion64_dot :: proc(a, b: $T/quaternion64) -> (c: f16) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
quaternion128_dot :: proc(a, b: $T/quaternion128) -> (c: f32) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
@@ -23,13 +57,27 @@ quaternion256_dot :: proc(a, b: $T/quaternion256) -> (c: f64) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
|
||||
dot :: proc{vector_dot, quaternion128_dot, quaternion256_dot};
|
||||
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot};
|
||||
|
||||
inner_product :: dot;
|
||||
outer_product :: proc(a: $A/[$M]$E, b: $B/[$N]E) -> (out: [M][N]E) where IS_NUMERIC(E) {
|
||||
for i in 0..<M {
|
||||
for j in 0..<N {
|
||||
out[i][j] = a[i]*b[j];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
quaternion_inverse :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
scalar_cross :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
|
||||
return a * b;
|
||||
}
|
||||
|
||||
vector_cross2 :: proc(a, b: $T/[2]$E) -> E where IS_NUMERIC(E) {
|
||||
return a[0]*b[1] - b[0]*a[1];
|
||||
}
|
||||
@@ -41,8 +89,16 @@ vector_cross3 :: proc(a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) {
|
||||
return;
|
||||
}
|
||||
|
||||
vector_cross :: proc{vector_cross2, vector_cross3};
|
||||
cross :: vector_cross;
|
||||
quaternion_cross :: proc(q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) {
|
||||
q3.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
|
||||
q3.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z;
|
||||
q3.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x;
|
||||
q3.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
|
||||
return;
|
||||
}
|
||||
|
||||
vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3};
|
||||
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross};
|
||||
|
||||
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
return v / length(v);
|
||||
@@ -79,231 +135,31 @@ quaternion_length2 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return dot(q, q);
|
||||
}
|
||||
|
||||
scalar_triple_product :: proc(a, b, c: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
// a . (b x c)
|
||||
// b . (c x a)
|
||||
// c . (a x b)
|
||||
return dot(a, cross(b, c));
|
||||
}
|
||||
|
||||
vector_triple_product :: proc(a, b, c: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
// a x (b x c)
|
||||
// (a . c)b - (a . b)c
|
||||
return cross(a, cross(b, c));
|
||||
}
|
||||
|
||||
|
||||
length :: proc{vector_length, quaternion_length};
|
||||
length2 :: proc{vector_length2, quaternion_length2};
|
||||
|
||||
|
||||
vector_lerp :: proc(x, y, t: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
ti := t[i];
|
||||
s[i] = x[i]*(1-ti) + y[i]*ti;
|
||||
}
|
||||
return s;
|
||||
projection :: proc(x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
return dot(x, normal) / dot(normal, normal) * normal;
|
||||
}
|
||||
|
||||
vector_unlerp :: proc(a, b, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
ai := a[i];
|
||||
s[i] = (x[i]-ai)/(b[i]-ai);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sin :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sin(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_cos :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.cos(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_tan :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.tan(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
vector_asin :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.asin(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_acos :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.acos(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_atan :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.atan(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_atan2 :: proc(y, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.atan(y[i], x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_pow :: proc(x, y: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.pow(x[i], y[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_expr :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.expr(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sqrt :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sqrt(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_abs :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = abs(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sign :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sign(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_floor :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.floor(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_ceil :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.ceil(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
vector_mod :: proc(x, y: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.mod(x[i], y[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_min :: proc(a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = min(a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_max :: proc(a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = max(a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_clamp :: proc(x, a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = clamp(x[i], a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_mix :: proc(x, y, a: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = x[i]*(1-a[i]) + y[i]*a[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_step :: proc(edge, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = 0 if x[i] < edge[i] else 1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_smoothstep :: proc(edge0, edge1, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
e0, e1 := edge0[i], edge1[i];
|
||||
t := clamp((x[i] - e0) / (e1 - e0), 0, 1);
|
||||
s[i] = t * t * (3 - 2*t);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_smootherstep :: proc(edge0, edge1, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
e0, e1 := edge0[i], edge1[i];
|
||||
t := clamp((x[i] - e0) / (e1 - e0), 0, 1);
|
||||
s[i] = t * t * t * (t * (6*t - 15) + 10);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_distance :: proc(p0, p1: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
return length(p1 - p0);
|
||||
}
|
||||
|
||||
vector_reflect :: proc(i, n: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
b := n * (2 * dot(n, i));
|
||||
return i - b;
|
||||
}
|
||||
|
||||
vector_refract :: proc(i, n: $V/[$N]$E, eta: E) -> V where IS_NUMERIC(E) {
|
||||
dv := dot(n, i);
|
||||
k := 1 - eta*eta - (1 - dv*dv);
|
||||
a := i * eta;
|
||||
b := n * eta*dv*math.sqrt(k);
|
||||
return (a - b) * E(int(k >= 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
identity :: proc($T: typeid/[$N][N]$E) -> (m: T) {
|
||||
for i in 0..<N do m[i][i] = E(1);
|
||||
for i in 0..<N {
|
||||
m[i][i] = E(1);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -337,6 +193,17 @@ matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
|
||||
return;
|
||||
}
|
||||
|
||||
matrix_comp_mul :: proc(a, b: $M/[$J][$I]$E) -> (c: M)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E) {
|
||||
for j in 0..<J {
|
||||
for i in 0..<I {
|
||||
c[j][i] = a[j][i] * b[j][i];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
matrix_mul_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (c: [K][I]E)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E),
|
||||
@@ -363,6 +230,19 @@ matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
|
||||
return;
|
||||
}
|
||||
|
||||
quaternion_mul_quaternion :: proc(q1, q2: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return q1 * q2;
|
||||
}
|
||||
|
||||
quaternion64_mul_vector3 :: proc(q: $Q/quaternion64, v: $V/[3]$F/f16) -> V {
|
||||
Raw_Quaternion :: struct {xyz: [3]f16, r: f16};
|
||||
|
||||
q := transmute(Raw_Quaternion)q;
|
||||
v := transmute([3]f16)v;
|
||||
|
||||
t := cross(2*q.xyz, v);
|
||||
return V(v + q.r*t + cross(q.xyz, t));
|
||||
}
|
||||
quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
|
||||
Raw_Quaternion :: struct {xyz: [3]f32, r: f32};
|
||||
|
||||
@@ -372,7 +252,6 @@ quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
|
||||
t := cross(2*q.xyz, v);
|
||||
return V(v + q.r*t + cross(q.xyz, t));
|
||||
}
|
||||
|
||||
quaternion256_mul_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
|
||||
Raw_Quaternion :: struct {xyz: [3]f64, r: f64};
|
||||
|
||||
@@ -382,14 +261,16 @@ quaternion256_mul_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
|
||||
t := cross(2*q.xyz, v);
|
||||
return V(v + q.r*t + cross(q.xyz, t));
|
||||
}
|
||||
quaternion_mul_vector3 :: proc{quaternion128_mul_vector3, quaternion256_mul_vector3};
|
||||
quaternion_mul_vector3 :: proc{quaternion64_mul_vector3, quaternion128_mul_vector3, quaternion256_mul_vector3};
|
||||
|
||||
mul :: proc{
|
||||
matrix_mul,
|
||||
matrix_mul_differ,
|
||||
matrix_mul_vector,
|
||||
quaternion64_mul_vector3,
|
||||
quaternion128_mul_vector3,
|
||||
quaternion256_mul_vector3,
|
||||
quaternion_mul_quaternion,
|
||||
};
|
||||
|
||||
vector_to_ptr :: proc(v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E), N > 0 #no_bounds_check {
|
||||
@@ -399,3 +280,89 @@ matrix_to_ptr :: proc(m: ^$A/[$I][$J]$E) -> ^E where IS_NUMERIC(E), I > 0, J > 0
|
||||
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); }
|
||||
|
||||
+2210
-303
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,
|
||||
};
|
||||
+266
-60
@@ -10,7 +10,7 @@ Float_Class :: enum {
|
||||
Neg_Zero, // the negative zero
|
||||
NaN, // Not-A-Number (NaN)
|
||||
Inf, // positive infinity
|
||||
Neg_Inf // negative infinity
|
||||
Neg_Inf, // negative infinity
|
||||
};
|
||||
|
||||
TAU :: 6.28318530717958647692528676655900576;
|
||||
@@ -31,6 +31,7 @@ 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'
|
||||
MAX_F16_PRECISION :: 4; // Maximum number of meaningful digits after the decimal point for 'f16'
|
||||
|
||||
RAD_PER_DEG :: TAU/360.0;
|
||||
DEG_PER_RAD :: 360.0/TAU;
|
||||
@@ -38,80 +39,101 @@ DEG_PER_RAD :: 360.0/TAU;
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign _ {
|
||||
@(link_name="llvm.sqrt.f16")
|
||||
sqrt_f16 :: proc(x: f16) -> f16 ---;
|
||||
@(link_name="llvm.sqrt.f32")
|
||||
sqrt_f32 :: proc(x: f32) -> f32 ---;
|
||||
@(link_name="llvm.sqrt.f64")
|
||||
sqrt_f64 :: proc(x: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.sin.f16")
|
||||
sin_f16 :: proc(θ: f16) -> f16 ---;
|
||||
@(link_name="llvm.sin.f32")
|
||||
sin_f32 :: proc(θ: f32) -> f32 ---;
|
||||
@(link_name="llvm.sin.f64")
|
||||
sin_f64 :: proc(θ: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.cos.f16")
|
||||
cos_f16 :: proc(θ: f16) -> f16 ---;
|
||||
@(link_name="llvm.cos.f32")
|
||||
cos_f32 :: proc(θ: f32) -> f32 ---;
|
||||
@(link_name="llvm.cos.f64")
|
||||
cos_f64 :: proc(θ: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.pow.f16")
|
||||
pow_f16 :: proc(x, power: f16) -> f16 ---;
|
||||
@(link_name="llvm.pow.f32")
|
||||
pow_f32 :: proc(x, power: f32) -> f32 ---;
|
||||
@(link_name="llvm.pow.f64")
|
||||
pow_f64 :: proc(x, power: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.fmuladd.f16")
|
||||
fmuladd_f16 :: proc(a, b, c: f16) -> f16 ---;
|
||||
@(link_name="llvm.fmuladd.f32")
|
||||
fmuladd_f32 :: proc(a, b, c: f32) -> f32 ---;
|
||||
@(link_name="llvm.fmuladd.f64")
|
||||
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.log.f16")
|
||||
ln_f16 :: proc(x: f16) -> f16 ---;
|
||||
@(link_name="llvm.log.f32")
|
||||
ln_f32 :: proc(x: f32) -> f32 ---;
|
||||
@(link_name="llvm.log.f64")
|
||||
ln_f64 :: proc(x: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.exp.f16")
|
||||
exp_f16 :: proc(x: f16) -> f16 ---;
|
||||
@(link_name="llvm.exp.f32")
|
||||
exp_f32 :: proc(x: f32) -> f32 ---;
|
||||
@(link_name="llvm.exp.f64")
|
||||
exp_f64 :: proc(x: f64) -> f64 ---;
|
||||
|
||||
@(link_name="llvm.ldexp.f16")
|
||||
ldexp_f16 :: proc(val: f16, exp: i32) -> f16 ---;
|
||||
@(link_name="llvm.ldexp.f32")
|
||||
ldexp_f32 :: proc(val: f32, exp: i32) -> f32 ---;
|
||||
|
||||
@(link_name="llvm.ldexp.f64")
|
||||
ldexp_f64 :: proc(val: f64, exp: i32) -> f64 ---;
|
||||
}
|
||||
|
||||
sqrt :: proc{sqrt_f32, sqrt_f64};
|
||||
sin :: proc{sin_f32, sin_f64};
|
||||
cos :: proc{cos_f32, cos_f64};
|
||||
pow :: proc{pow_f32, pow_f64};
|
||||
fmuladd :: proc{fmuladd_f32, fmuladd_f64};
|
||||
ln :: proc{ln_f32, ln_f64};
|
||||
exp :: proc{exp_f32, exp_f64};
|
||||
sqrt :: proc{sqrt_f16, sqrt_f32, sqrt_f64};
|
||||
sin :: proc{sin_f16, sin_f32, sin_f64};
|
||||
cos :: proc{cos_f16, cos_f32, cos_f64};
|
||||
pow :: proc{pow_f16, pow_f32, pow_f64};
|
||||
fmuladd :: proc{fmuladd_f16, fmuladd_f32, fmuladd_f64};
|
||||
ln :: proc{ln_f16, ln_f32, ln_f64};
|
||||
exp :: proc{exp_f16, exp_f32, exp_f64};
|
||||
|
||||
ldexp :: proc{ldexp_f32, ldexp_f64};
|
||||
ldexp :: proc{ldexp_f16, ldexp_f32, ldexp_f64};
|
||||
|
||||
log_f16 :: proc(x, base: f16) -> f16 { return ln(x) / ln(base); }
|
||||
log_f32 :: proc(x, base: f32) -> f32 { return ln(x) / ln(base); }
|
||||
log_f64 :: proc(x, base: f64) -> f64 { return ln(x) / ln(base); }
|
||||
log :: proc{log_f32, log_f64};
|
||||
log :: proc{log_f16, log_f32, log_f64};
|
||||
|
||||
log2_f16 :: proc(x: f16) -> f16 { return ln(x)/LN2; }
|
||||
log2_f32 :: proc(x: f32) -> f32 { return ln(x)/LN2; }
|
||||
log2_f64 :: proc(x: f64) -> f64 { return ln(x)/LN2; }
|
||||
log2 :: proc{log2_f32, log2_f64};
|
||||
log2 :: proc{log2_f16, log2_f32, log2_f64};
|
||||
|
||||
log10_f16 :: proc(x: f16) -> f16 { return ln(x)/LN10; }
|
||||
log10_f32 :: proc(x: f32) -> f32 { return ln(x)/LN10; }
|
||||
log10_f64 :: proc(x: f64) -> f64 { return ln(x)/LN10; }
|
||||
log10 :: proc{log10_f32, log10_f64};
|
||||
log10 :: proc{log10_f16, log10_f32, log10_f64};
|
||||
|
||||
|
||||
tan_f32 :: proc "c" (θ: f32) -> f32 { return sin(θ)/cos(θ); }
|
||||
tan_f64 :: proc "c" (θ: f64) -> f64 { return sin(θ)/cos(θ); }
|
||||
tan :: proc{tan_f32, tan_f64};
|
||||
tan_f16 :: proc(θ: f16) -> f16 { return sin(θ)/cos(θ); }
|
||||
tan_f32 :: proc(θ: f32) -> f32 { return sin(θ)/cos(θ); }
|
||||
tan_f64 :: proc(θ: f64) -> f64 { return sin(θ)/cos(θ); }
|
||||
tan :: proc{tan_f16, tan_f32, tan_f64};
|
||||
|
||||
lerp :: proc(a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t; }
|
||||
saturate :: proc(a: $T) -> (x: T) { return clamp(a, 0, 1); };
|
||||
|
||||
unlerp_f16 :: proc(a, b, x: f16) -> (t: f16) { return (x-a)/(b-a); }
|
||||
unlerp_f32 :: proc(a, b, x: f32) -> (t: f32) { return (x-a)/(b-a); }
|
||||
unlerp_f64 :: proc(a, b, x: f64) -> (t: f64) { return (x-a)/(b-a); }
|
||||
unlerp :: proc{unlerp_f32, unlerp_f64};
|
||||
unlerp :: proc{unlerp_f16, unlerp_f32, unlerp_f64};
|
||||
|
||||
|
||||
wrap :: proc(x, y: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
|
||||
@@ -148,18 +170,30 @@ gain :: proc(t, g: $T) -> T where intrinsics.type_is_numeric(T) {
|
||||
}
|
||||
|
||||
|
||||
sign_f16 :: proc(x: f16) -> f16 { return f16(int(0 < x) - int(x < 0)); }
|
||||
sign_f32 :: proc(x: f32) -> f32 { return f32(int(0 < x) - int(x < 0)); }
|
||||
sign_f64 :: proc(x: f64) -> f64 { return f64(int(0 < x) - int(x < 0)); }
|
||||
sign :: proc{sign_f32, sign_f64};
|
||||
sign :: proc{sign_f16, sign_f32, sign_f64};
|
||||
|
||||
|
||||
sign_bit_f16 :: proc(x: f16) -> bool {
|
||||
return (transmute(u16)x) & (1<<15) != 0;
|
||||
}
|
||||
sign_bit_f32 :: proc(x: f32) -> bool {
|
||||
return (transmute(u32)x) & (1<<31) != 0;
|
||||
}
|
||||
sign_bit_f64 :: proc(x: f64) -> bool {
|
||||
return (transmute(u64)x) & (1<<63) != 0;
|
||||
}
|
||||
sign_bit :: proc{sign_bit_f32, sign_bit_f64};
|
||||
sign_bit :: proc{sign_bit_f16, sign_bit_f32, sign_bit_f64};
|
||||
|
||||
copy_sign_f16 :: proc(x, y: f16) -> f16 {
|
||||
ix := transmute(u16)x;
|
||||
iy := transmute(u16)y;
|
||||
ix &= 0x7fff;
|
||||
ix |= iy & 0x8000;
|
||||
return transmute(f16)ix;
|
||||
}
|
||||
copy_sign_f32 :: proc(x, y: f32) -> f32 {
|
||||
ix := transmute(u32)x;
|
||||
iy := transmute(u32)y;
|
||||
@@ -174,15 +208,47 @@ copy_sign_f64 :: proc(x, y: f64) -> f64 {
|
||||
ix |= iy & 0x8000_0000_0000_0000;
|
||||
return transmute(f64)ix;
|
||||
}
|
||||
copy_sign :: proc{copy_sign_f32, copy_sign_f64};
|
||||
copy_sign :: proc{copy_sign_f16, copy_sign_f32, copy_sign_f64};
|
||||
|
||||
|
||||
to_radians_f16 :: proc(degrees: f16) -> f16 { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f32 :: proc(degrees: f32) -> f32 { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f64 :: proc(degrees: f64) -> f64 { return degrees * RAD_PER_DEG; }
|
||||
to_degrees_f16 :: proc(radians: f16) -> f16 { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f32 :: proc(radians: f32) -> f32 { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f64 :: proc(radians: f64) -> f64 { return radians * DEG_PER_RAD; }
|
||||
to_radians :: proc{to_radians_f32, to_radians_f64};
|
||||
to_degrees :: proc{to_degrees_f32, to_degrees_f64};
|
||||
to_radians :: proc{to_radians_f16, to_radians_f32, to_radians_f64};
|
||||
to_degrees :: proc{to_degrees_f16, to_degrees_f32, to_degrees_f64};
|
||||
|
||||
trunc_f16 :: proc(x: f16) -> f16 {
|
||||
trunc_internal :: proc(f: f16) -> f16 {
|
||||
mask :: 0x1f;
|
||||
shift :: 16 - 6;
|
||||
bias :: 0xf;
|
||||
|
||||
if f < 1 {
|
||||
switch {
|
||||
case f < 0: return -trunc_internal(-f);
|
||||
case f == 0: return f;
|
||||
case: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
x := transmute(u16)f;
|
||||
e := (x >> shift) & mask - bias;
|
||||
|
||||
if e < shift {
|
||||
x &= ~(1 << (shift-e)) - 1;
|
||||
}
|
||||
return transmute(f16)x;
|
||||
}
|
||||
switch classify(x) {
|
||||
case .Zero, .Neg_Zero, .NaN, .Inf, .Neg_Inf:
|
||||
return x;
|
||||
case .Normal, .Subnormal: // carry on
|
||||
}
|
||||
return trunc_internal(x);
|
||||
}
|
||||
|
||||
trunc_f32 :: proc(x: f32) -> f32 {
|
||||
trunc_internal :: proc(f: f32) -> f32 {
|
||||
@@ -244,21 +310,39 @@ trunc_f64 :: proc(x: f64) -> f64 {
|
||||
return trunc_internal(x);
|
||||
}
|
||||
|
||||
trunc :: proc{trunc_f32, trunc_f64};
|
||||
trunc :: proc{trunc_f16, trunc_f32, trunc_f64};
|
||||
|
||||
round_f16 :: proc(x: f16) -> f16 {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round_f32 :: proc(x: f32) -> f32 {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round_f64 :: proc(x: f64) -> f64 {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round :: proc{round_f32, round_f64};
|
||||
round :: proc{round_f16, round_f32, round_f64};
|
||||
|
||||
|
||||
ceil_f16 :: proc(x: f16) -> f16 { return -floor(-x); }
|
||||
ceil_f32 :: proc(x: f32) -> f32 { return -floor(-x); }
|
||||
ceil_f64 :: proc(x: f64) -> f64 { return -floor(-x); }
|
||||
ceil :: proc{ceil_f32, ceil_f64};
|
||||
ceil :: proc{ceil_f16, ceil_f32, ceil_f64};
|
||||
|
||||
floor_f16 :: proc(x: f16) -> f16 {
|
||||
if x == 0 || is_nan(x) || is_inf(x) {
|
||||
return x;
|
||||
}
|
||||
if x < 0 {
|
||||
d, fract := modf(-x);
|
||||
if fract != 0.0 {
|
||||
d = d + 1;
|
||||
}
|
||||
return -d;
|
||||
}
|
||||
d, _ := modf(x);
|
||||
return d;
|
||||
}
|
||||
floor_f32 :: proc(x: f32) -> f32 {
|
||||
if x == 0 || is_nan(x) || is_inf(x) {
|
||||
return x;
|
||||
@@ -287,7 +371,7 @@ floor_f64 :: proc(x: f64) -> f64 {
|
||||
d, _ := modf(x);
|
||||
return d;
|
||||
}
|
||||
floor :: proc{floor_f32, floor_f64};
|
||||
floor :: proc{floor_f16, floor_f32, floor_f64};
|
||||
|
||||
|
||||
floor_div :: proc(x, y: $T) -> T
|
||||
@@ -309,7 +393,32 @@ floor_mod :: proc(x, y: $T) -> T
|
||||
return r;
|
||||
}
|
||||
|
||||
modf_f16 :: proc(x: f16) -> (int: f16, frac: f16) {
|
||||
shift :: 16 - 5 - 1;
|
||||
mask :: 0x1f;
|
||||
bias :: 15;
|
||||
|
||||
if x < 1 {
|
||||
switch {
|
||||
case x < 0:
|
||||
int, frac = modf(-x);
|
||||
return -int, -frac;
|
||||
case x == 0:
|
||||
return x, x;
|
||||
}
|
||||
return 0, x;
|
||||
}
|
||||
|
||||
i := transmute(u16)x;
|
||||
e := uint(i>>shift)&mask - bias;
|
||||
|
||||
if e < shift {
|
||||
i &~= 1<<(shift-e) - 1;
|
||||
}
|
||||
int = transmute(f16)i;
|
||||
frac = x - int;
|
||||
return;
|
||||
}
|
||||
modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) {
|
||||
shift :: 32 - 8 - 1;
|
||||
mask :: 0xff;
|
||||
@@ -362,9 +471,17 @@ modf_f64 :: proc(x: f64) -> (int: f64, frac: f64) {
|
||||
frac = x - int;
|
||||
return;
|
||||
}
|
||||
modf :: proc{modf_f32, modf_f64};
|
||||
modf :: proc{modf_f16, modf_f32, modf_f64};
|
||||
split_decimal :: modf;
|
||||
|
||||
mod_f16 :: proc(x, y: f16) -> (n: f16) {
|
||||
z := abs(y);
|
||||
n = remainder(abs(x), z);
|
||||
if sign(n) < 0 {
|
||||
n += z;
|
||||
}
|
||||
return copy_sign(n, x);
|
||||
}
|
||||
mod_f32 :: proc(x, y: f32) -> (n: f32) {
|
||||
z := abs(y);
|
||||
n = remainder(abs(x), z);
|
||||
@@ -381,11 +498,12 @@ mod_f64 :: proc(x, y: f64) -> (n: f64) {
|
||||
}
|
||||
return copy_sign(n, x);
|
||||
}
|
||||
mod :: proc{mod_f32, mod_f64};
|
||||
mod :: proc{mod_f16, mod_f32, mod_f64};
|
||||
|
||||
remainder_f16 :: proc(x, y: f16) -> f16 { return x - round(x/y) * y; }
|
||||
remainder_f32 :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; }
|
||||
remainder_f64 :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; }
|
||||
remainder :: proc{remainder_f32, remainder_f64};
|
||||
remainder :: proc{remainder_f16, remainder_f32, remainder_f64};
|
||||
|
||||
|
||||
|
||||
@@ -404,25 +522,13 @@ lcm :: proc(x, y: $T) -> T
|
||||
return x / gcd(x, y) * y;
|
||||
}
|
||||
|
||||
frexp_f16 :: proc(x: f16) -> (significand: f16, exponent: int) {
|
||||
f, e := frexp_f64(f64(x));
|
||||
return f16(f), e;
|
||||
}
|
||||
frexp_f32 :: proc(x: f32) -> (significand: f32, exponent: int) {
|
||||
switch {
|
||||
case x == 0:
|
||||
return 0, 0;
|
||||
case x < 0:
|
||||
significand, exponent = frexp(-x);
|
||||
return -significand, exponent;
|
||||
}
|
||||
ex := trunc(log2(x));
|
||||
exponent = int(ex);
|
||||
significand = x / pow(2.0, ex);
|
||||
if abs(significand) >= 1 {
|
||||
exponent += 1;
|
||||
significand /= 2;
|
||||
}
|
||||
if exponent == 1024 && significand == 0 {
|
||||
significand = 0.99999999999999988898;
|
||||
}
|
||||
return;
|
||||
f, e := frexp_f64(f64(x));
|
||||
return f32(f), e;
|
||||
}
|
||||
frexp_f64 :: proc(x: f64) -> (significand: f64, exponent: int) {
|
||||
switch {
|
||||
@@ -444,7 +550,7 @@ frexp_f64 :: proc(x: f64) -> (significand: f64, exponent: int) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
frexp :: proc{frexp_f32, frexp_f64};
|
||||
frexp :: proc{frexp_f16, frexp_f32, frexp_f64};
|
||||
|
||||
|
||||
|
||||
@@ -507,9 +613,33 @@ factorial :: proc(n: int) -> int {
|
||||
|
||||
assert(n >= 0, "parameter must not be negative");
|
||||
assert(n < len(table), "parameter is too large to lookup in the table");
|
||||
return 0;
|
||||
return table[n];
|
||||
}
|
||||
|
||||
classify_f16 :: proc(x: f16) -> Float_Class {
|
||||
switch {
|
||||
case x == 0:
|
||||
i := transmute(i16)x;
|
||||
if i < 0 {
|
||||
return .Neg_Zero;
|
||||
}
|
||||
return .Zero;
|
||||
case x*0.5 == x:
|
||||
if x < 0 {
|
||||
return .Neg_Inf;
|
||||
}
|
||||
return .Inf;
|
||||
case !(x == x):
|
||||
return .NaN;
|
||||
}
|
||||
|
||||
u := transmute(u16)x;
|
||||
exp := int(u>>10) & (1<<5 - 1);
|
||||
if exp == 0 {
|
||||
return .Subnormal;
|
||||
}
|
||||
return .Normal;
|
||||
}
|
||||
classify_f32 :: proc(x: f32) -> Float_Class {
|
||||
switch {
|
||||
case x == 0:
|
||||
@@ -557,17 +687,28 @@ classify_f64 :: proc(x: f64) -> Float_Class {
|
||||
}
|
||||
return .Normal;
|
||||
}
|
||||
classify :: proc{classify_f32, classify_f64};
|
||||
classify :: proc{classify_f16, classify_f32, classify_f64};
|
||||
|
||||
is_nan_f16 :: proc(x: f16) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f32 :: proc(x: f32) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f64 :: proc(x: f64) -> bool { return classify(x) == .NaN; }
|
||||
is_nan :: proc{is_nan_f32, is_nan_f64};
|
||||
is_nan :: proc{is_nan_f16, is_nan_f32, is_nan_f64};
|
||||
|
||||
|
||||
// is_inf reports whether f is an infinity, according to sign.
|
||||
// If sign > 0, is_inf reports whether f is positive infinity.
|
||||
// If sign < 0, is_inf reports whether f is negative infinity.
|
||||
// If sign == 0, is_inf reports whether f is either infinity.
|
||||
is_inf_f16 :: proc(x: f16, sign: int = 0) -> bool {
|
||||
class := classify(abs(x));
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf;
|
||||
case sign < 0:
|
||||
return class == .Neg_Inf;
|
||||
}
|
||||
return class == .Inf || class == .Neg_Inf;
|
||||
}
|
||||
is_inf_f32 :: proc(x: f32, sign: int = 0) -> bool {
|
||||
class := classify(abs(x));
|
||||
switch {
|
||||
@@ -588,7 +729,35 @@ is_inf_f64 :: proc(x: f64, sign: int = 0) -> bool {
|
||||
}
|
||||
return class == .Inf || class == .Neg_Inf;
|
||||
}
|
||||
is_inf :: proc{is_inf_f32, is_inf_f64};
|
||||
is_inf :: proc{is_inf_f16, is_inf_f32, is_inf_f64};
|
||||
|
||||
|
||||
inf_f16 :: proc(sign: int) -> f16 {
|
||||
return f16(inf_f16(sign));
|
||||
}
|
||||
inf_f32 :: proc(sign: int) -> f32 {
|
||||
return f32(inf_f64(sign));
|
||||
}
|
||||
inf_f64 :: proc(sign: int) -> f64 {
|
||||
v: u64;
|
||||
if sign >= 0 {
|
||||
v = 0x7ff00000_00000000;
|
||||
} else {
|
||||
v = 0xfff00000_00000000;
|
||||
}
|
||||
return transmute(f64)v;
|
||||
}
|
||||
|
||||
nan_f16 :: proc() -> f16 {
|
||||
return f16(nan_f64());
|
||||
}
|
||||
nan_f32 :: proc() -> f32 {
|
||||
return f32(nan_f64());
|
||||
}
|
||||
nan_f64 :: proc() -> f64 {
|
||||
v: u64 = 0x7ff80000_00000001;
|
||||
return transmute(f64)v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -647,7 +816,10 @@ cumsum :: proc(dst, src: $T/[]$E) -> T
|
||||
}
|
||||
|
||||
|
||||
|
||||
atan2_f16 :: proc(y, x: f16) -> f16 {
|
||||
// TODO(bill): Better atan2_f16
|
||||
return f16(atan2_f64(f64(y), f64(x)));
|
||||
}
|
||||
atan2_f32 :: proc(y, x: f32) -> f32 {
|
||||
// TODO(bill): Better atan2_f32
|
||||
return f32(atan2_f64(f64(y), f64(x)));
|
||||
@@ -740,49 +912,68 @@ atan2_f64 :: proc(y, x: f64) -> f64 {
|
||||
}
|
||||
|
||||
|
||||
atan2 :: proc{atan2_f32, atan2_f64};
|
||||
atan2 :: proc{atan2_f16, atan2_f32, atan2_f64};
|
||||
|
||||
atan_f16 :: proc(x: f16) -> f16 {
|
||||
return atan2_f16(x, 1);
|
||||
}
|
||||
atan_f32 :: proc(x: f32) -> f32 {
|
||||
return atan2_f32(x, 1);
|
||||
}
|
||||
atan_f64 :: proc(x: f64) -> f64 {
|
||||
return atan2_f64(x, 1);
|
||||
}
|
||||
atan :: proc{atan_f32, atan_f64};
|
||||
atan :: proc{atan_f16, atan_f32, atan_f64};
|
||||
|
||||
asin_f16 :: proc(x: f16) -> f16 {
|
||||
return atan2_f16(x, 1 + sqrt_f16(1 - x*x));
|
||||
}
|
||||
asin_f32 :: proc(x: f32) -> f32 {
|
||||
return atan2_f32(x, 1 + sqrt_f32(1 - x*x));
|
||||
}
|
||||
asin_f64 :: proc(x: f64) -> f64 {
|
||||
return atan2_f64(x, 1 + sqrt_f64(1 - x*x));
|
||||
}
|
||||
asin :: proc{asin_f32, asin_f64};
|
||||
asin :: proc{asin_f16, asin_f32, asin_f64};
|
||||
|
||||
acos_f16 :: proc(x: f16) -> f16 {
|
||||
return 2 * atan2_f16(sqrt_f16(1 - x), sqrt_f16(1 + x));
|
||||
}
|
||||
acos_f32 :: proc(x: f32) -> f32 {
|
||||
return 2 * atan2_f32(sqrt_f32(1 - x), sqrt_f32(1 + x));
|
||||
}
|
||||
acos_f64 :: proc(x: f64) -> f64 {
|
||||
return 2 * atan2_f64(sqrt_f64(1 - x), sqrt_f64(1 + x));
|
||||
}
|
||||
acos :: proc{acos_f32, acos_f64};
|
||||
acos :: proc{acos_f16, acos_f32, acos_f64};
|
||||
|
||||
|
||||
sinh_f16 :: proc(x: f16) -> f16 {
|
||||
return (exp(x) - exp(-x))*0.5;
|
||||
}
|
||||
sinh_f32 :: proc(x: f32) -> f32 {
|
||||
return (exp(x) - exp(-x))*0.5;
|
||||
}
|
||||
sinh_f64 :: proc(x: f64) -> f64 {
|
||||
return (exp(x) - exp(-x))*0.5;
|
||||
}
|
||||
sinh :: proc{sinh_f32, sinh_f64};
|
||||
sinh :: proc{sinh_f16, sinh_f32, sinh_f64};
|
||||
|
||||
cosh_f16 :: proc(x: f16) -> f16 {
|
||||
return (exp(x) + exp(-x))*0.5;
|
||||
}
|
||||
cosh_f32 :: proc(x: f32) -> f32 {
|
||||
return (exp(x) + exp(-x))*0.5;
|
||||
}
|
||||
cosh_f64 :: proc(x: f64) -> f64 {
|
||||
return (exp(x) + exp(-x))*0.5;
|
||||
}
|
||||
cosh :: proc{cosh_f32, cosh_f64};
|
||||
cosh :: proc{cosh_f16, cosh_f32, cosh_f64};
|
||||
|
||||
tanh_f16 :: proc(x: f16) -> f16 {
|
||||
t := exp(2*x);
|
||||
return (t - 1) / (t + 1);
|
||||
}
|
||||
tanh_f32 :: proc(x: f32) -> f32 {
|
||||
t := exp(2*x);
|
||||
return (t - 1) / (t + 1);
|
||||
@@ -791,7 +982,22 @@ tanh_f64 :: proc(x: f64) -> f64 {
|
||||
t := exp(2*x);
|
||||
return (t - 1) / (t + 1);
|
||||
}
|
||||
tanh :: proc{tanh_f32, tanh_f64};
|
||||
tanh :: proc{tanh_f16, tanh_f32, tanh_f64};
|
||||
|
||||
|
||||
F16_DIG :: 3;
|
||||
F16_EPSILON :: 0.00097656;
|
||||
F16_GUARD :: 0;
|
||||
F16_MANT_DIG :: 11;
|
||||
F16_MAX :: 65504.0;
|
||||
F16_MAX_10_EXP :: 4;
|
||||
F16_MAX_EXP :: 15;
|
||||
F16_MIN :: 6.10351562e-5;
|
||||
F16_MIN_10_EXP :: -4;
|
||||
F16_MIN_EXP :: -14;
|
||||
F16_NORMALIZE :: 0;
|
||||
F16_RADIX :: 2;
|
||||
F16_ROUNDS :: 1;
|
||||
|
||||
|
||||
F32_DIG :: 6;
|
||||
|
||||
@@ -17,7 +17,7 @@ import "core:math"
|
||||
//
|
||||
// sample = norm_float64() * std_dev + mean
|
||||
//
|
||||
norm_float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 {
|
||||
norm_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
rn :: 3.442619855899;
|
||||
|
||||
@(static)
|
||||
@@ -116,6 +116,12 @@ norm_float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 {
|
||||
0.008624485, 0.005548995, 0.0026696292,
|
||||
};
|
||||
|
||||
r := r;
|
||||
if r == nil {
|
||||
// NOTE(bill, 2020-09-07): Do this so that people can
|
||||
// enforce the global random state if necessary with `nil`
|
||||
r = &global_rand;
|
||||
}
|
||||
|
||||
for {
|
||||
j := i32(uint32(r));
|
||||
|
||||
+40
-26
@@ -10,11 +10,9 @@ Rand :: struct {
|
||||
_GLOBAL_SEED_DATA := 1234567890;
|
||||
@(private, static)
|
||||
global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)));
|
||||
@(private, static)
|
||||
global_rand_ptr := &global_rand;
|
||||
|
||||
set_global_seed :: proc(seed: u64) {
|
||||
init(global_rand_ptr, seed);
|
||||
init(&global_rand, seed);
|
||||
}
|
||||
|
||||
create :: proc(seed: u64) -> Rand {
|
||||
@@ -32,6 +30,12 @@ init :: proc(r: ^Rand, seed: u64) {
|
||||
}
|
||||
|
||||
_random :: proc(r: ^Rand) -> u32 {
|
||||
r := r;
|
||||
if r == nil {
|
||||
// NOTE(bill, 2020-09-07): Do this so that people can
|
||||
// enforce the global random state if necessary with `nil`
|
||||
r = &global_rand;
|
||||
}
|
||||
old_state := r.state;
|
||||
r.state = old_state * 6364136223846793005 + (r.inc|1);
|
||||
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27);
|
||||
@@ -39,15 +43,15 @@ _random :: proc(r: ^Rand) -> u32 {
|
||||
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31));
|
||||
}
|
||||
|
||||
uint32 :: proc(r: ^Rand = global_rand_ptr) -> u32 { return _random(r); }
|
||||
uint32 :: proc(r: ^Rand = nil) -> u32 { return _random(r); }
|
||||
|
||||
uint64 :: proc(r: ^Rand = global_rand_ptr) -> u64 {
|
||||
uint64 :: proc(r: ^Rand = nil) -> u64 {
|
||||
a := u64(_random(r));
|
||||
b := u64(_random(r));
|
||||
return (a<<32) | b;
|
||||
}
|
||||
|
||||
uint128 :: proc(r: ^Rand = global_rand_ptr) -> u128 {
|
||||
uint128 :: proc(r: ^Rand = nil) -> u128 {
|
||||
a := u128(_random(r));
|
||||
b := u128(_random(r));
|
||||
c := u128(_random(r));
|
||||
@@ -55,12 +59,14 @@ uint128 :: proc(r: ^Rand = global_rand_ptr) -> u128 {
|
||||
return (a<<96) | (b<<64) | (c<<32) | d;
|
||||
}
|
||||
|
||||
int31 :: proc(r: ^Rand = global_rand_ptr) -> i32 { return i32(uint32(r) << 1 >> 1); }
|
||||
int63 :: proc(r: ^Rand = global_rand_ptr) -> i64 { return i64(uint64(r) << 1 >> 1); }
|
||||
int127 :: proc(r: ^Rand = global_rand_ptr) -> i128 { return i128(uint128(r) << 1 >> 1); }
|
||||
int31 :: proc(r: ^Rand = nil) -> i32 { return i32(uint32(r) << 1 >> 1); }
|
||||
int63 :: proc(r: ^Rand = nil) -> i64 { return i64(uint64(r) << 1 >> 1); }
|
||||
int127 :: proc(r: ^Rand = nil) -> i128 { return i128(uint128(r) << 1 >> 1); }
|
||||
|
||||
int31_max :: proc(n: i32, r: ^Rand = global_rand_ptr) -> i32 {
|
||||
if n <= 0 do panic("Invalid argument to int31_max");
|
||||
int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int31_max");
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int31(r) & (n-1);
|
||||
}
|
||||
@@ -72,8 +78,10 @@ int31_max :: proc(n: i32, r: ^Rand = global_rand_ptr) -> i32 {
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int63_max :: proc(n: i64, r: ^Rand = global_rand_ptr) -> i64 {
|
||||
if n <= 0 do panic("Invalid argument to int63_max");
|
||||
int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int63_max");
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int63(r) & (n-1);
|
||||
}
|
||||
@@ -85,8 +93,10 @@ int63_max :: proc(n: i64, r: ^Rand = global_rand_ptr) -> i64 {
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int127_max :: proc(n: i128, r: ^Rand = global_rand_ptr) -> i128 {
|
||||
if n <= 0 do panic("Invalid argument to int63_max");
|
||||
int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int63_max");
|
||||
}
|
||||
if n&(n-1) == 0 {
|
||||
return int127(r) & (n-1);
|
||||
}
|
||||
@@ -98,8 +108,10 @@ int127_max :: proc(n: i128, r: ^Rand = global_rand_ptr) -> i128 {
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int_max :: proc(n: int, r: ^Rand = global_rand_ptr) -> int {
|
||||
if n <= 0 do panic("Invalid argument to int_max");
|
||||
int_max :: proc(n: int, r: ^Rand = nil) -> int {
|
||||
if n <= 0 {
|
||||
panic("Invalid argument to int_max");
|
||||
}
|
||||
when size_of(int) == 4 {
|
||||
return int(int31_max(i32(n), r));
|
||||
} else {
|
||||
@@ -107,14 +119,14 @@ int_max :: proc(n: int, r: ^Rand = global_rand_ptr) -> int {
|
||||
}
|
||||
}
|
||||
|
||||
float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53); }
|
||||
float32 :: proc(r: ^Rand = global_rand_ptr) -> f32 { return f32(float64(r)); }
|
||||
float64 :: proc(r: ^Rand = nil) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53); }
|
||||
float32 :: proc(r: ^Rand = nil) -> f32 { return f32(float64(r)); }
|
||||
|
||||
float64_range :: proc(lo, hi: f64, r: ^Rand = global_rand_ptr) -> f64 { return (hi-lo)*float64(r) + lo; }
|
||||
float32_range :: proc(lo, hi: f32, r: ^Rand = global_rand_ptr) -> f32 { return (hi-lo)*float32(r) + lo; }
|
||||
float64_range :: proc(lo, hi: f64, r: ^Rand = nil) -> f64 { return (hi-lo)*float64(r) + lo; }
|
||||
float32_range :: proc(lo, hi: f32, r: ^Rand = nil) -> f32 { return (hi-lo)*float32(r) + lo; }
|
||||
|
||||
|
||||
read :: proc(p: []byte, r: ^Rand = global_rand_ptr) -> (n: int) {
|
||||
read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
|
||||
pos := i8(0);
|
||||
val := i64(0);
|
||||
for n = 0; n < len(p); n += 1 {
|
||||
@@ -130,10 +142,10 @@ read :: proc(p: []byte, r: ^Rand = global_rand_ptr) -> (n: int) {
|
||||
}
|
||||
|
||||
// perm returns a slice of n ints in a pseudo-random permutation of integers in the range [0, n)
|
||||
perm :: proc(n: int, r: ^Rand = global_rand_ptr) -> []int {
|
||||
perm :: proc(n: int, r: ^Rand = nil) -> []int {
|
||||
m := make([]int, n);
|
||||
for i := 0; i < n; i += 1 {
|
||||
j := int_max(i+1);
|
||||
j := int_max(i+1, r);
|
||||
m[i] = m[j];
|
||||
m[j] = i;
|
||||
}
|
||||
@@ -141,9 +153,11 @@ perm :: proc(n: int, r: ^Rand = global_rand_ptr) -> []int {
|
||||
}
|
||||
|
||||
|
||||
shuffle :: proc(array: $T/[]$E, r: ^Rand = global_rand_ptr) {
|
||||
shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
|
||||
n := i64(len(array));
|
||||
if n < 2 do return;
|
||||
if n < 2 {
|
||||
return;
|
||||
}
|
||||
|
||||
for i := i64(0); i < n; i += 1 {
|
||||
j := int63_max(n, r);
|
||||
|
||||
+127
-32
@@ -28,11 +28,20 @@ Allocator_Query_Info :: struct {
|
||||
}
|
||||
*/
|
||||
|
||||
Allocator_Error :: runtime.Allocator_Error;
|
||||
/*
|
||||
Allocator_Error :: enum byte {
|
||||
None = 0,
|
||||
Out_Of_Memory = 1,
|
||||
Invalid_Pointer = 2,
|
||||
Invalid_Argument = 3,
|
||||
}
|
||||
*/
|
||||
Allocator_Proc :: runtime.Allocator_Proc;
|
||||
/*
|
||||
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, location := #caller_location) -> rawptr;
|
||||
old_memory: rawptr, old_size: int, location: Source_Code_Location = #caller_location) -> ([]byte, Allocator_Error);
|
||||
*/
|
||||
|
||||
Allocator :: runtime.Allocator;
|
||||
@@ -45,42 +54,98 @@ Allocator :: struct {
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
|
||||
|
||||
alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 do return nil;
|
||||
if allocator.procedure == nil do return nil;
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, 0, loc);
|
||||
}
|
||||
|
||||
free :: inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
|
||||
if ptr == nil do return;
|
||||
if allocator.procedure == nil do return;
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
|
||||
}
|
||||
|
||||
free_all :: inline proc(allocator := context.allocator, loc := #caller_location) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, 0, loc);
|
||||
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 {
|
||||
return nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
|
||||
_ = err;
|
||||
return raw_data(data);
|
||||
}
|
||||
|
||||
resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 {
|
||||
return nil, nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil;
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc);
|
||||
}
|
||||
|
||||
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil {
|
||||
return nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc);
|
||||
return err;
|
||||
}
|
||||
|
||||
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if bytes == nil {
|
||||
return nil;
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc);
|
||||
return err;
|
||||
}
|
||||
|
||||
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if allocator.procedure != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc);
|
||||
return err;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if allocator.procedure == nil {
|
||||
return nil;
|
||||
}
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
|
||||
}
|
||||
return nil;
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, 0, loc);
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
|
||||
_ = err;
|
||||
return nil;
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, 0, loc);
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
|
||||
_ = err;
|
||||
return raw_data(data);
|
||||
}
|
||||
|
||||
resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil;
|
||||
}
|
||||
ptr := raw_data(old_data);
|
||||
old_size := len(old_data);
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
|
||||
return nil, err;
|
||||
}
|
||||
return nil, nil;
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
|
||||
}
|
||||
|
||||
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, 0, loc);
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc);
|
||||
return set;
|
||||
}
|
||||
return nil;
|
||||
@@ -89,7 +154,7 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A
|
||||
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
|
||||
props.pointer = pointer;
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, 0, loc);
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -110,7 +175,7 @@ delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #cal
|
||||
}
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
|
||||
raw := transmute(Raw_Map)m;
|
||||
delete_slice(raw.hashes);
|
||||
delete_slice(raw.hashes, raw.entries.allocator, loc);
|
||||
free(raw.entries.data, raw.entries.allocator, loc);
|
||||
}
|
||||
|
||||
@@ -124,22 +189,22 @@ delete :: proc{
|
||||
};
|
||||
|
||||
|
||||
new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
return new_aligned(T, align_of(T), allocator, loc);
|
||||
}
|
||||
new_aligned :: inline proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(alloc(size_of(T), alignment, allocator, loc));
|
||||
if ptr != nil do ptr^ = T{};
|
||||
if ptr != nil { ptr^ = T{}; }
|
||||
return ptr;
|
||||
}
|
||||
new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(alloc(size_of(T), align_of(T), allocator, loc));
|
||||
if ptr != nil do ptr^ = data;
|
||||
if ptr != nil { ptr^ = data; }
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
return make_aligned(T, len, align_of(E), allocator, loc);
|
||||
}
|
||||
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
@@ -188,20 +253,50 @@ make :: proc{
|
||||
|
||||
|
||||
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if old_memory == nil do return alloc(new_size, alignment, allocator, loc);
|
||||
if old_memory == nil {
|
||||
return alloc(new_size, alignment, allocator, loc);
|
||||
}
|
||||
|
||||
if new_size == 0 {
|
||||
free(old_memory, allocator, loc);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if new_size == old_size do return old_memory;
|
||||
if new_size == old_size {
|
||||
return old_memory;
|
||||
}
|
||||
|
||||
new_memory := alloc(new_size, alignment, allocator, loc);
|
||||
if new_memory == nil do return nil;
|
||||
if new_memory == nil {
|
||||
return nil;
|
||||
}
|
||||
|
||||
copy(new_memory, old_memory, min(old_size, new_size));
|
||||
free(old_memory, allocator, loc);
|
||||
return new_memory;
|
||||
}
|
||||
default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
old_memory := raw_data(old_data);
|
||||
old_size := len(old_data);
|
||||
if old_memory == nil {
|
||||
return alloc_bytes(new_size, alignment, allocator, loc);
|
||||
}
|
||||
|
||||
if new_size == 0 {
|
||||
err := free_bytes(old_data, allocator, loc);
|
||||
return nil, err;
|
||||
}
|
||||
|
||||
if new_size == old_size {
|
||||
return old_data, .None;
|
||||
}
|
||||
|
||||
new_memory, err := alloc_bytes(new_size, alignment, allocator, loc);
|
||||
if new_memory == nil || err != nil {
|
||||
return nil, err;
|
||||
}
|
||||
|
||||
runtime.copy(new_memory, old_data);
|
||||
free_bytes(old_data, allocator, loc);
|
||||
return new_memory, err;
|
||||
}
|
||||
|
||||
+352
-221
@@ -5,8 +5,8 @@ import "core:runtime"
|
||||
|
||||
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
return nil;
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
nil_allocator :: proc() -> Allocator {
|
||||
@@ -47,7 +47,7 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
arena := cast(^Arena)allocator_data;
|
||||
|
||||
switch mode {
|
||||
@@ -55,7 +55,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
total_size := size + alignment;
|
||||
|
||||
if arena.offset + total_size > len(arena.data) {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
|
||||
#no_bounds_check end := &arena.data[arena.offset];
|
||||
@@ -63,7 +63,8 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
ptr := align_forward(end, uintptr(alignment));
|
||||
arena.offset += total_size;
|
||||
arena.peak_used = max(arena.peak_used, arena.offset);
|
||||
return zero(ptr, size);
|
||||
zero(ptr, size);
|
||||
return byte_slice(ptr, size), nil;
|
||||
|
||||
case .Free:
|
||||
// NOTE(bill): Free all at once
|
||||
@@ -73,20 +74,20 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
arena.offset = 0;
|
||||
|
||||
case .Resize:
|
||||
return default_resize_align(old_memory, old_size, size, alignment, arena_allocator(arena));
|
||||
return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena));
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
||||
@@ -107,131 +108,177 @@ end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
|
||||
|
||||
Scratch_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_offset: int,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic]rawptr,
|
||||
default_to_default_allocator: bool,
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_allocation: rawptr,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic][]byte,
|
||||
}
|
||||
|
||||
scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup_allocator := context.allocator) {
|
||||
scratch.data = data;
|
||||
scratch.curr_offset = 0;
|
||||
scratch.prev_offset = 0;
|
||||
scratch.backup_allocator = backup_allocator;
|
||||
scratch_allocator_init :: proc(s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) {
|
||||
s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator);
|
||||
s.curr_offset = 0;
|
||||
s.prev_allocation = nil;
|
||||
s.backup_allocator = backup_allocator;
|
||||
s.leaked_allocations.allocator = backup_allocator;
|
||||
}
|
||||
|
||||
scratch_allocator_destroy :: proc(using scratch: ^Scratch_Allocator) {
|
||||
if scratch == nil {
|
||||
scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) {
|
||||
if s == nil {
|
||||
return;
|
||||
}
|
||||
for ptr in leaked_allocations {
|
||||
free(ptr, backup_allocator);
|
||||
for ptr in s.leaked_allocations {
|
||||
free_bytes(ptr, s.backup_allocator);
|
||||
}
|
||||
delete(leaked_allocations);
|
||||
delete(data, backup_allocator);
|
||||
scratch^ = {};
|
||||
delete(s.leaked_allocations);
|
||||
delete(s.data, s.backup_allocator);
|
||||
s^ = {};
|
||||
}
|
||||
|
||||
scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
|
||||
scratch := (^Scratch_Allocator)(allocator_data);
|
||||
s := (^Scratch_Allocator)(allocator_data);
|
||||
|
||||
if scratch.data == nil {
|
||||
DEFAULT_SCRATCH_BACKING_SIZE :: 1<<22;
|
||||
if s.data == nil {
|
||||
DEFAULT_BACKING_SIZE :: 1<<22;
|
||||
if !(context.allocator.procedure != scratch_allocator_proc &&
|
||||
context.allocator.data != allocator_data) {
|
||||
panic("cyclic initialization of the scratch allocator with itself");
|
||||
}
|
||||
scratch_allocator_init(scratch, make([]byte, 1<<22));
|
||||
scratch_allocator_init(s, DEFAULT_BACKING_SIZE);
|
||||
}
|
||||
|
||||
size := size;
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
size = align_forward_int(size, alignment);
|
||||
|
||||
switch {
|
||||
case scratch.curr_offset+size <= len(scratch.data):
|
||||
offset := align_forward_uintptr(uintptr(scratch.curr_offset), uintptr(alignment));
|
||||
ptr := &scratch.data[offset];
|
||||
zero(ptr, size);
|
||||
scratch.prev_offset = int(offset);
|
||||
scratch.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
case size <= len(scratch.data):
|
||||
offset := align_forward_uintptr(uintptr(0), uintptr(alignment));
|
||||
ptr := &scratch.data[offset];
|
||||
zero(ptr, size);
|
||||
scratch.prev_offset = int(offset);
|
||||
scratch.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
case s.curr_offset+size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data));
|
||||
ptr := start + uintptr(s.curr_offset);
|
||||
ptr = align_forward_uintptr(ptr, uintptr(alignment));
|
||||
zero(rawptr(ptr), size);
|
||||
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return byte_slice(rawptr(ptr), size), nil;
|
||||
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data));
|
||||
ptr := align_forward_uintptr(start, uintptr(alignment));
|
||||
zero(rawptr(ptr), size);
|
||||
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return byte_slice(rawptr(ptr), size), nil;
|
||||
}
|
||||
// TODO(bill): Should leaks be notified about? Should probably use a logging system that is built into the context system
|
||||
a := scratch.backup_allocator;
|
||||
a := s.backup_allocator;
|
||||
if a.procedure == nil {
|
||||
a = context.allocator;
|
||||
scratch.backup_allocator = a;
|
||||
s.backup_allocator = a;
|
||||
}
|
||||
|
||||
ptr := alloc(size, alignment, a, loc);
|
||||
if scratch.leaked_allocations == nil {
|
||||
scratch.leaked_allocations = make([dynamic]rawptr, a);
|
||||
ptr, err := alloc_bytes(size, alignment, a, loc);
|
||||
if err != nil {
|
||||
return ptr, err;
|
||||
}
|
||||
append(&scratch.leaked_allocations, ptr);
|
||||
if s.leaked_allocations == nil {
|
||||
s.leaked_allocations = make([dynamic][]byte, a);
|
||||
}
|
||||
append(&s.leaked_allocations, ptr);
|
||||
|
||||
return ptr;
|
||||
if logger := context.logger; logger.lowest_level <= .Warning {
|
||||
if logger.procedure != nil {
|
||||
logger.procedure(logger.data, .Warning, "mem.Scratch_Allocator resorted to backup_allocator" , logger.options, loc);
|
||||
}
|
||||
}
|
||||
|
||||
return ptr, err;
|
||||
|
||||
case .Free:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr {
|
||||
full_size := scratch.curr_offset - scratch.prev_offset;
|
||||
scratch.curr_offset = scratch.prev_offset;
|
||||
zero(last_ptr, full_size);
|
||||
return nil;
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
old_ptr := uintptr(old_memory);
|
||||
|
||||
if s.prev_allocation == old_memory {
|
||||
s.curr_offset = int(uintptr(s.prev_allocation) - start);
|
||||
s.prev_allocation = nil;
|
||||
return nil, nil;
|
||||
}
|
||||
// NOTE(bill): It's scratch memory, don't worry about freeing
|
||||
|
||||
if start <= old_ptr && old_ptr < end {
|
||||
// NOTE(bill): Cannot free this pointer but it is valid
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
if len(s.leaked_allocations) != 0 {
|
||||
for data, i in s.leaked_allocations {
|
||||
ptr := raw_data(data);
|
||||
if ptr == old_memory {
|
||||
free_bytes(data, s.backup_allocator);
|
||||
ordered_remove(&s.leaked_allocations, i);
|
||||
return nil, nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, .Invalid_Pointer;
|
||||
// panic("invalid pointer passed to default_temp_allocator");
|
||||
|
||||
case .Free_All:
|
||||
scratch.curr_offset = 0;
|
||||
scratch.prev_offset = 0;
|
||||
for ptr in scratch.leaked_allocations {
|
||||
free(ptr, scratch.backup_allocator);
|
||||
s.curr_offset = 0;
|
||||
s.prev_allocation = nil;
|
||||
for ptr in s.leaked_allocations {
|
||||
free_bytes(ptr, s.backup_allocator);
|
||||
}
|
||||
clear(&scratch.leaked_allocations);
|
||||
clear(&s.leaked_allocations);
|
||||
|
||||
case .Resize:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr && len(scratch.data)-scratch.prev_offset >= size {
|
||||
scratch.curr_offset = scratch.prev_offset+size;
|
||||
return old_memory;
|
||||
begin := uintptr(raw_data(s.data));
|
||||
end := begin + uintptr(len(s.data));
|
||||
old_ptr := uintptr(old_memory);
|
||||
if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
|
||||
s.curr_offset = int(old_ptr-begin)+size;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
return scratch_allocator_proc(allocator_data, Allocator_Mode.Alloc, size, alignment, old_memory, old_size, flags, loc);
|
||||
data, err := scratch_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc);
|
||||
if err != nil {
|
||||
return data, err;
|
||||
}
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
_, err = scratch_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, loc);
|
||||
return data, err;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
scratch_allocator :: proc(scratch: ^Scratch_Allocator) -> Allocator {
|
||||
scratch_allocator :: proc(allocator: ^Scratch_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = scratch_allocator_proc,
|
||||
data = scratch,
|
||||
data = allocator,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Stack_Allocation_Header :: struct {
|
||||
prev_offset: int,
|
||||
padding: int,
|
||||
@@ -262,32 +309,33 @@ stack_allocator :: proc(stack: ^Stack) -> Allocator {
|
||||
|
||||
stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
s := cast(^Stack)allocator_data;
|
||||
|
||||
if s.data == nil {
|
||||
return nil;
|
||||
return nil, .Invalid_Argument;
|
||||
}
|
||||
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> rawptr {
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset);
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header));
|
||||
if s.curr_offset + padding + size > len(s.data) {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
s.prev_offset = s.curr_offset;
|
||||
s.curr_offset += padding;
|
||||
|
||||
next_addr := curr_addr + uintptr(padding);
|
||||
header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header));
|
||||
header.padding = auto_cast padding;
|
||||
header.prev_offset = auto_cast s.prev_offset;
|
||||
header.padding = padding;
|
||||
header.prev_offset = s.prev_offset;
|
||||
|
||||
s.curr_offset += size;
|
||||
|
||||
s.peak_used = max(s.peak_used, s.curr_offset);
|
||||
|
||||
return zero(rawptr(next_addr), size);
|
||||
zero(rawptr(next_addr), size);
|
||||
return byte_slice(rawptr(next_addr), size), nil;
|
||||
}
|
||||
|
||||
switch mode {
|
||||
@@ -295,7 +343,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, alignment);
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
@@ -307,19 +355,19 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
if curr_addr >= start+uintptr(s.curr_offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||
|
||||
if old_offset != int(header.prev_offset) {
|
||||
panic("Out of order stack allocator free");
|
||||
if old_offset != header.prev_offset {
|
||||
// panic("Out of order stack allocator free");
|
||||
return nil, .Invalid_Pointer;
|
||||
}
|
||||
|
||||
s.curr_offset = int(old_offset);
|
||||
s.prev_offset = int(header.prev_offset);
|
||||
|
||||
s.curr_offset = old_offset;
|
||||
s.prev_offset = header.prev_offset;
|
||||
|
||||
case .Free_All:
|
||||
s.prev_offset = 0;
|
||||
@@ -330,7 +378,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, alignment);
|
||||
}
|
||||
if size == 0 {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
start := uintptr(raw_data(s.data));
|
||||
@@ -342,20 +390,22 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
if curr_addr >= start+uintptr(s.curr_offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
if old_size == size {
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
|
||||
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||
|
||||
if old_offset != int(header.prev_offset) {
|
||||
ptr := raw_alloc(s, size, alignment);
|
||||
copy(ptr, old_memory, min(old_size, size));
|
||||
return ptr;
|
||||
if old_offset != header.prev_offset {
|
||||
data, err := raw_alloc(s, size, alignment);
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
}
|
||||
return data, err;
|
||||
}
|
||||
|
||||
old_memory_size := uintptr(s.curr_offset) - (curr_addr - start);
|
||||
@@ -367,19 +417,19 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
zero(rawptr(curr_addr + uintptr(diff)), diff);
|
||||
}
|
||||
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -414,20 +464,20 @@ small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
|
||||
|
||||
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, ocation := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
s := cast(^Small_Stack)allocator_data;
|
||||
|
||||
if s.data == nil {
|
||||
return nil;
|
||||
return nil, .Invalid_Argument;
|
||||
}
|
||||
|
||||
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
|
||||
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> rawptr {
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset);
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header));
|
||||
if s.offset + padding + size > len(s.data) {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
s.offset += padding;
|
||||
|
||||
@@ -439,7 +489,8 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.offset);
|
||||
|
||||
return zero(rawptr(next_addr), size);
|
||||
zero(rawptr(next_addr), size);
|
||||
return byte_slice(rawptr(next_addr), size), nil;
|
||||
}
|
||||
|
||||
switch mode {
|
||||
@@ -447,25 +498,26 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, align);
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (free)");
|
||||
// panic("Out of bounds memory address passed to stack allocator (free)");
|
||||
return nil, .Invalid_Pointer;
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.offset) {
|
||||
// NOTE(bill): Allow double frees
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header));
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)));
|
||||
|
||||
s.offset = int(old_offset);
|
||||
s.offset = old_offset;
|
||||
|
||||
case .Free_All:
|
||||
s.offset = 0;
|
||||
@@ -475,41 +527,44 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return raw_alloc(s, size, align);
|
||||
}
|
||||
if size == 0 {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
curr_addr := uintptr(old_memory);
|
||||
if !(start <= curr_addr && curr_addr < end) {
|
||||
panic("Out of bounds memory address passed to stack allocator (resize)");
|
||||
// panic("Out of bounds memory address passed to stack allocator (resize)");
|
||||
return nil, .Invalid_Pointer;
|
||||
}
|
||||
|
||||
if curr_addr >= start+uintptr(s.offset) {
|
||||
// NOTE(bill): Treat as a double free
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
if old_size == size {
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
|
||||
ptr := raw_alloc(s, size, align);
|
||||
copy(ptr, old_memory, min(old_size, size));
|
||||
return ptr;
|
||||
data, err := raw_alloc(s, size, align);
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
}
|
||||
return data, err;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -540,42 +595,44 @@ DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554;
|
||||
|
||||
dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
pool := (^Dynamic_Pool)(allocator_data);
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return dynamic_pool_alloc(pool, size);
|
||||
return dynamic_pool_alloc_bytes(pool, size);
|
||||
case .Free:
|
||||
//
|
||||
return nil, nil;
|
||||
case .Free_All:
|
||||
dynamic_pool_free_all(pool);
|
||||
return nil, nil;
|
||||
case .Resize:
|
||||
if old_size >= size {
|
||||
return old_memory;
|
||||
return byte_slice(old_memory, size), nil;
|
||||
}
|
||||
ptr := dynamic_pool_alloc(pool, size);
|
||||
copy(ptr, old_memory, old_size);
|
||||
return ptr;
|
||||
data, err := dynamic_pool_alloc_bytes(pool, size);
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size));
|
||||
}
|
||||
return data, err;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
info := (^Allocator_Query_Info)(old_memory);
|
||||
if info != nil && info.pointer != nil {
|
||||
info.size = pool.block_size;
|
||||
info.alignment = pool.alignment;
|
||||
return info;
|
||||
return byte_slice(info, size_of(info^)), nil;
|
||||
}
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -610,8 +667,14 @@ dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
|
||||
}
|
||||
|
||||
|
||||
dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
cycle_new_block :: proc(using pool: ^Dynamic_Pool) {
|
||||
dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
data, err := dynamic_pool_alloc_bytes(pool, bytes);
|
||||
assert(err == nil);
|
||||
return raw_data(data);
|
||||
}
|
||||
|
||||
dynamic_pool_alloc_bytes :: proc(using pool: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
|
||||
cycle_new_block :: proc(using pool: ^Dynamic_Pool) -> (err: Allocator_Error) {
|
||||
if block_allocator.procedure == nil {
|
||||
panic("You must call pool_init on a Pool before using it");
|
||||
}
|
||||
@@ -624,42 +687,47 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
if len(unused_blocks) > 0 {
|
||||
new_block = pop(&unused_blocks);
|
||||
} else {
|
||||
new_block = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
data: []byte;
|
||||
data, err = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
new_block = raw_data(data);
|
||||
}
|
||||
|
||||
bytes_left = block_size;
|
||||
current_pos = new_block;
|
||||
current_block = new_block;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
n := bytes;
|
||||
extra := alignment - (n % alignment);
|
||||
n += extra;
|
||||
if n >= out_band_size {
|
||||
assert(block_allocator.procedure != nil);
|
||||
memory := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
memory, err := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0);
|
||||
if memory != nil {
|
||||
append(&out_band_allocations, (^byte)(memory));
|
||||
append(&out_band_allocations, raw_data(memory));
|
||||
}
|
||||
return memory;
|
||||
return memory, err;
|
||||
}
|
||||
|
||||
if bytes_left < n {
|
||||
cycle_new_block(pool);
|
||||
err := cycle_new_block(pool);
|
||||
if err != nil {
|
||||
return nil, err;
|
||||
}
|
||||
if current_block == nil {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
}
|
||||
|
||||
memory := current_pos;
|
||||
current_pos = ptr_offset((^byte)(current_pos), n);
|
||||
bytes_left -= n;
|
||||
return memory;
|
||||
return byte_slice(memory, bytes), nil;
|
||||
}
|
||||
|
||||
|
||||
@@ -692,7 +760,7 @@ dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
|
||||
|
||||
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int,loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
@@ -715,13 +783,13 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
if set != nil {
|
||||
set^ = {.Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
panic_allocator :: proc() -> Allocator {
|
||||
@@ -732,84 +800,33 @@ panic_allocator :: proc() -> Allocator {
|
||||
}
|
||||
|
||||
|
||||
alloca_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
switch alignment {
|
||||
case: return intrinsics.alloca(size, 2*align_of(uintptr));
|
||||
case 0: return intrinsics.alloca(size, 0);
|
||||
|
||||
case 1: return intrinsics.alloca(size, 1);
|
||||
case 2: return intrinsics.alloca(size, 2);
|
||||
case 4: return intrinsics.alloca(size, 4);
|
||||
case 8: return intrinsics.alloca(size, 8);
|
||||
case 16: return intrinsics.alloca(size, 16);
|
||||
case 32: return intrinsics.alloca(size, 32);
|
||||
case 64: return intrinsics.alloca(size, 64);
|
||||
case 128: return intrinsics.alloca(size, 128);
|
||||
case 256: return intrinsics.alloca(size, 256);
|
||||
case 512: return intrinsics.alloca(size, 512);
|
||||
case 1024: return intrinsics.alloca(size, 1024);
|
||||
case 2048: return intrinsics.alloca(size, 2048);
|
||||
case 4096: return intrinsics.alloca(size, 4096);
|
||||
case 8192: return intrinsics.alloca(size, 8192);
|
||||
case 16384: return intrinsics.alloca(size, 16384);
|
||||
case 32768: return intrinsics.alloca(size, 32768);
|
||||
case 65536: return intrinsics.alloca(size, 65536);
|
||||
}
|
||||
case .Resize:
|
||||
return default_resize_align(old_memory, old_size, size, alignment, alloca_allocator());
|
||||
|
||||
case .Free:
|
||||
// Do nothing
|
||||
case .Free_All:
|
||||
// Do nothing
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
alloca_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = alloca_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Tracking_Allocator_Entry :: struct {
|
||||
memory: rawptr,
|
||||
size: int,
|
||||
alignment: int,
|
||||
err: Allocator_Error,
|
||||
location: runtime.Source_Code_Location,
|
||||
}
|
||||
Tracking_Allocator_Bad_Free_Entry :: struct {
|
||||
memory: rawptr,
|
||||
location: runtime.Source_Code_Location,
|
||||
}
|
||||
Tracking_Allocator :: struct {
|
||||
backing: Allocator,
|
||||
allocation_map: map[rawptr]Tracking_Allocator_Entry,
|
||||
bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
|
||||
clear_on_free_all: bool,
|
||||
}
|
||||
|
||||
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, allocation_map_allocator := context.allocator) {
|
||||
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
|
||||
t.backing = backing_allocator;
|
||||
t.allocation_map.allocator = allocation_map_allocator;
|
||||
t.allocation_map.allocator = internals_allocator;
|
||||
t.bad_free_array.allocator = internals_allocator;
|
||||
}
|
||||
|
||||
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
delete(t.allocation_map);
|
||||
delete(t.bad_free_array);
|
||||
}
|
||||
|
||||
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
@@ -819,7 +836,9 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
};
|
||||
}
|
||||
|
||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
data := (^Tracking_Allocator)(allocator_data);
|
||||
if mode == .Query_Info {
|
||||
info := (^Allocator_Query_Info)(old_memory);
|
||||
@@ -827,15 +846,27 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
|
||||
if entry, ok := data.allocation_map[info.pointer]; ok {
|
||||
info.size = entry.size;
|
||||
info.alignment = entry.alignment;
|
||||
return info;
|
||||
}
|
||||
info.pointer = nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
result := data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, flags, loc);
|
||||
result: []byte;
|
||||
err: Allocator_Error;
|
||||
if mode == .Free && old_memory not_in data.allocation_map {
|
||||
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
||||
memory = old_memory,
|
||||
location = loc,
|
||||
});
|
||||
} else {
|
||||
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc);
|
||||
if err != nil {
|
||||
return result, err;
|
||||
}
|
||||
}
|
||||
result_ptr := raw_data(result);
|
||||
|
||||
if data.allocation_map.allocator.procedure == nil {
|
||||
data.allocation_map.allocator = context.allocator;
|
||||
@@ -843,22 +874,24 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
data.allocation_map[result] = Tracking_Allocator_Entry{
|
||||
memory = result,
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
};
|
||||
case .Free:
|
||||
delete_key(&data.allocation_map, old_memory);
|
||||
case .Resize:
|
||||
if old_memory != result {
|
||||
if old_memory != result_ptr {
|
||||
delete_key(&data.allocation_map, old_memory);
|
||||
}
|
||||
data.allocation_map[result] = Tracking_Allocator_Entry{
|
||||
memory = result,
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
};
|
||||
|
||||
@@ -872,11 +905,109 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, si
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
unreachable();
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return result;
|
||||
return result, err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Small_Allocator primary allocates memory from its local buffer of size BUFFER_SIZE
|
||||
// If that buffer's memory is exhausted, it will use the backing allocator (a scratch allocator is recommended)
|
||||
// Memory allocated with Small_Allocator cannot be freed individually using 'free' and must be freed using 'free_all'
|
||||
Small_Allocator :: struct(BUFFER_SIZE: int)
|
||||
where
|
||||
BUFFER_SIZE >= 2*size_of(uintptr),
|
||||
BUFFER_SIZE & (BUFFER_SIZE-1) == 0 {
|
||||
|
||||
buffer: [BUFFER_SIZE]byte,
|
||||
backing: Allocator,
|
||||
start: uintptr,
|
||||
curr: uintptr,
|
||||
end: uintptr,
|
||||
chunk_size: int,
|
||||
}
|
||||
|
||||
small_allocator :: proc(s: ^$S/Small_Allocator, backing := context.allocator) -> (a: Allocator) {
|
||||
if s.backing.procedure == nil {
|
||||
s.backing = backing;
|
||||
}
|
||||
a.data = s;
|
||||
a.procedure = proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
s := (^S)(allocator_data);
|
||||
if s.chunk_size <= 0 {
|
||||
s.chunk_size = 4*1024;
|
||||
}
|
||||
if s.start == 0 {
|
||||
s.start = uintptr(&s.buffer[0]);
|
||||
s.curr = s.start;
|
||||
s.end = s.start + uintptr(S.BUFFER_SIZE);
|
||||
(^rawptr)(s.start)^ = nil;
|
||||
s.curr += size_of(rawptr);
|
||||
}
|
||||
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
s.curr = align_forward_uintptr(s.curr, uintptr(alignment));
|
||||
if size > int(s.end - s.curr) {
|
||||
to_allocate := size_of(rawptr) + size + alignment;
|
||||
if to_allocate < s.chunk_size {
|
||||
to_allocate = s.chunk_size;
|
||||
}
|
||||
s.chunk_size *= 2;
|
||||
|
||||
p := alloc(to_allocate, 16, s.backing, loc);
|
||||
(^rawptr)(s.start)^ = p;
|
||||
s.start = uintptr(p);
|
||||
s.curr = s.start;
|
||||
s.end = s.start + uintptr(to_allocate);
|
||||
|
||||
(^rawptr)(s.start)^ = nil;
|
||||
s.curr += size_of(rawptr);
|
||||
s.curr = align_forward_uintptr(s.curr, uintptr(alignment));
|
||||
}
|
||||
|
||||
p := rawptr(s.curr);
|
||||
s.curr += uintptr(size);
|
||||
return mem_zero(p, size);
|
||||
|
||||
case .Free:
|
||||
// NOP
|
||||
return nil;
|
||||
|
||||
case .Resize:
|
||||
// No need copying the code
|
||||
return default_resize_align(old_memory, old_size, size, alignment, small_allocator(s, s.backing), loc);
|
||||
|
||||
case .Free_All:
|
||||
p := (^rawptr)(&s.buffer[0])^;
|
||||
for p != nil {
|
||||
next := (^rawptr)(p)^;
|
||||
free(next, s.backing, loc);
|
||||
p = next;
|
||||
}
|
||||
// Reset to default
|
||||
s.start = uintptr(&s.buffer[0]);
|
||||
s.curr = s.start;
|
||||
s.end = s.start + uintptr(S.BUFFER_SIZE);
|
||||
|
||||
(^rawptr)(s.start)^ = nil;
|
||||
s.curr += size_of(rawptr);
|
||||
|
||||
|
||||
case .Query_Features:
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil, nil;
|
||||
};
|
||||
return a;
|
||||
}
|
||||
|
||||
+47
-31
@@ -6,10 +6,10 @@ import "core:intrinsics"
|
||||
set :: proc(data: rawptr, value: byte, len: int) -> rawptr {
|
||||
return runtime.memset(data, i32(value), len);
|
||||
}
|
||||
zero :: inline proc(data: rawptr, len: int) -> rawptr {
|
||||
zero :: proc(data: rawptr, len: int) -> rawptr {
|
||||
return set(data, 0, len);
|
||||
}
|
||||
zero_item :: inline proc(item: $P/^$T) {
|
||||
zero_item :: proc(item: $P/^$T) {
|
||||
set(item, 0, size_of(T));
|
||||
}
|
||||
zero_slice :: proc(data: $T/[]$E) {
|
||||
@@ -23,7 +23,7 @@ copy :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
return runtime.mem_copy_non_overlapping(dst, src, len);
|
||||
}
|
||||
compare :: inline proc(a, b: []byte) -> int {
|
||||
compare :: proc(a, b: []byte) -> int {
|
||||
res := compare_byte_ptrs(raw_data(a), raw_data(b), min(len(a), len(b)));
|
||||
if res == 0 && len(a) != len(b) {
|
||||
return len(a) <= len(b) ? -1 : +1;
|
||||
@@ -121,36 +121,39 @@ simple_equal :: proc(a, b: $T) -> bool where intrinsics.type_is_simple_compare(T
|
||||
return compare_byte_ptrs((^byte)(&a), (^byte)(&b), size_of(T)) == 0;
|
||||
}
|
||||
|
||||
compare_ptrs :: inline proc(a, b: rawptr, n: int) -> int {
|
||||
compare_ptrs :: proc(a, b: rawptr, n: int) -> int {
|
||||
return compare_byte_ptrs((^byte)(a), (^byte)(b), n);
|
||||
}
|
||||
|
||||
ptr_offset :: inline proc(ptr: $P/^$T, n: int) -> P {
|
||||
ptr_offset :: proc(ptr: $P/^$T, n: int) -> P {
|
||||
new := int(uintptr(ptr)) + size_of(T)*n;
|
||||
return P(uintptr(new));
|
||||
}
|
||||
|
||||
ptr_sub :: inline proc(a, b: $P/^$T) -> int {
|
||||
ptr_sub :: proc(a, b: $P/^$T) -> int {
|
||||
return (int(uintptr(a)) - int(uintptr(b)))/size_of(T);
|
||||
}
|
||||
|
||||
slice_ptr :: inline proc(ptr: ^$T, len: int) -> []T {
|
||||
slice_ptr :: proc(ptr: ^$T, len: int) -> []T {
|
||||
assert(len >= 0);
|
||||
return transmute([]T)Raw_Slice{data = ptr, len = len};
|
||||
}
|
||||
|
||||
slice_ptr_to_bytes :: proc(ptr: rawptr, len: int) -> []byte {
|
||||
assert(len >= 0);
|
||||
return transmute([]byte)Raw_Slice{data = ptr, len = len};
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
||||
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
|
||||
}
|
||||
slice_ptr_to_bytes :: proc(data: rawptr, len: int) -> []byte {
|
||||
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
|
||||
}
|
||||
|
||||
slice_to_bytes :: inline proc(slice: $E/[]$T) -> []byte {
|
||||
|
||||
slice_to_bytes :: proc(slice: $E/[]$T) -> []byte {
|
||||
s := transmute(Raw_Slice)slice;
|
||||
s.len *= size_of(T);
|
||||
return transmute([]byte)s;
|
||||
}
|
||||
|
||||
slice_data_cast :: inline proc($T: typeid/[]$A, slice: $S/[]$B) -> T {
|
||||
slice_data_cast :: proc($T: typeid/[]$A, slice: $S/[]$B) -> T {
|
||||
when size_of(A) == 0 || size_of(B) == 0 {
|
||||
return nil;
|
||||
} else {
|
||||
@@ -165,7 +168,7 @@ slice_to_components :: proc(slice: $E/[]$T) -> (data: ^T, len: int) {
|
||||
return s.data, s.len;
|
||||
}
|
||||
|
||||
buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E {
|
||||
buffer_from_slice :: proc(backing: $T/[]$E) -> [dynamic]E {
|
||||
return transmute([dynamic]E)Raw_Dynamic_Array{
|
||||
data = raw_data(backing),
|
||||
len = 0,
|
||||
@@ -174,29 +177,31 @@ buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E {
|
||||
};
|
||||
}
|
||||
|
||||
ptr_to_bytes :: inline proc(ptr: ^$T, len := 1) -> []byte {
|
||||
ptr_to_bytes :: proc(ptr: ^$T, len := 1) -> []byte {
|
||||
assert(len >= 0);
|
||||
return transmute([]byte)Raw_Slice{ptr, len*size_of(T)};
|
||||
}
|
||||
|
||||
any_to_bytes :: inline proc(val: any) -> []byte {
|
||||
any_to_bytes :: proc(val: any) -> []byte {
|
||||
ti := type_info_of(val.id);
|
||||
size := ti != nil ? ti.size : 0;
|
||||
return transmute([]byte)Raw_Slice{val.data, size};
|
||||
}
|
||||
|
||||
|
||||
kilobytes :: inline proc(x: int) -> int do return (x) * 1024;
|
||||
megabytes :: inline proc(x: int) -> int do return kilobytes(x) * 1024;
|
||||
gigabytes :: inline proc(x: int) -> int do return megabytes(x) * 1024;
|
||||
terabytes :: inline proc(x: int) -> int do return gigabytes(x) * 1024;
|
||||
kilobytes :: proc(x: int) -> int { return (x) * 1024; }
|
||||
megabytes :: proc(x: int) -> int { return kilobytes(x) * 1024; }
|
||||
gigabytes :: proc(x: int) -> int { return megabytes(x) * 1024; }
|
||||
terabytes :: proc(x: int) -> int { return gigabytes(x) * 1024; }
|
||||
|
||||
is_power_of_two :: inline proc(x: uintptr) -> bool {
|
||||
if x <= 0 do return false;
|
||||
is_power_of_two :: proc(x: uintptr) -> bool {
|
||||
if x <= 0 {
|
||||
return false;
|
||||
}
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
return rawptr(align_forward_uintptr(uintptr(ptr), align));
|
||||
}
|
||||
|
||||
@@ -205,18 +210,20 @@ align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
|
||||
|
||||
p := ptr;
|
||||
modulo := p & (align-1);
|
||||
if modulo != 0 do p += align - modulo;
|
||||
if modulo != 0 {
|
||||
p += align - modulo;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
align_forward_int :: inline proc(ptr, align: int) -> int {
|
||||
align_forward_int :: proc(ptr, align: int) -> int {
|
||||
return int(align_forward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
align_forward_uint :: inline proc(ptr, align: uint) -> uint {
|
||||
align_forward_uint :: proc(ptr, align: uint) -> uint {
|
||||
return uint(align_forward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
|
||||
align_backward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
align_backward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
return rawptr(align_backward_uintptr(uintptr(ptr), align));
|
||||
}
|
||||
|
||||
@@ -225,10 +232,10 @@ align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
|
||||
return align_forward_uintptr(ptr - align + 1, align);
|
||||
}
|
||||
|
||||
align_backward_int :: inline proc(ptr, align: int) -> int {
|
||||
align_backward_int :: proc(ptr, align: int) -> int {
|
||||
return int(align_backward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
align_backward_uint :: inline proc(ptr, align: uint) -> uint {
|
||||
align_backward_uint :: proc(ptr, align: uint) -> uint {
|
||||
return uint(align_backward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
|
||||
@@ -259,12 +266,13 @@ align_formula :: proc(size, align: int) -> int {
|
||||
}
|
||||
|
||||
calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int) -> int {
|
||||
p := uintptr(ptr);
|
||||
a := uintptr(align);
|
||||
p, a := ptr, align;
|
||||
modulo := p & (a-1);
|
||||
|
||||
padding := uintptr(0);
|
||||
if modulo != 0 do padding = a - modulo;
|
||||
if modulo != 0 {
|
||||
padding = a - modulo;
|
||||
}
|
||||
|
||||
needed_space := uintptr(header_size);
|
||||
if padding < needed_space {
|
||||
@@ -279,3 +287,11 @@ calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int)
|
||||
|
||||
return int(padding);
|
||||
}
|
||||
|
||||
|
||||
|
||||
clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
new_slice := make(T, len(slice), allocator, loc);
|
||||
runtime.copy(new_slice, slice);
|
||||
return new_slice;
|
||||
}
|
||||
|
||||
+4
-4
@@ -38,20 +38,20 @@ Raw_Quaternion256 :: struct {imag, jmag, kmag: f64, real: f64};
|
||||
Raw_Quaternion128_Vector_Scalar :: struct {vector: [3]f32, scalar: f32};
|
||||
Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64};
|
||||
|
||||
make_any :: inline proc(data: rawptr, id: typeid) -> any {
|
||||
make_any :: proc(data: rawptr, id: typeid) -> any {
|
||||
return transmute(any)Raw_Any{data, id};
|
||||
}
|
||||
|
||||
raw_array_data :: proc(a: $P/^($T/[$N]$E)) -> ^E {
|
||||
return (^E)(a);
|
||||
}
|
||||
raw_string_data :: inline proc(s: $T/string) -> ^byte {
|
||||
raw_string_data :: proc(s: $T/string) -> ^byte {
|
||||
return (transmute(Raw_String)s).data;
|
||||
}
|
||||
raw_slice_data :: inline proc(a: $T/[]$E) -> ^E {
|
||||
raw_slice_data :: proc(a: $T/[]$E) -> ^E {
|
||||
return cast(^E)(transmute(Raw_Slice)a).data;
|
||||
}
|
||||
raw_dynamic_array_data :: inline proc(a: $T/[dynamic]$E) -> ^E {
|
||||
raw_dynamic_array_data :: proc(a: $T/[dynamic]$E) -> ^E {
|
||||
return cast(^E)(transmute(Raw_Dynamic_Array)a).data;
|
||||
}
|
||||
|
||||
|
||||
+120
-36
@@ -5,6 +5,8 @@ import "core:odin/tokenizer"
|
||||
Proc_Tag :: enum {
|
||||
Bounds_Check,
|
||||
No_Bounds_Check,
|
||||
Optional_Ok,
|
||||
Optional_Second,
|
||||
}
|
||||
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
|
||||
|
||||
@@ -21,6 +23,7 @@ Proc_Calling_Convention :: enum i32 {
|
||||
C_Decl,
|
||||
Std_Call,
|
||||
Fast_Call,
|
||||
None,
|
||||
|
||||
Foreign_Block_Default = -1,
|
||||
}
|
||||
@@ -31,18 +34,61 @@ Node_State_Flag :: enum {
|
||||
}
|
||||
Node_State_Flags :: distinct bit_set[Node_State_Flag];
|
||||
|
||||
|
||||
Comment_Group :: struct {
|
||||
list: []tokenizer.Token,
|
||||
}
|
||||
|
||||
Node :: struct {
|
||||
pos: tokenizer.Pos,
|
||||
end: tokenizer.Pos,
|
||||
derived: any,
|
||||
state_flags: Node_State_Flags,
|
||||
derived: any,
|
||||
}
|
||||
|
||||
Comment_Group :: struct {
|
||||
using node: Node,
|
||||
list: []tokenizer.Token,
|
||||
}
|
||||
|
||||
Package_Kind :: enum {
|
||||
Normal,
|
||||
Runtime,
|
||||
Init,
|
||||
}
|
||||
|
||||
Package :: struct {
|
||||
using node: Node,
|
||||
kind: Package_Kind,
|
||||
id: int,
|
||||
name: string,
|
||||
fullpath: string,
|
||||
files: map[string]^File,
|
||||
|
||||
user_data: rawptr,
|
||||
}
|
||||
|
||||
File :: struct {
|
||||
using node: Node,
|
||||
id: int,
|
||||
pkg: ^Package,
|
||||
|
||||
fullpath: string,
|
||||
src: []byte,
|
||||
|
||||
docs: ^Comment_Group,
|
||||
|
||||
pkg_decl: ^Package_Decl,
|
||||
pkg_token: tokenizer.Token,
|
||||
pkg_name: string,
|
||||
|
||||
decls: [dynamic]^Stmt,
|
||||
imports: [dynamic]^Import_Decl,
|
||||
directive_count: int,
|
||||
|
||||
comments: [dynamic]^Comment_Group,
|
||||
|
||||
syntax_warning_count: int,
|
||||
syntax_error_count: int,
|
||||
}
|
||||
|
||||
|
||||
// Base Types
|
||||
|
||||
Expr :: struct {
|
||||
using expr_base: Node,
|
||||
@@ -142,6 +188,7 @@ Paren_Expr :: struct {
|
||||
Selector_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
op: tokenizer.Token,
|
||||
field: ^Ident,
|
||||
}
|
||||
|
||||
@@ -150,6 +197,13 @@ Implicit_Selector_Expr :: struct {
|
||||
field: ^Ident,
|
||||
}
|
||||
|
||||
Selector_Call_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
call: ^Call_Expr,
|
||||
modified_call: bool,
|
||||
}
|
||||
|
||||
Index_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
@@ -202,9 +256,9 @@ Ternary_Expr :: struct {
|
||||
|
||||
Ternary_If_Expr :: struct {
|
||||
using node: Expr,
|
||||
x: ^Expr,
|
||||
x: ^Expr,
|
||||
op1: tokenizer.Token,
|
||||
cond: ^Expr,
|
||||
cond: ^Expr,
|
||||
op2: tokenizer.Token,
|
||||
y: ^Expr,
|
||||
}
|
||||
@@ -213,7 +267,7 @@ Ternary_When_Expr :: struct {
|
||||
using node: Expr,
|
||||
x: ^Expr,
|
||||
op1: tokenizer.Token,
|
||||
cond: ^Expr,
|
||||
cond: ^Expr,
|
||||
op2: tokenizer.Token,
|
||||
y: ^Expr,
|
||||
}
|
||||
@@ -242,6 +296,27 @@ Auto_Cast :: struct {
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
Inline_Asm_Dialect :: enum u8 {
|
||||
Default = 0,
|
||||
ATT = 1,
|
||||
Intel = 2,
|
||||
}
|
||||
|
||||
|
||||
Inline_Asm_Expr :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token,
|
||||
param_types: []^Expr,
|
||||
return_type: ^Expr,
|
||||
has_side_effects: bool,
|
||||
is_align_stack: bool,
|
||||
dialect: Inline_Asm_Dialect,
|
||||
open: tokenizer.Pos,
|
||||
constraints_string: ^Expr,
|
||||
asm_string: ^Expr,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -292,6 +367,7 @@ If_Stmt :: struct {
|
||||
init: ^Stmt,
|
||||
cond: ^Expr,
|
||||
body: ^Stmt,
|
||||
else_pos: tokenizer.Pos,
|
||||
else_stmt: ^Stmt,
|
||||
}
|
||||
|
||||
@@ -327,13 +403,26 @@ Range_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
for_pos: tokenizer.Pos,
|
||||
val0: ^Expr,
|
||||
val1: ^Expr,
|
||||
vals: []^Expr,
|
||||
in_pos: tokenizer.Pos,
|
||||
expr: ^Expr,
|
||||
body: ^Stmt,
|
||||
}
|
||||
|
||||
Inline_Range_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
inline_pos: tokenizer.Pos,
|
||||
for_pos: tokenizer.Pos,
|
||||
val0: ^Expr,
|
||||
val1: ^Expr,
|
||||
in_pos: tokenizer.Pos,
|
||||
expr: ^Expr,
|
||||
body: ^Stmt,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Case_Clause :: struct {
|
||||
using node: Stmt,
|
||||
@@ -424,12 +513,12 @@ Foreign_Block_Decl :: struct {
|
||||
Foreign_Import_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
|
||||
foreign_tok: tokenizer.Token,
|
||||
import_tok: tokenizer.Token,
|
||||
name: ^Ident,
|
||||
collection_name: string,
|
||||
fullpaths: []string,
|
||||
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
|
||||
comment: ^Comment_Group,
|
||||
}
|
||||
|
||||
@@ -443,7 +532,9 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
|
||||
}
|
||||
for {
|
||||
e, ok := val.derived.(Paren_Expr);
|
||||
if !ok do break;
|
||||
if !ok || e.expr == nil {
|
||||
break;
|
||||
}
|
||||
val = e.expr;
|
||||
}
|
||||
return;
|
||||
@@ -471,6 +562,7 @@ Field_Flags_Struct :: Field_Flags{
|
||||
};
|
||||
Field_Flags_Record_Poly_Params :: Field_Flags{
|
||||
.Typeid_Token,
|
||||
.Default_Parameters,
|
||||
};
|
||||
Field_Flags_Signature :: Field_Flags{
|
||||
.Ellipsis,
|
||||
@@ -539,12 +631,6 @@ Distinct_Type :: struct {
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
Opaque_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: tokenizer.Token_Kind,
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
Poly_Type :: struct {
|
||||
using node: Expr,
|
||||
dollar: tokenizer.Pos,
|
||||
@@ -593,23 +679,23 @@ Struct_Type :: struct {
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
fields: ^Field_List,
|
||||
name_count: int,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
is_packed: bool,
|
||||
is_raw_union: bool,
|
||||
fields: ^Field_List,
|
||||
name_count: int,
|
||||
}
|
||||
|
||||
Union_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
variants: []^Expr,
|
||||
where_token: tokenizer.Token,
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
is_maybe: bool,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
is_maybe: bool,
|
||||
variants: []^Expr,
|
||||
}
|
||||
|
||||
Enum_Type :: struct {
|
||||
@@ -623,15 +709,6 @@ Enum_Type :: struct {
|
||||
is_using: bool,
|
||||
}
|
||||
|
||||
Bit_Field_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
align: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
fields: []^Field_Value, // Field_Value with ':' rather than '='
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Bit_Set_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: tokenizer.Pos,
|
||||
@@ -647,3 +724,10 @@ Map_Type :: struct {
|
||||
key: ^Expr,
|
||||
value: ^Expr,
|
||||
}
|
||||
|
||||
|
||||
Relative_Type :: struct {
|
||||
using node: Expr,
|
||||
tag: ^Expr,
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
+22
-65
@@ -67,6 +67,11 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
align = ti.align;
|
||||
}
|
||||
|
||||
switch in node.derived {
|
||||
case Package, File:
|
||||
panic("Cannot clone this node type");
|
||||
}
|
||||
|
||||
res := cast(^Node)mem.alloc(size, align);
|
||||
src: rawptr = node;
|
||||
if node.derived != nil {
|
||||
@@ -74,8 +79,9 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
}
|
||||
mem.copy(res, src, size);
|
||||
res.derived.data = rawptr(res);
|
||||
res.derived.id = node.derived.id;
|
||||
|
||||
switch n in node.derived {
|
||||
switch r in &res.derived {
|
||||
case Bad_Expr:
|
||||
case Ident:
|
||||
case Implicit:
|
||||
@@ -83,155 +89,131 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
case Basic_Lit:
|
||||
|
||||
case Ellipsis:
|
||||
r := cast(^Ellipsis)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Proc_Lit:
|
||||
r := cast(^Proc_Lit)res;
|
||||
r.type = auto_cast clone(r.type);
|
||||
r.body = clone(r.body);
|
||||
case Comp_Lit:
|
||||
r := cast(^Comp_Lit)res;
|
||||
r.type = clone(r.type);
|
||||
r.elems = clone(r.elems);
|
||||
|
||||
case Tag_Expr:
|
||||
r := cast(^Tag_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Unary_Expr:
|
||||
r := cast(^Unary_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Binary_Expr:
|
||||
r := cast(^Binary_Expr)res;
|
||||
r.left = clone(r.left);
|
||||
r.right = clone(r.right);
|
||||
case Paren_Expr:
|
||||
r := cast(^Paren_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Selector_Expr:
|
||||
r := cast(^Selector_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.field = auto_cast clone(r.field);
|
||||
case Implicit_Selector_Expr:
|
||||
r.field = auto_cast clone(r.field);
|
||||
case Selector_Call_Expr:
|
||||
r.expr = clone(r.expr);
|
||||
r.call = auto_cast clone(r.call);
|
||||
case Index_Expr:
|
||||
r := cast(^Index_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.index = clone(r.index);
|
||||
case Deref_Expr:
|
||||
r := cast(^Deref_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Slice_Expr:
|
||||
r := cast(^Slice_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.low = clone(r.low);
|
||||
r.high = clone(r.high);
|
||||
case Call_Expr:
|
||||
r := cast(^Call_Expr)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.args = clone(r.args);
|
||||
case Field_Value:
|
||||
r := cast(^Field_Value)res;
|
||||
r.field = clone(r.field);
|
||||
r.value = clone(r.value);
|
||||
case Ternary_Expr:
|
||||
r := cast(^Ternary_Expr)res;
|
||||
r.cond = clone(r.cond);
|
||||
r.x = clone(r.x);
|
||||
r.y = clone(r.y);
|
||||
case Ternary_If_Expr:
|
||||
r := cast(^Ternary_If_Expr)res;
|
||||
r.x = clone(r.x);
|
||||
r.cond = clone(r.cond);
|
||||
r.y = clone(r.y);
|
||||
case Ternary_When_Expr:
|
||||
r := cast(^Ternary_When_Expr)res;
|
||||
r.x = clone(r.x);
|
||||
r.cond = clone(r.cond);
|
||||
r.y = clone(r.y);
|
||||
case Type_Assertion:
|
||||
r := cast(^Type_Assertion)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.type = clone(r.type);
|
||||
case Type_Cast:
|
||||
r := cast(^Type_Cast)res;
|
||||
r.type = clone(r.type);
|
||||
r.expr = clone(r.expr);
|
||||
case Auto_Cast:
|
||||
r := cast(^Auto_Cast)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Inline_Asm_Expr:
|
||||
r.param_types = clone(r.param_types);
|
||||
r.return_type = clone(r.return_type);
|
||||
r.constraints_string = clone(r.constraints_string);
|
||||
r.asm_string = clone(r.asm_string);
|
||||
|
||||
case Bad_Stmt:
|
||||
// empty
|
||||
case Empty_Stmt:
|
||||
// empty
|
||||
case Expr_Stmt:
|
||||
r := cast(^Expr_Stmt)res;
|
||||
r.expr = clone(r.expr);
|
||||
case Tag_Stmt:
|
||||
r := cast(^Expr_Stmt)res;
|
||||
r.expr = clone(r.expr);
|
||||
r.stmt = clone(r.stmt);
|
||||
|
||||
case Assign_Stmt:
|
||||
r := cast(^Assign_Stmt)res;
|
||||
r.lhs = clone(r.lhs);
|
||||
r.rhs = clone(r.rhs);
|
||||
case Block_Stmt:
|
||||
r := cast(^Block_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.stmts = clone(r.stmts);
|
||||
case If_Stmt:
|
||||
r := cast(^If_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.init = clone(r.init);
|
||||
r.cond = clone(r.cond);
|
||||
r.body = clone(r.body);
|
||||
r.else_stmt = clone(r.else_stmt);
|
||||
case When_Stmt:
|
||||
r := cast(^When_Stmt)res;
|
||||
r.cond = clone(r.cond);
|
||||
r.body = clone(r.body);
|
||||
r.else_stmt = clone(r.else_stmt);
|
||||
case Return_Stmt:
|
||||
r := cast(^Return_Stmt)res;
|
||||
r.results = clone(r.results);
|
||||
case Defer_Stmt:
|
||||
r := cast(^Defer_Stmt)res;
|
||||
r.stmt = clone(r.stmt);
|
||||
case For_Stmt:
|
||||
r := cast(^For_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.init = clone(r.init);
|
||||
r.cond = clone(r.cond);
|
||||
r.post = clone(r.post);
|
||||
r.body = clone(r.body);
|
||||
case Range_Stmt:
|
||||
r := cast(^Range_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.val0 = clone(r.val0);
|
||||
r.val1 = clone(r.val1);
|
||||
r.vals = clone(r.vals);
|
||||
r.expr = clone(r.expr);
|
||||
r.body = clone(r.body);
|
||||
case Case_Clause:
|
||||
r := cast(^Case_Clause)res;
|
||||
r.list = clone(r.list);
|
||||
r.body = clone(r.body);
|
||||
case Switch_Stmt:
|
||||
r := cast(^Switch_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.init = clone(r.init);
|
||||
r.cond = clone(r.cond);
|
||||
r.body = clone(r.body);
|
||||
case Type_Switch_Stmt:
|
||||
r := cast(^Type_Switch_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
r.tag = clone(r.tag);
|
||||
r.expr = clone(r.expr);
|
||||
r.body = clone(r.body);
|
||||
case Branch_Stmt:
|
||||
r := cast(^Branch_Stmt)res;
|
||||
r.label = auto_cast clone(r.label);
|
||||
case Using_Stmt:
|
||||
r := cast(^Using_Stmt)res;
|
||||
r.list = clone(r.list);
|
||||
case Bad_Decl:
|
||||
case Value_Decl:
|
||||
r := cast(^Value_Decl)res;
|
||||
r.attributes = clone(r.attributes);
|
||||
r.names = clone(r.names);
|
||||
r.type = clone(r.type);
|
||||
@@ -239,85 +221,60 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
case Package_Decl:
|
||||
case Import_Decl:
|
||||
case Foreign_Block_Decl:
|
||||
r := cast(^Foreign_Block_Decl)res;
|
||||
r.attributes = clone(r.attributes);
|
||||
r.foreign_library = clone(r.foreign_library);
|
||||
r.body = clone(r.body);
|
||||
case Foreign_Import_Decl:
|
||||
r := cast(^Foreign_Import_Decl)res;
|
||||
r.name = auto_cast clone(r.name);
|
||||
case Proc_Group:
|
||||
r := cast(^Proc_Group)res;
|
||||
r.args = clone(r.args);
|
||||
case Attribute:
|
||||
r := cast(^Attribute)res;
|
||||
r.elems = clone(r.elems);
|
||||
case Field:
|
||||
r := cast(^Field)res;
|
||||
r.names = clone(r.names);
|
||||
r.type = clone(r.type);
|
||||
r.default_value = clone(r.default_value);
|
||||
case Field_List:
|
||||
r := cast(^Field_List)res;
|
||||
r.list = clone(r.list);
|
||||
case Typeid_Type:
|
||||
r := cast(^Typeid_Type)res;
|
||||
r.specialization = clone(r.specialization);
|
||||
case Helper_Type:
|
||||
r := cast(^Helper_Type)res;
|
||||
r.type = clone(r.type);
|
||||
case Distinct_Type:
|
||||
r := cast(^Distinct_Type)res;
|
||||
r.type = clone(r.type);
|
||||
case Opaque_Type:
|
||||
r := cast(^Opaque_Type)res;
|
||||
r.type = clone(r.type);
|
||||
case Poly_Type:
|
||||
r := cast(^Poly_Type)res;
|
||||
r.type = auto_cast clone(r.type);
|
||||
r.specialization = clone(r.specialization);
|
||||
case Proc_Type:
|
||||
r := cast(^Proc_Type)res;
|
||||
r.params = auto_cast clone(r.params);
|
||||
r.results = auto_cast clone(r.results);
|
||||
case Pointer_Type:
|
||||
r := cast(^Pointer_Type)res;
|
||||
r.elem = clone(r.elem);
|
||||
case Array_Type:
|
||||
r := cast(^Array_Type)res;
|
||||
r.len = clone(r.len);
|
||||
r.elem = clone(r.elem);
|
||||
case Dynamic_Array_Type:
|
||||
r := cast(^Dynamic_Array_Type)res;
|
||||
r.elem = clone(r.elem);
|
||||
case Struct_Type:
|
||||
r := cast(^Struct_Type)res;
|
||||
r.poly_params = auto_cast clone(r.poly_params);
|
||||
r.align = clone(r.align);
|
||||
r.fields = auto_cast clone(r.fields);
|
||||
case Union_Type:
|
||||
r := cast(^Union_Type)res;
|
||||
r.poly_params = auto_cast clone(r.poly_params);
|
||||
r.align = clone(r.align);
|
||||
r.variants = clone(r.variants);
|
||||
case Enum_Type:
|
||||
r := cast(^Enum_Type)res;
|
||||
r.base_type = clone(r.base_type);
|
||||
r.fields = clone(r.fields);
|
||||
case Bit_Field_Type:
|
||||
r := cast(^Bit_Field_Type)res;
|
||||
r.fields = clone(r.fields);
|
||||
case Bit_Set_Type:
|
||||
r := cast(^Bit_Set_Type)res;
|
||||
r.elem = clone(r.elem);
|
||||
r.underlying = clone(r.underlying);
|
||||
case Map_Type:
|
||||
r := cast(^Map_Type)res;
|
||||
r.key = clone(r.key);
|
||||
r.value = clone(r.value);
|
||||
|
||||
case:
|
||||
fmt.panicf("Unhandled node kind: %T", n);
|
||||
fmt.panicf("Unhandled node kind: %T", r);
|
||||
}
|
||||
|
||||
return res;
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package odin_ast
|
||||
|
||||
import "core:odin/tokenizer"
|
||||
|
||||
Package_Kind :: enum {
|
||||
Normal,
|
||||
Runtime,
|
||||
Init,
|
||||
}
|
||||
|
||||
Package :: struct {
|
||||
kind: Package_Kind,
|
||||
id: int,
|
||||
name: string,
|
||||
fullpath: string,
|
||||
files: []^File,
|
||||
|
||||
user_data: rawptr,
|
||||
}
|
||||
|
||||
File :: struct {
|
||||
id: int,
|
||||
pkg: ^Package,
|
||||
|
||||
fullpath: string,
|
||||
src: []byte,
|
||||
|
||||
pkg_decl: ^Package_Decl,
|
||||
pkg_token: tokenizer.Token,
|
||||
pkg_name: string,
|
||||
|
||||
decls: [dynamic]^Stmt,
|
||||
imports: [dynamic]^Import_Decl,
|
||||
directive_count: int,
|
||||
|
||||
comments: [dynamic]^Comment_Group,
|
||||
|
||||
syntax_warning_count: int,
|
||||
syntax_error_count: int,
|
||||
}
|
||||
@@ -0,0 +1,405 @@
|
||||
package odin_ast
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
// A Visitor's visit procedure is invoked for each node encountered by walk
|
||||
// If the result visitor is not nil, walk visits each of the children of node with the new visitor,
|
||||
// followed by a call of v.visit(v, nil)
|
||||
Visitor :: struct {
|
||||
visit: proc(visitor: ^Visitor, node: ^Node) -> ^Visitor,
|
||||
data: rawptr,
|
||||
}
|
||||
|
||||
|
||||
// inspect traverses an AST in depth-first order
|
||||
// It starts by calling f(node), and node must be non-nil
|
||||
// If f returns true, inspect invokes f recursively for each of the non-nil children of node,
|
||||
// followed by a call of f(nil)
|
||||
inspect :: proc(node: ^Node, f: proc(^Node) -> bool) {
|
||||
v := &Visitor{
|
||||
visit = proc(v: ^Visitor, node: ^Node) -> ^Visitor {
|
||||
f := (proc(^Node) -> bool)(v.data);
|
||||
if f(node) {
|
||||
return v;
|
||||
}
|
||||
return nil;
|
||||
},
|
||||
data = rawptr(f),
|
||||
};
|
||||
walk(v, node);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// walk traverses an AST in depth-first order: It starts by calling v.visit(v, node), and node must not be nil
|
||||
// If the visitor returned by v.visit(v, node) is not nil, walk is invoked recursively with the new visitor for
|
||||
// each of the non-nil children of node, followed by a call of the new visit procedure
|
||||
walk :: proc(v: ^Visitor, node: ^Node) {
|
||||
walk_expr_list :: proc(v: ^Visitor, list: []^Expr) {
|
||||
for x in list {
|
||||
walk(v, x);
|
||||
}
|
||||
}
|
||||
|
||||
walk_stmt_list :: proc(v: ^Visitor, list: []^Stmt) {
|
||||
for x in list {
|
||||
walk(v, x);
|
||||
}
|
||||
}
|
||||
walk_attribute_list :: proc(v: ^Visitor, list: []^Attribute) {
|
||||
for x in list {
|
||||
walk(v, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
v := v;
|
||||
if v = v->visit(node); v == nil {
|
||||
return;
|
||||
}
|
||||
|
||||
switch n in &node.derived {
|
||||
case File:
|
||||
if n.docs != nil {
|
||||
walk(v, n.docs);
|
||||
}
|
||||
walk_stmt_list(v, n.decls[:]);
|
||||
case Package:
|
||||
for _, f in n.files {
|
||||
walk(v, f);
|
||||
}
|
||||
|
||||
case Comment_Group:
|
||||
// empty
|
||||
case Bad_Expr:
|
||||
case Ident:
|
||||
case Implicit:
|
||||
case Undef:
|
||||
case Basic_Lit:
|
||||
case Basic_Directive:
|
||||
case Ellipsis:
|
||||
if n.expr != nil {
|
||||
walk(v, n.expr);
|
||||
}
|
||||
case Proc_Lit:
|
||||
walk(v, n.type);
|
||||
walk(v, n.body);
|
||||
walk_expr_list(v, n.where_clauses);
|
||||
case Comp_Lit:
|
||||
if n.type != nil {
|
||||
walk(v, n.type);
|
||||
}
|
||||
walk_expr_list(v, n.elems);
|
||||
case Tag_Expr:
|
||||
walk(v, n.expr);
|
||||
case Unary_Expr:
|
||||
walk(v, n.expr);
|
||||
case Binary_Expr:
|
||||
walk(v, n.left);
|
||||
walk(v, n.right);
|
||||
case Paren_Expr:
|
||||
walk(v, n.expr);
|
||||
case Selector_Expr:
|
||||
walk(v, n.expr);
|
||||
walk(v, n.field);
|
||||
case Implicit_Selector_Expr:
|
||||
walk(v, n.field);
|
||||
case Selector_Call_Expr:
|
||||
walk(v, n.expr);
|
||||
walk(v, n.call);
|
||||
case Index_Expr:
|
||||
walk(v, n.expr);
|
||||
walk(v, n.index);
|
||||
case Deref_Expr:
|
||||
walk(v, n.expr);
|
||||
case Slice_Expr:
|
||||
walk(v, n.expr);
|
||||
if n.low != nil {
|
||||
walk(v, n.low);
|
||||
}
|
||||
if n.high != nil {
|
||||
walk(v, n.high);
|
||||
}
|
||||
case Call_Expr:
|
||||
walk(v, n.expr);
|
||||
walk_expr_list(v, n.args);
|
||||
case Field_Value:
|
||||
walk(v, n.field);
|
||||
walk(v, n.value);
|
||||
case Ternary_Expr:
|
||||
walk(v, n.cond);
|
||||
walk(v, n.x);
|
||||
walk(v, n.y);
|
||||
case Ternary_If_Expr:
|
||||
walk(v, n.x);
|
||||
walk(v, n.cond);
|
||||
walk(v, n.y);
|
||||
case Ternary_When_Expr:
|
||||
walk(v, n.x);
|
||||
walk(v, n.cond);
|
||||
walk(v, n.y);
|
||||
case Type_Assertion:
|
||||
walk(v, n.expr);
|
||||
if n.type != nil {
|
||||
walk(v, n.type);
|
||||
}
|
||||
case Type_Cast:
|
||||
walk(v, n.type);
|
||||
walk(v, n.expr);
|
||||
case Auto_Cast:
|
||||
walk(v, n.expr);
|
||||
case Inline_Asm_Expr:
|
||||
walk_expr_list(v, n.param_types);
|
||||
walk(v, n.return_type);
|
||||
walk(v, n.constraints_string);
|
||||
walk(v, n.asm_string);
|
||||
|
||||
|
||||
case Bad_Stmt:
|
||||
case Empty_Stmt:
|
||||
case Expr_Stmt:
|
||||
walk(v, n.expr);
|
||||
case Tag_Stmt:
|
||||
walk(v, n.stmt);
|
||||
case Assign_Stmt:
|
||||
walk_expr_list(v, n.lhs);
|
||||
walk_expr_list(v, n.rhs);
|
||||
case Block_Stmt:
|
||||
if n.label != nil {
|
||||
walk(v, n.label);
|
||||
}
|
||||
walk_stmt_list(v, n.stmts);
|
||||
case If_Stmt:
|
||||
if n.label != nil {
|
||||
walk(v, n.label);
|
||||
}
|
||||
if n.init != nil {
|
||||
walk(v, n.init);
|
||||
}
|
||||
walk(v, n.cond);
|
||||
walk(v, n.body);
|
||||
if n.else_stmt != nil {
|
||||
walk(v, n.else_stmt);
|
||||
}
|
||||
case When_Stmt:
|
||||
walk(v, n.cond);
|
||||
walk(v, n.body);
|
||||
if n.else_stmt != nil {
|
||||
walk(v, n.else_stmt);
|
||||
}
|
||||
case Return_Stmt:
|
||||
walk_expr_list(v, n.results);
|
||||
case Defer_Stmt:
|
||||
walk(v, n.stmt);
|
||||
case For_Stmt:
|
||||
if n.label != nil {
|
||||
walk(v, n.label);
|
||||
}
|
||||
if n.init != nil {
|
||||
walk(v, n.init);
|
||||
}
|
||||
if n.cond != nil {
|
||||
walk(v, n.cond);
|
||||
}
|
||||
if n.post != nil {
|
||||
walk(v, n.post);
|
||||
}
|
||||
walk(v, n.body);
|
||||
case Range_Stmt:
|
||||
if n.label != nil {
|
||||
walk(v, n.label);
|
||||
}
|
||||
for val in n.vals {
|
||||
if val != nil {
|
||||
walk(v, val);
|
||||
}
|
||||
}
|
||||
walk(v, n.expr);
|
||||
walk(v, n.body);
|
||||
case Inline_Range_Stmt:
|
||||
if n.label != nil {
|
||||
walk(v, n.label);
|
||||
}
|
||||
if n.val0 != nil {
|
||||
walk(v, n.val0);
|
||||
}
|
||||
if n.val1 != nil {
|
||||
walk(v, n.val1);
|
||||
}
|
||||
walk(v, n.expr);
|
||||
walk(v, n.body);
|
||||
case Case_Clause:
|
||||
walk_expr_list(v, n.list);
|
||||
walk_stmt_list(v, n.body);
|
||||
case Switch_Stmt:
|
||||
if n.label != nil {
|
||||
walk(v, n.label);
|
||||
}
|
||||
if n.init != nil {
|
||||
walk(v, n.init);
|
||||
}
|
||||
if n.cond != nil {
|
||||
walk(v, n.cond);
|
||||
}
|
||||
walk(v, n.body);
|
||||
case Type_Switch_Stmt:
|
||||
if n.label != nil {
|
||||
walk(v, n.label);
|
||||
}
|
||||
if n.tag != nil {
|
||||
walk(v, n.tag);
|
||||
}
|
||||
if n.expr != nil {
|
||||
walk(v, n.expr);
|
||||
}
|
||||
walk(v, n.body);
|
||||
case Branch_Stmt:
|
||||
if n.label != nil {
|
||||
walk(v, n.label);
|
||||
}
|
||||
case Using_Stmt:
|
||||
walk_expr_list(v, n.list);
|
||||
|
||||
|
||||
case Bad_Decl:
|
||||
case Value_Decl:
|
||||
if n.docs != nil {
|
||||
walk(v, n.docs);
|
||||
}
|
||||
walk_attribute_list(v, n.attributes[:]);
|
||||
walk_expr_list(v, n.names);
|
||||
if n.type != nil {
|
||||
walk(v, n.type);
|
||||
}
|
||||
walk_expr_list(v, n.values);
|
||||
if n.comment != nil {
|
||||
walk(v, n.comment);
|
||||
}
|
||||
case Package_Decl:
|
||||
if n.docs != nil {
|
||||
walk(v, n.docs);
|
||||
}
|
||||
if n.comment != nil {
|
||||
walk(v, n.comment);
|
||||
}
|
||||
case Import_Decl:
|
||||
if n.docs != nil {
|
||||
walk(v, n.docs);
|
||||
}
|
||||
if n.comment != nil {
|
||||
walk(v, n.comment);
|
||||
}
|
||||
case Foreign_Block_Decl:
|
||||
if n.docs != nil {
|
||||
walk(v, n.docs);
|
||||
}
|
||||
walk_attribute_list(v, n.attributes[:]);
|
||||
if n.foreign_library != nil {
|
||||
walk(v, n.foreign_library);
|
||||
}
|
||||
walk(v, n.body);
|
||||
case Foreign_Import_Decl:
|
||||
if n.docs != nil {
|
||||
walk(v, n.docs);
|
||||
}
|
||||
walk_attribute_list(v, n.attributes[:]);
|
||||
walk(v, n.name);
|
||||
if n.comment != nil {
|
||||
walk(v, n.comment);
|
||||
}
|
||||
|
||||
case Proc_Group:
|
||||
walk_expr_list(v, n.args);
|
||||
case Attribute:
|
||||
walk_expr_list(v, n.elems);
|
||||
case Field:
|
||||
if n.docs != nil {
|
||||
walk(v, n.docs);
|
||||
}
|
||||
walk_expr_list(v, n.names);
|
||||
if n.type != nil {
|
||||
walk(v, n.type);
|
||||
}
|
||||
if n.default_value != nil {
|
||||
walk(v, n.default_value);
|
||||
}
|
||||
if n.comment != nil {
|
||||
walk(v, n.comment);
|
||||
}
|
||||
case Field_List:
|
||||
for x in n.list {
|
||||
walk(v, x);
|
||||
}
|
||||
case Typeid_Type:
|
||||
if n.specialization != nil {
|
||||
walk(v, n.specialization);
|
||||
}
|
||||
case Helper_Type:
|
||||
walk(v, n.type);
|
||||
case Distinct_Type:
|
||||
walk(v, n.type);
|
||||
case Poly_Type:
|
||||
walk(v, n.type);
|
||||
if n.specialization != nil {
|
||||
walk(v, n.specialization);
|
||||
}
|
||||
case Proc_Type:
|
||||
walk(v, n.params);
|
||||
walk(v, n.results);
|
||||
case Pointer_Type:
|
||||
walk(v, n.elem);
|
||||
case Array_Type:
|
||||
if n.tag != nil {
|
||||
walk(v, n.tag);
|
||||
}
|
||||
if n.len != nil {
|
||||
walk(v, n.len);
|
||||
}
|
||||
walk(v, n.elem);
|
||||
case Dynamic_Array_Type:
|
||||
if n.tag != nil {
|
||||
walk(v, n.tag);
|
||||
}
|
||||
walk(v, n.elem);
|
||||
case Struct_Type:
|
||||
if n.poly_params != nil {
|
||||
walk(v, n.poly_params);
|
||||
}
|
||||
if n.align != nil {
|
||||
walk(v, n.align);
|
||||
}
|
||||
walk_expr_list(v, n.where_clauses);
|
||||
walk(v, n.fields);
|
||||
case Union_Type:
|
||||
if n.poly_params != nil {
|
||||
walk(v, n.poly_params);
|
||||
}
|
||||
if n.align != nil {
|
||||
walk(v, n.align);
|
||||
}
|
||||
walk_expr_list(v, n.where_clauses);
|
||||
walk_expr_list(v, n.variants);
|
||||
case Enum_Type:
|
||||
if n.base_type != nil {
|
||||
walk(v, n.base_type);
|
||||
}
|
||||
walk_expr_list(v, n.fields);
|
||||
case Bit_Set_Type:
|
||||
walk(v, n.elem);
|
||||
if n.underlying != nil {
|
||||
walk(v, n.underlying);
|
||||
}
|
||||
case Map_Type:
|
||||
walk(v, n.key);
|
||||
walk(v, n.value);
|
||||
case Relative_Type:
|
||||
walk(v, n.tag);
|
||||
walk(v, n.type);
|
||||
|
||||
case:
|
||||
fmt.panicf("ast.walk: unexpected node type %T", n);
|
||||
}
|
||||
|
||||
v->visit(nil);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
package odin_doc_format
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Array :: struct($T: typeid) {
|
||||
offset: u32le,
|
||||
length: u32le,
|
||||
}
|
||||
|
||||
String :: distinct Array(byte);
|
||||
|
||||
Version_Type_Major :: 0;
|
||||
Version_Type_Minor :: 1;
|
||||
Version_Type_Patch :: 0;
|
||||
|
||||
Version_Type :: struct {
|
||||
major, minor, patch: u8,
|
||||
_: u8,
|
||||
};
|
||||
|
||||
Version_Type_Default :: Version_Type{
|
||||
major=Version_Type_Major,
|
||||
minor=Version_Type_Minor,
|
||||
patch=Version_Type_Patch,
|
||||
};
|
||||
|
||||
Magic_String :: "odindoc\x00";
|
||||
|
||||
Header_Base :: struct {
|
||||
magic: [8]byte,
|
||||
_: u32le,
|
||||
version: Version_Type,
|
||||
total_size: u32le,
|
||||
header_size: u32le,
|
||||
hash: u32le,
|
||||
}
|
||||
|
||||
Header :: struct {
|
||||
using base: Header_Base,
|
||||
|
||||
// NOTE: These arrays reserve the zero element as a sentinel value
|
||||
files: Array(File),
|
||||
pkgs: Array(Pkg),
|
||||
entities: Array(Entity),
|
||||
types: Array(Type),
|
||||
}
|
||||
|
||||
File_Index :: distinct u32le;
|
||||
Pkg_Index :: distinct u32le;
|
||||
Entity_Index :: distinct u32le;
|
||||
Type_Index :: distinct u32le;
|
||||
|
||||
|
||||
Position :: struct {
|
||||
file: File_Index,
|
||||
line: u32le,
|
||||
column: u32le,
|
||||
offset: u32le,
|
||||
};
|
||||
|
||||
File :: struct {
|
||||
pkg: Pkg_Index,
|
||||
name: String,
|
||||
}
|
||||
|
||||
Pkg_Flag :: enum u32le {
|
||||
Builtin = 0,
|
||||
Runtime = 1,
|
||||
Init = 2,
|
||||
}
|
||||
|
||||
Pkg_Flags :: distinct bit_set[Pkg_Flag; u32le];
|
||||
|
||||
Pkg :: struct {
|
||||
fullpath: String,
|
||||
name: String,
|
||||
flags: Pkg_Flags,
|
||||
docs: String,
|
||||
files: Array(File_Index),
|
||||
entities: Array(Entity_Index),
|
||||
}
|
||||
|
||||
Entity_Kind :: enum u32le {
|
||||
Invalid = 0,
|
||||
Constant = 1,
|
||||
Variable = 2,
|
||||
Type_Name = 3,
|
||||
Procedure = 4,
|
||||
Proc_Group = 5,
|
||||
Import_Name = 6,
|
||||
Library_Name = 7,
|
||||
}
|
||||
|
||||
Entity_Flag :: enum u32le {
|
||||
Foreign = 0,
|
||||
Export = 1,
|
||||
|
||||
Param_Using = 2,
|
||||
Param_Const = 3,
|
||||
Param_Auto_Cast = 4,
|
||||
Param_Ellipsis = 5,
|
||||
Param_CVararg = 6,
|
||||
Param_No_Alias = 7,
|
||||
|
||||
Type_Alias = 8,
|
||||
|
||||
Var_Thread_Local = 9,
|
||||
}
|
||||
|
||||
Entity_Flags :: distinct bit_set[Entity_Flag; u32le];
|
||||
|
||||
Entity :: struct {
|
||||
kind: Entity_Kind,
|
||||
flags: Entity_Flags,
|
||||
pos: Position,
|
||||
name: String,
|
||||
type: Type_Index,
|
||||
init_string: String,
|
||||
_: u32le,
|
||||
comment: String,
|
||||
docs: String,
|
||||
foreign_library: Entity_Index,
|
||||
link_name: String,
|
||||
attributes: Array(Attribute),
|
||||
grouped_entities: Array(Entity_Index), // Procedure Groups
|
||||
where_clauses: Array(String), // Procedures
|
||||
}
|
||||
|
||||
Attribute :: struct {
|
||||
name: String,
|
||||
value: String,
|
||||
}
|
||||
|
||||
Type_Kind :: enum u32le {
|
||||
Invalid = 0,
|
||||
Basic = 1,
|
||||
Named = 2,
|
||||
Generic = 3,
|
||||
Pointer = 4,
|
||||
Array = 5,
|
||||
Enumerated_Array = 6,
|
||||
Slice = 7,
|
||||
Dynamic_Array = 8,
|
||||
Map = 9,
|
||||
Struct = 10,
|
||||
Union = 11,
|
||||
Enum = 12,
|
||||
Tuple = 13,
|
||||
Proc = 14,
|
||||
Bit_Set = 15,
|
||||
Simd_Vector = 16,
|
||||
SOA_Struct_Fixed = 17,
|
||||
SOA_Struct_Slice = 18,
|
||||
SOA_Struct_Dynamic = 19,
|
||||
Relative_Pointer = 20,
|
||||
Relative_Slice = 21,
|
||||
}
|
||||
|
||||
Type_Elems_Cap :: 4;
|
||||
|
||||
Type :: struct {
|
||||
kind: Type_Kind,
|
||||
flags: u32le, // Type_Kind specific
|
||||
name: String,
|
||||
custom_align: String,
|
||||
|
||||
// Used by some types
|
||||
elem_count_len: u32le,
|
||||
elem_counts: [Type_Elems_Cap]i64le,
|
||||
|
||||
// Each of these is esed by some types, not all
|
||||
calling_convention: String, // Procedures
|
||||
types: Array(Type_Index),
|
||||
entities: Array(Entity_Index),
|
||||
polymorphic_params: Type_Index, // Struct, Union
|
||||
where_clauses: Array(String), // Struct, Union
|
||||
}
|
||||
|
||||
Type_Flags_Basic :: distinct bit_set[Type_Flag_Basic; u32le];
|
||||
Type_Flag_Basic :: enum u32le {
|
||||
Untyped = 1,
|
||||
}
|
||||
|
||||
Type_Flags_Struct :: distinct bit_set[Type_Flag_Struct; u32le];
|
||||
Type_Flag_Struct :: enum u32le {
|
||||
Polymorphic = 0,
|
||||
Packed = 1,
|
||||
Raw_Union = 2,
|
||||
}
|
||||
|
||||
Type_Flags_Union :: distinct bit_set[Type_Flag_Union; u32le];
|
||||
Type_Flag_Union :: enum u32le {
|
||||
Polymorphic = 0,
|
||||
No_Nil = 1,
|
||||
Maybe = 2,
|
||||
}
|
||||
|
||||
Type_Flags_Proc :: distinct bit_set[Type_Flag_Proc; u32le];
|
||||
Type_Flag_Proc :: enum u32le {
|
||||
Polymorphic = 0,
|
||||
Diverging = 1,
|
||||
Optional_Ok = 2,
|
||||
Variadic = 3,
|
||||
C_Vararg = 4,
|
||||
}
|
||||
|
||||
Type_Flags_Bit_Set :: distinct bit_set[Type_Flag_Bit_Set; u32le];
|
||||
Type_Flag_Bit_Set :: enum u32le {
|
||||
Range = 1,
|
||||
Op_Lt = 2,
|
||||
Op_Lt_Eq = 3,
|
||||
Underlying_Type = 4,
|
||||
}
|
||||
|
||||
from_array :: proc(base: ^Header_Base, a: $A/Array($T)) -> []T {
|
||||
s: mem.Raw_Slice;
|
||||
s.data = rawptr(uintptr(base) + uintptr(a.offset));
|
||||
s.len = int(a.length);
|
||||
return transmute([]T)s;
|
||||
}
|
||||
from_string :: proc(base: ^Header_Base, s: String) -> string {
|
||||
return string(from_array(base, s));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reader_Error :: enum {
|
||||
None,
|
||||
Header_Too_Small,
|
||||
Invalid_Magic,
|
||||
Data_Too_Small,
|
||||
Invalid_Version,
|
||||
}
|
||||
|
||||
read_from_bytes :: proc(data: []byte) -> (h: ^Header, err: Reader_Error) {
|
||||
if len(data) < size_of(Header_Base) {
|
||||
err = .Header_Too_Small;
|
||||
return;
|
||||
}
|
||||
header_base := (^Header_Base)(raw_data(data));
|
||||
if header_base.magic != Magic_String {
|
||||
err = .Invalid_Magic;
|
||||
return;
|
||||
}
|
||||
if len(data) < int(header_base.total_size) {
|
||||
err = .Data_Too_Small;
|
||||
return;
|
||||
}
|
||||
if header_base.version != Version_Type_Default {
|
||||
err = .Invalid_Version;
|
||||
return;
|
||||
}
|
||||
h = (^Header)(header_base);
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package odin_parser
|
||||
|
||||
import "core:odin/tokenizer"
|
||||
import "core:odin/ast"
|
||||
import "core:path/filepath"
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:slice"
|
||||
|
||||
collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
|
||||
NO_POS :: tokenizer.Pos{};
|
||||
|
||||
pkg_path, pkg_path_ok := filepath.abs(path);
|
||||
if !pkg_path_ok {
|
||||
return;
|
||||
}
|
||||
|
||||
path_pattern := fmt.tprintf("%s/*.odin", pkg_path);
|
||||
matches, err := filepath.glob(path_pattern);
|
||||
defer delete(matches);
|
||||
|
||||
if err != nil {
|
||||
return;
|
||||
}
|
||||
|
||||
pkg = ast.new(ast.Package, NO_POS, NO_POS);
|
||||
pkg.fullpath = pkg_path;
|
||||
|
||||
for match in matches {
|
||||
src: []byte;
|
||||
fullpath, ok := filepath.abs(match);
|
||||
if !ok {
|
||||
return;
|
||||
}
|
||||
src, ok = os.read_entire_file(fullpath);
|
||||
if !ok {
|
||||
delete(fullpath);
|
||||
return;
|
||||
}
|
||||
file := ast.new(ast.File, NO_POS, NO_POS);
|
||||
file.pkg = pkg;
|
||||
file.src = src;
|
||||
file.fullpath = fullpath;
|
||||
pkg.files[fullpath] = file;
|
||||
}
|
||||
|
||||
success = true;
|
||||
return;
|
||||
}
|
||||
|
||||
parse_package :: proc(pkg: ^ast.Package, p: ^Parser = nil) -> bool {
|
||||
p := p;
|
||||
if p == nil {
|
||||
p = &Parser{};
|
||||
p^ = default_parser();
|
||||
}
|
||||
|
||||
ok := true;
|
||||
|
||||
files := make([]^ast.File, len(pkg.files), context.temp_allocator);
|
||||
i := 0;
|
||||
for _, file in pkg.files {
|
||||
files[i] = file;
|
||||
i += 1;
|
||||
}
|
||||
slice.sort(files);
|
||||
|
||||
for file in files {
|
||||
if !parse_file(p, file) {
|
||||
ok = false;
|
||||
}
|
||||
if pkg.name == "" {
|
||||
pkg.name = file.pkg_decl.name;
|
||||
} else if pkg.name != file.pkg_decl.name {
|
||||
error(p, file.pkg_decl.pos, "different package name, expected '%s', got '%s'", pkg.name, file.pkg_decl.name);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
parse_package_from_path :: proc(path: string, p: ^Parser = nil) -> (pkg: ^ast.Package, ok: bool) {
|
||||
pkg, ok = collect_package(path);
|
||||
if !ok {
|
||||
return;
|
||||
}
|
||||
ok = parse_package(pkg, p);
|
||||
return;
|
||||
}
|
||||
+509
-227
File diff suppressed because it is too large
Load Diff
+113
-116
@@ -34,127 +34,118 @@ Token_Kind :: enum u32 {
|
||||
Comment,
|
||||
|
||||
B_Literal_Begin,
|
||||
Ident,
|
||||
Integer,
|
||||
Float,
|
||||
Imag,
|
||||
Rune,
|
||||
String,
|
||||
Ident, // main
|
||||
Integer, // 12345
|
||||
Float, // 123.45
|
||||
Imag, // 123.45i
|
||||
Rune, // 'a'
|
||||
String, // "abc"
|
||||
B_Literal_End,
|
||||
|
||||
B_Operator_Begin,
|
||||
Eq,
|
||||
Not,
|
||||
Hash,
|
||||
At,
|
||||
Dollar,
|
||||
Pointer,
|
||||
Question,
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Quo,
|
||||
Mod,
|
||||
Mod_Mod,
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
And_Not,
|
||||
Shl,
|
||||
Shr,
|
||||
Eq, // =
|
||||
Not, // !
|
||||
Hash, // #
|
||||
At, // @
|
||||
Dollar, // $
|
||||
Pointer, // ^
|
||||
Question, // ?
|
||||
Add, // +
|
||||
Sub, // -
|
||||
Mul, // *
|
||||
Quo, // /
|
||||
Mod, // %
|
||||
Mod_Mod, // %%
|
||||
And, // &
|
||||
Or, // |
|
||||
Xor, // ~
|
||||
And_Not, // &~
|
||||
Shl, // <<
|
||||
Shr, // >>
|
||||
|
||||
Cmp_And,
|
||||
Cmp_Or,
|
||||
Cmp_And, // &&
|
||||
Cmp_Or, // ||
|
||||
|
||||
B_Assign_Op_Begin,
|
||||
Add_Eq,
|
||||
Sub_Eq,
|
||||
Mul_Eq,
|
||||
Quo_Eq,
|
||||
Mod_Eq,
|
||||
Mod_Mod_Eq,
|
||||
And_Eq,
|
||||
Or_Eq,
|
||||
Xor_Eq,
|
||||
And_Not_Eq,
|
||||
Shl_Eq,
|
||||
Shr_Eq,
|
||||
Cmp_And_Eq,
|
||||
Cmp_Or_Eq,
|
||||
Add_Eq, // +=
|
||||
Sub_Eq, // -=
|
||||
Mul_Eq, // *=
|
||||
Quo_Eq, // /=
|
||||
Mod_Eq, // %=
|
||||
Mod_Mod_Eq, // %%=
|
||||
And_Eq, // &=
|
||||
Or_Eq, // |=
|
||||
Xor_Eq, // ~=
|
||||
And_Not_Eq, // &~=
|
||||
Shl_Eq, // <<=
|
||||
Shr_Eq, // >>=
|
||||
Cmp_And_Eq, // &&=
|
||||
Cmp_Or_Eq, // ||=
|
||||
B_Assign_Op_End,
|
||||
|
||||
Arrow_Right,
|
||||
Arrow_Left,
|
||||
Double_Arrow_Right,
|
||||
Undef,
|
||||
Arrow_Right, // ->
|
||||
Undef, // ---
|
||||
|
||||
B_Comparison_Begin,
|
||||
Cmp_Eq,
|
||||
Not_Eq,
|
||||
Lt,
|
||||
Gt,
|
||||
Lt_Eq,
|
||||
Gt_Eq,
|
||||
Cmp_Eq, // ==
|
||||
Not_Eq, // !=
|
||||
Lt, // <
|
||||
Gt, // >
|
||||
Lt_Eq, // <=
|
||||
Gt_Eq, // >=
|
||||
B_Comparison_End,
|
||||
|
||||
Open_Paren,
|
||||
Close_Paren,
|
||||
Open_Bracket,
|
||||
Close_Bracket,
|
||||
Open_Brace,
|
||||
Close_Brace,
|
||||
Colon,
|
||||
Semicolon,
|
||||
Period,
|
||||
Comma,
|
||||
Ellipsis,
|
||||
Range_Half,
|
||||
Back_Slash,
|
||||
Open_Paren, // (
|
||||
Close_Paren, // )
|
||||
Open_Bracket, // [
|
||||
Close_Bracket, // ]
|
||||
Open_Brace, // {
|
||||
Close_Brace, // }
|
||||
Colon, // :
|
||||
Semicolon, // ;
|
||||
Period, // .
|
||||
Comma, // ,
|
||||
Ellipsis, // ..
|
||||
Range_Half, // ..<
|
||||
Back_Slash, // \
|
||||
B_Operator_End,
|
||||
|
||||
B_Keyword_Begin,
|
||||
Import,
|
||||
Foreign,
|
||||
Package,
|
||||
Typeid,
|
||||
When,
|
||||
Where,
|
||||
If,
|
||||
Else,
|
||||
For,
|
||||
Switch,
|
||||
In,
|
||||
Not_In,
|
||||
Do,
|
||||
Case,
|
||||
Break,
|
||||
Continue,
|
||||
Fallthrough,
|
||||
Defer,
|
||||
Return,
|
||||
Proc,
|
||||
Macro,
|
||||
Struct,
|
||||
Union,
|
||||
Enum,
|
||||
Bit_Field,
|
||||
Bit_Set,
|
||||
Map,
|
||||
Dynamic,
|
||||
Auto_Cast,
|
||||
Cast,
|
||||
Transmute,
|
||||
Distinct,
|
||||
Opaque,
|
||||
Using,
|
||||
Inline,
|
||||
No_Inline,
|
||||
Context,
|
||||
Size_Of,
|
||||
Align_Of,
|
||||
Offset_Of,
|
||||
Type_Of,
|
||||
Const,
|
||||
Import, // import
|
||||
Foreign, // foreign
|
||||
Package, // package
|
||||
Typeid, // typeid
|
||||
When, // when
|
||||
Where, // where
|
||||
If, // if
|
||||
Else, // else
|
||||
For, // for
|
||||
Switch, // switch
|
||||
In, // in
|
||||
Not_In, // not_in
|
||||
Do, // do
|
||||
Case, // case
|
||||
Break, // break
|
||||
Continue, // continue
|
||||
Fallthrough, // fallthrough
|
||||
Defer, // defer
|
||||
Return, // return
|
||||
Proc, // proc
|
||||
Struct, // struct
|
||||
Union, // union
|
||||
Enum, // enum
|
||||
Bit_Set, // bit_set
|
||||
Map, // map
|
||||
Dynamic, // dynamic
|
||||
Auto_Cast, // auto_cast
|
||||
Cast, // cast
|
||||
Transmute, // transmute
|
||||
Distinct, // distinct
|
||||
Using, // using
|
||||
Inline, // inline
|
||||
No_Inline, // no_inline
|
||||
Context, // context
|
||||
Asm, // asm
|
||||
B_Keyword_End,
|
||||
|
||||
COUNT,
|
||||
@@ -219,8 +210,6 @@ tokens := [Token_Kind.COUNT]string {
|
||||
"",
|
||||
|
||||
"->",
|
||||
"<-",
|
||||
"=>",
|
||||
"---",
|
||||
|
||||
"",
|
||||
@@ -268,11 +257,9 @@ tokens := [Token_Kind.COUNT]string {
|
||||
"defer",
|
||||
"return",
|
||||
"proc",
|
||||
"macro",
|
||||
"struct",
|
||||
"union",
|
||||
"enum",
|
||||
"bit_field",
|
||||
"bit_set",
|
||||
"map",
|
||||
"dynamic",
|
||||
@@ -280,21 +267,29 @@ tokens := [Token_Kind.COUNT]string {
|
||||
"cast",
|
||||
"transmute",
|
||||
"distinct",
|
||||
"opaque",
|
||||
"using",
|
||||
"inline",
|
||||
"no_inline",
|
||||
"context",
|
||||
"size_of",
|
||||
"align_of",
|
||||
"offset_of",
|
||||
"type_of",
|
||||
"const",
|
||||
"asm",
|
||||
"",
|
||||
};
|
||||
|
||||
custom_keyword_tokens: []string;
|
||||
|
||||
|
||||
is_newline :: proc(tok: Token) -> bool {
|
||||
return tok.kind == .Semicolon && tok.text == "\n";
|
||||
}
|
||||
|
||||
|
||||
token_to_string :: proc(tok: Token) -> string {
|
||||
if is_newline(tok) {
|
||||
return "newline";
|
||||
}
|
||||
return to_string(tok.kind);
|
||||
}
|
||||
|
||||
to_string :: proc(kind: Token_Kind) -> string {
|
||||
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
|
||||
return tokens[kind];
|
||||
@@ -318,6 +313,8 @@ is_operator :: proc(kind: Token_Kind) -> bool {
|
||||
return true;
|
||||
case .In, .Not_In:
|
||||
return true;
|
||||
case .If:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,31 @@
|
||||
package odin_tokenizer
|
||||
|
||||
import "core:fmt"
|
||||
import "core:unicode"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
|
||||
|
||||
Flag :: enum {
|
||||
Insert_Semicolon,
|
||||
}
|
||||
Flags :: distinct bit_set[Flag; u32];
|
||||
|
||||
Tokenizer :: struct {
|
||||
// Immutable data
|
||||
path: string,
|
||||
src: []byte,
|
||||
err: Error_Handler,
|
||||
|
||||
flags: Flags,
|
||||
|
||||
// Tokenizing state
|
||||
ch: rune,
|
||||
offset: int,
|
||||
read_offset: int,
|
||||
line_offset: int,
|
||||
line_count: int,
|
||||
insert_semicolon: bool,
|
||||
|
||||
// Mutable data
|
||||
error_count: int,
|
||||
@@ -105,11 +114,18 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
|
||||
}
|
||||
|
||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||
for t.ch == ' ' ||
|
||||
t.ch == '\t' ||
|
||||
t.ch == '\n' ||
|
||||
t.ch == '\r' {
|
||||
advance_rune(t);
|
||||
for {
|
||||
switch t.ch {
|
||||
case ' ', '\t', '\r':
|
||||
advance_rune(t);
|
||||
case '\n':
|
||||
if t.insert_semicolon {
|
||||
return;
|
||||
}
|
||||
advance_rune(t);
|
||||
case:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,12 +138,13 @@ is_letter :: proc(r: rune) -> bool {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// TODO(bill): Add unicode lookup tables
|
||||
return false;
|
||||
return unicode.is_letter(r);
|
||||
}
|
||||
is_digit :: proc(r: rune) -> bool {
|
||||
// TODO(bill): Add unicode lookup tables
|
||||
return '0' <= r && r <= '9';
|
||||
if '0' <= r && r <= '9' {
|
||||
return true;
|
||||
}
|
||||
return unicode.is_digit(r);
|
||||
}
|
||||
|
||||
|
||||
@@ -150,13 +167,21 @@ scan_comment :: proc(t: ^Tokenizer) -> string {
|
||||
|
||||
/* style comment */
|
||||
advance_rune(t);
|
||||
for t.ch >= 0 {
|
||||
nest := 1;
|
||||
for t.ch >= 0 && nest > 0 {
|
||||
ch := t.ch;
|
||||
advance_rune(t);
|
||||
if ch == '/' && t.ch == '*' {
|
||||
nest += 1;
|
||||
}
|
||||
|
||||
if ch == '*' && t.ch == '/' {
|
||||
nest -= 1;
|
||||
advance_rune(t);
|
||||
next = t.offset;
|
||||
break general;
|
||||
if nest == 0 {
|
||||
break general;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +235,7 @@ scan_raw_string :: proc(t: ^Tokenizer) -> string {
|
||||
|
||||
for {
|
||||
ch := t.ch;
|
||||
if ch == '\n' || ch < 0 {
|
||||
if ch == utf8.RUNE_EOF {
|
||||
error(t, offset, "raw string literal was not terminated");
|
||||
break;
|
||||
}
|
||||
@@ -241,7 +266,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
|
||||
n: int;
|
||||
base, max: u32;
|
||||
switch t.ch {
|
||||
case 'a', 'b', 'e', 'f', 'n', 't', 'v', '\\', '\'', '\"':
|
||||
case 'a', 'b', 'e', 'f', 'n', 't', 'v', 'r', '\\', '\'', '\"':
|
||||
advance_rune(t);
|
||||
return true;
|
||||
|
||||
@@ -372,7 +397,7 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, str
|
||||
scan_exponent(t, &kind);
|
||||
} else {
|
||||
if t.ch == '0' {
|
||||
int_base :: inline proc(t: ^Tokenizer, kind: ^Token_Kind, base: int, msg: string) {
|
||||
int_base :: proc(t: ^Tokenizer, kind: ^Token_Kind, base: int, msg: string) {
|
||||
prev := t.offset;
|
||||
advance_rune(t);
|
||||
scan_mantissa(t, base);
|
||||
@@ -406,9 +431,9 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, str
|
||||
}
|
||||
|
||||
switch digit_count {
|
||||
case 8, 16: break;
|
||||
case 4, 8, 16: break;
|
||||
case:
|
||||
error(t, t.offset, "invalid hexadecimal floating-point number, expected 8 or 16 digits, got %d", digit_count);
|
||||
error(t, t.offset, "invalid hexadecimal floating-point number, expected 4, 8, or 16 digits, got %d", digit_count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,6 +508,8 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
lit: string;
|
||||
pos := offset_to_pos(t, offset);
|
||||
|
||||
insert_semicolon := false;
|
||||
|
||||
switch ch := t.ch; true {
|
||||
case is_letter(ch):
|
||||
lit = scan_identifier(t);
|
||||
@@ -501,41 +528,53 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
break check_keyword;
|
||||
}
|
||||
}
|
||||
if kind == .Ident && lit == "notin" {
|
||||
kind = .Not_In;
|
||||
|
||||
#partial switch kind {
|
||||
case .Ident, .Context, .Typeid, .Break, .Continue, .Fallthrough, .Return:
|
||||
insert_semicolon = true;
|
||||
}
|
||||
}
|
||||
case '0' <= ch && ch <= '9':
|
||||
insert_semicolon = true;
|
||||
kind, lit = scan_number(t, false);
|
||||
case:
|
||||
advance_rune(t);
|
||||
switch ch {
|
||||
case -1:
|
||||
kind = .EOF;
|
||||
if t.insert_semicolon {
|
||||
t.insert_semicolon = false;
|
||||
kind = .Semicolon;
|
||||
lit = "\n";
|
||||
}
|
||||
case '\n':
|
||||
t.insert_semicolon = false;
|
||||
kind = .Semicolon;
|
||||
lit = "\n";
|
||||
case '"':
|
||||
insert_semicolon = true;
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
case '\'':
|
||||
insert_semicolon = true;
|
||||
kind = .Rune;
|
||||
lit = scan_rune(t);
|
||||
case '`':
|
||||
insert_semicolon = true;
|
||||
kind = .String;
|
||||
lit = scan_raw_string(t);
|
||||
case '=':
|
||||
if t.ch == '>' {
|
||||
advance_rune(t);
|
||||
kind = .Double_Arrow_Right;
|
||||
} else {
|
||||
kind = switch2(t, .Eq, .Cmp_Eq);
|
||||
}
|
||||
case '=': kind = switch2(t, .Eq, .Cmp_Eq);
|
||||
case '!': kind = switch2(t, .Not, .Not_Eq);
|
||||
case '#':
|
||||
kind = .Hash;
|
||||
if t.ch == '!' {
|
||||
insert_semicolon = t.insert_semicolon;
|
||||
kind = .Comment;
|
||||
lit = scan_comment(t);
|
||||
}
|
||||
case '?': kind = .Question;
|
||||
case '?':
|
||||
insert_semicolon = true;
|
||||
kind = .Question;
|
||||
case '@': kind = .At;
|
||||
case '$': kind = .Dollar;
|
||||
case '^': kind = .Pointer;
|
||||
@@ -554,6 +593,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
case '*': kind = switch2(t, .Mul, .Mul_Eq);
|
||||
case '/':
|
||||
if t.ch == '/' || t.ch == '*' {
|
||||
insert_semicolon = t.insert_semicolon;
|
||||
kind = .Comment;
|
||||
lit = scan_comment(t);
|
||||
} else {
|
||||
@@ -569,13 +609,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
}
|
||||
case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or);
|
||||
case '~': kind = .Xor;
|
||||
case '<':
|
||||
if t.ch == '-' {
|
||||
advance_rune(t);
|
||||
kind = .Arrow_Left;
|
||||
} else {
|
||||
kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
|
||||
}
|
||||
case '<': kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
|
||||
case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq);
|
||||
|
||||
case '.':
|
||||
@@ -596,11 +630,17 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
case ',': kind = .Comma;
|
||||
case ';': kind = .Semicolon;
|
||||
case '(': kind = .Open_Paren;
|
||||
case ')': kind = .Close_Paren;
|
||||
case ')':
|
||||
insert_semicolon = true;
|
||||
kind = .Close_Paren;
|
||||
case '[': kind = .Open_Bracket;
|
||||
case ']': kind = .Close_Bracket;
|
||||
case ']':
|
||||
insert_semicolon = true;
|
||||
kind = .Close_Bracket;
|
||||
case '{': kind = .Open_Brace;
|
||||
case '}': kind = .Close_Brace;
|
||||
case '}':
|
||||
insert_semicolon = true;
|
||||
kind = .Close_Brace;
|
||||
|
||||
case '\\': kind = .Back_Slash;
|
||||
|
||||
@@ -608,10 +648,15 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
if ch != utf8.RUNE_BOM {
|
||||
error(t, t.offset, "illegal character '%r': %d", ch, ch);
|
||||
}
|
||||
insert_semicolon = t.insert_semicolon; // preserve insert_semicolon info
|
||||
kind = .Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
if .Insert_Semicolon in t.flags {
|
||||
t.insert_semicolon = insert_semicolon;
|
||||
}
|
||||
|
||||
if lit == "" {
|
||||
lit = string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package os
|
||||
|
||||
import "core:strings"
|
||||
import "core:mem"
|
||||
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||
dirp: Dir;
|
||||
dirp, err = _fdopendir(fd);
|
||||
if err != ERROR_NONE {
|
||||
return;
|
||||
}
|
||||
|
||||
defer _closedir(dirp);
|
||||
|
||||
dirpath: string;
|
||||
dirpath, err = absolute_path_from_handle(fd);
|
||||
|
||||
if err != ERROR_NONE {
|
||||
return;
|
||||
}
|
||||
|
||||
defer delete(dirpath);
|
||||
|
||||
n := n;
|
||||
size := n;
|
||||
if n <= 0 {
|
||||
n = -1;
|
||||
size = 100;
|
||||
}
|
||||
|
||||
dfi := make([dynamic]File_Info, 0, size, allocator);
|
||||
|
||||
for {
|
||||
entry: Dirent;
|
||||
end_of_stream: bool;
|
||||
entry, err, end_of_stream = _readdir(dirp);
|
||||
if err != ERROR_NONE {
|
||||
for fi_ in dfi {
|
||||
file_info_delete(fi_, allocator);
|
||||
}
|
||||
delete(dfi);
|
||||
return;
|
||||
} else if end_of_stream {
|
||||
break;
|
||||
}
|
||||
|
||||
fi_: File_Info;
|
||||
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] });
|
||||
|
||||
if filename == "." || filename == ".." {
|
||||
continue;
|
||||
}
|
||||
|
||||
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator);
|
||||
defer delete(fullpath, context.temp_allocator);
|
||||
|
||||
fi_, err = stat(fullpath, allocator);
|
||||
if err != ERROR_NONE {
|
||||
for fi__ in dfi {
|
||||
file_info_delete(fi__, allocator);
|
||||
}
|
||||
delete(dfi);
|
||||
return;
|
||||
}
|
||||
|
||||
append(&dfi, fi_);
|
||||
}
|
||||
|
||||
return dfi[:], ERROR_NONE;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package os
|
||||
|
||||
import "core:strings"
|
||||
import "core:mem"
|
||||
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||
dirp: Dir;
|
||||
dirp, err = _fdopendir(fd);
|
||||
if err != ERROR_NONE {
|
||||
return;
|
||||
}
|
||||
|
||||
defer _closedir(dirp);
|
||||
|
||||
dirpath: string;
|
||||
dirpath, err = absolute_path_from_handle(fd);
|
||||
|
||||
if err != ERROR_NONE {
|
||||
return;
|
||||
}
|
||||
|
||||
defer delete(dirpath);
|
||||
|
||||
n := n;
|
||||
size := n;
|
||||
if n <= 0 {
|
||||
n = -1;
|
||||
size = 100;
|
||||
}
|
||||
|
||||
dfi := make([dynamic]File_Info, 0, size, allocator);
|
||||
|
||||
for {
|
||||
entry: Dirent;
|
||||
end_of_stream: bool;
|
||||
entry, err, end_of_stream = _readdir(dirp);
|
||||
if err != ERROR_NONE {
|
||||
for fi_ in dfi {
|
||||
file_info_delete(fi_, allocator);
|
||||
}
|
||||
delete(dfi);
|
||||
return;
|
||||
} else if end_of_stream {
|
||||
break;
|
||||
}
|
||||
|
||||
fi_: File_Info;
|
||||
filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] });
|
||||
|
||||
if filename == "." || filename == ".." {
|
||||
continue;
|
||||
}
|
||||
|
||||
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator);
|
||||
defer delete(fullpath, context.temp_allocator);
|
||||
|
||||
fi_, err = stat(fullpath, allocator);
|
||||
if err != ERROR_NONE {
|
||||
for fi__ in dfi {
|
||||
file_info_delete(fi__, allocator);
|
||||
}
|
||||
delete(dfi);
|
||||
return;
|
||||
}
|
||||
|
||||
append(&dfi, fi_);
|
||||
}
|
||||
|
||||
return dfi[:], ERROR_NONE;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
|
||||
// Ignore "." and ".."
|
||||
if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
|
||||
return;
|
||||
}
|
||||
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
|
||||
return;
|
||||
}
|
||||
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:])});
|
||||
fi.fullpath = path;
|
||||
fi.name = basename(path);
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow);
|
||||
|
||||
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
fi.mode |= 0o444;
|
||||
} else {
|
||||
fi.mode |= 0o666;
|
||||
}
|
||||
|
||||
is_sym := false;
|
||||
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 {
|
||||
is_sym = false;
|
||||
} else {
|
||||
is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT;
|
||||
}
|
||||
|
||||
if is_sym {
|
||||
fi.mode |= File_Mode_Sym_Link;
|
||||
} else {
|
||||
if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
fi.mode |= 0o111 | File_Mode_Dir;
|
||||
}
|
||||
|
||||
// fi.mode |= file_type_mode(h);
|
||||
}
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime));
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime));
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime));
|
||||
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if fd == 0 {
|
||||
return nil, ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
context.allocator = allocator;
|
||||
|
||||
h := win32.HANDLE(fd);
|
||||
|
||||
dir_fi, _ := file_info_from_get_file_information_by_handle("", h);
|
||||
if !dir_fi.is_dir {
|
||||
return nil, ERROR_FILE_IS_NOT_DIR;
|
||||
}
|
||||
|
||||
n := n;
|
||||
size := n;
|
||||
if n <= 0 {
|
||||
n = -1;
|
||||
size = 100;
|
||||
}
|
||||
dfi := make([dynamic]File_Info, 0, size);
|
||||
|
||||
wpath: []u16;
|
||||
wpath, err = cleanpath_from_handle_u16(fd);
|
||||
if len(wpath) == 0 || err != ERROR_NONE {
|
||||
return;
|
||||
}
|
||||
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator);
|
||||
copy(wpath_search, wpath);
|
||||
wpath_search[len(wpath)+0] = '\\';
|
||||
wpath_search[len(wpath)+1] = '*';
|
||||
wpath_search[len(wpath)+2] = 0;
|
||||
|
||||
path := cleanpath_from_buf(wpath);
|
||||
|
||||
find_data := &win32.WIN32_FIND_DATAW{};
|
||||
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data);
|
||||
defer win32.FindClose(find_handle);
|
||||
for n != 0 && find_handle != nil {
|
||||
fi: File_Info;
|
||||
fi = find_data_to_file_info(path, find_data);
|
||||
if fi.name != "" {
|
||||
append(&dfi, fi);
|
||||
n -= 1;
|
||||
}
|
||||
|
||||
if !win32.FindNextFileW(find_handle, find_data) {
|
||||
e := Errno(win32.GetLastError());
|
||||
if e == ERROR_NO_MORE_FILES {
|
||||
break;
|
||||
}
|
||||
return dfi[:], e;
|
||||
}
|
||||
}
|
||||
|
||||
return dfi[:], ERROR_NONE;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:mem"
|
||||
|
||||
// lookup_env gets the value of the environment variable named by the key
|
||||
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
|
||||
// Otherwise the returned value will be empty and the boolean will be false
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
if key == "" {
|
||||
return;
|
||||
}
|
||||
wkey := win32.utf8_to_wstring(key);
|
||||
b := make([dynamic]u16, 100, context.temp_allocator);
|
||||
for {
|
||||
n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)));
|
||||
if n == 0 {
|
||||
err := win32.GetLastError();
|
||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
||||
return "", false;
|
||||
}
|
||||
}
|
||||
|
||||
if n <= u32(len(b)) {
|
||||
value = win32.utf16_to_utf8(b[:n], allocator);
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
|
||||
resize(&b, len(b)*2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// get_env retrieves the value of the environment variable named by the key
|
||||
// It returns the value, which will be empty if the variable is not present
|
||||
// To distinguish between an empty value and an unset value, use lookup_env
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator);
|
||||
return;
|
||||
}
|
||||
|
||||
// set_env sets the value of the environment variable named by the key
|
||||
set_env :: proc(key, value: string) -> Errno {
|
||||
k := win32.utf8_to_wstring(key);
|
||||
v := win32.utf8_to_wstring(value);
|
||||
|
||||
if !win32.SetEnvironmentVariableW(k, v) {
|
||||
return Errno(win32.GetLastError());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// unset_env unsets a single environment variable
|
||||
unset_env :: proc(key: string) -> Errno {
|
||||
k := win32.utf8_to_wstring(key);
|
||||
if !win32.SetEnvironmentVariableW(k, nil) {
|
||||
return Errno(win32.GetLastError());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// environ returns a copy of strings representing the environment, in the form "key=value"
|
||||
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
|
||||
environ :: proc(allocator := context.allocator) -> []string {
|
||||
envs := win32.GetEnvironmentStringsW();
|
||||
if envs == nil {
|
||||
return nil;
|
||||
}
|
||||
defer win32.FreeEnvironmentStringsW(envs);
|
||||
|
||||
r := make([dynamic]string, 0, 50, allocator);
|
||||
for from, i, p := 0, 0, envs; true; i += 1 {
|
||||
c := (^u16)(uintptr(p) + uintptr(i*2))^;
|
||||
if c == 0 {
|
||||
if i <= from {
|
||||
break;
|
||||
}
|
||||
w := mem.slice_ptr(mem.ptr_offset(p, from), i-from);
|
||||
|
||||
append(&r, win32.utf16_to_utf8(w, allocator));
|
||||
from = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return r[:];
|
||||
}
|
||||
|
||||
|
||||
// clear_env deletes all environment variables
|
||||
clear_env :: proc() {
|
||||
envs := environ(context.temp_allocator);
|
||||
for env in envs {
|
||||
for j in 1..<len(env) {
|
||||
if env[j] == '=' {
|
||||
unset_env(env[0:j]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "intrinsics"
|
||||
|
||||
is_path_separator :: proc(c: byte) -> bool {
|
||||
return c == '/' || c == '\\';
|
||||
}
|
||||
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
|
||||
if len(path) == 0 {
|
||||
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
access: u32;
|
||||
switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: access = win32.FILE_GENERIC_READ;
|
||||
case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
|
||||
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE;
|
||||
}
|
||||
|
||||
if mode&O_APPEND != 0 {
|
||||
access &~= win32.FILE_GENERIC_WRITE;
|
||||
access |= win32.FILE_APPEND_DATA;
|
||||
}
|
||||
if mode&O_CREATE != 0 {
|
||||
access |= win32.FILE_GENERIC_WRITE;
|
||||
}
|
||||
|
||||
share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE;
|
||||
sa: ^win32.SECURITY_ATTRIBUTES = nil;
|
||||
sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true};
|
||||
if mode&O_CLOEXEC == 0 {
|
||||
sa = &sa_inherit;
|
||||
}
|
||||
|
||||
create_mode: u32;
|
||||
switch {
|
||||
case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL):
|
||||
create_mode = win32.CREATE_NEW;
|
||||
case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC):
|
||||
create_mode = win32.CREATE_ALWAYS;
|
||||
case mode&O_CREATE == O_CREATE:
|
||||
create_mode = win32.OPEN_ALWAYS;
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
create_mode = win32.TRUNCATE_EXISTING;
|
||||
case:
|
||||
create_mode = win32.OPEN_EXISTING;
|
||||
}
|
||||
wide_path := win32.utf8_to_wstring(path);
|
||||
handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil));
|
||||
if handle != INVALID_HANDLE {
|
||||
return handle, ERROR_NONE;
|
||||
}
|
||||
|
||||
err := Errno(win32.GetLastError());
|
||||
return INVALID_HANDLE, err;
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
if !win32.CloseHandle(win32.HANDLE(fd)) {
|
||||
return Errno(win32.GetLastError());
|
||||
}
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
flush :: proc(fd: Handle) -> (err: Errno) {
|
||||
if !win32.FlushFileBuffers(win32.HANDLE(fd)) {
|
||||
err = Errno(win32.GetLastError());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
}
|
||||
|
||||
single_write_length: win32.DWORD;
|
||||
total_write: i64;
|
||||
length := i64(len(data));
|
||||
|
||||
for total_write < length {
|
||||
remaining := length - total_write;
|
||||
to_write := win32.DWORD(min(i32(remaining), MAX_RW));
|
||||
|
||||
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil);
|
||||
if single_write_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError());
|
||||
return int(total_write), err;
|
||||
}
|
||||
total_write += i64(single_write_length);
|
||||
}
|
||||
return int(total_write), ERROR_NONE;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
}
|
||||
|
||||
single_read_length: win32.DWORD;
|
||||
total_read: i64;
|
||||
length := i64(len(data));
|
||||
|
||||
for total_read < length {
|
||||
remaining := length - total_read;
|
||||
to_read := min(win32.DWORD(remaining), MAX_RW);
|
||||
|
||||
e := win32.ReadFile(win32.HANDLE(fd), &data[total_read], to_read, &single_read_length, nil);
|
||||
if single_read_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError());
|
||||
return int(total_read), err;
|
||||
}
|
||||
total_read += i64(single_read_length);
|
||||
}
|
||||
return int(total_read), ERROR_NONE;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
w: u32;
|
||||
switch whence {
|
||||
case 0: w = win32.FILE_BEGIN;
|
||||
case 1: w = win32.FILE_CURRENT;
|
||||
case 2: w = win32.FILE_END;
|
||||
}
|
||||
hi := i32(offset>>32);
|
||||
lo := i32(offset);
|
||||
ft := win32.GetFileType(win32.HANDLE(fd));
|
||||
if ft == win32.FILE_TYPE_PIPE {
|
||||
return 0, ERROR_FILE_IS_PIPE;
|
||||
}
|
||||
|
||||
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w);
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
err := Errno(win32.GetLastError());
|
||||
return 0, err;
|
||||
}
|
||||
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
length: win32.LARGE_INTEGER;
|
||||
err: Errno;
|
||||
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
|
||||
err = Errno(win32.GetLastError());
|
||||
}
|
||||
return i64(length), err;
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
MAX_RW :: 1<<30;
|
||||
|
||||
@(private)
|
||||
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
buf := data;
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW];
|
||||
|
||||
}
|
||||
curr_offset, e := seek(fd, offset, 1);
|
||||
if e != 0 {
|
||||
return 0, e;
|
||||
}
|
||||
defer seek(fd, curr_offset, 0);
|
||||
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
};
|
||||
|
||||
h := win32.HANDLE(fd);
|
||||
done: win32.DWORD;
|
||||
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
e = Errno(win32.GetLastError());
|
||||
done = 0;
|
||||
}
|
||||
return int(done), e;
|
||||
}
|
||||
@(private)
|
||||
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
buf := data;
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW];
|
||||
|
||||
}
|
||||
curr_offset, e := seek(fd, offset, 1);
|
||||
if e != 0 {
|
||||
return 0, e;
|
||||
}
|
||||
defer seek(fd, curr_offset, 0);
|
||||
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
};
|
||||
|
||||
h := win32.HANDLE(fd);
|
||||
done: win32.DWORD;
|
||||
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
e = Errno(win32.GetLastError());
|
||||
done = 0;
|
||||
}
|
||||
return int(done), e;
|
||||
}
|
||||
|
||||
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
|
||||
if offset < 0 {
|
||||
return 0, ERROR_NEGATIVE_OFFSET;
|
||||
}
|
||||
|
||||
b, offset := data, offset;
|
||||
for len(b) > 0 {
|
||||
m, e := pread(fd, b, offset);
|
||||
if e != 0 {
|
||||
err = e;
|
||||
break;
|
||||
}
|
||||
n += m;
|
||||
b = b[m:];
|
||||
offset += i64(m);
|
||||
}
|
||||
return;
|
||||
}
|
||||
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
|
||||
if offset < 0 {
|
||||
return 0, ERROR_NEGATIVE_OFFSET;
|
||||
}
|
||||
|
||||
b, offset := data, offset;
|
||||
for len(b) > 0 {
|
||||
m, e := pwrite(fd, b, offset);
|
||||
if e != 0 {
|
||||
err = e;
|
||||
break;
|
||||
}
|
||||
n += m;
|
||||
b = b[m:];
|
||||
offset += i64(m);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE));
|
||||
stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE));
|
||||
stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE));
|
||||
|
||||
|
||||
get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
||||
fd := win32.GetStdHandle(win32.DWORD(h));
|
||||
when size_of(uintptr) == 8 {
|
||||
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
|
||||
}
|
||||
return Handle(fd);
|
||||
}
|
||||
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
attribs := win32.GetFileAttributesW(wpath);
|
||||
|
||||
return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES;
|
||||
}
|
||||
|
||||
is_file :: proc(path: string) -> bool {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
attribs := win32.GetFileAttributesW(wpath);
|
||||
|
||||
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == win32.FILE_ATTRIBUTE_DIRECTORY;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
is_dir :: proc(path: string) -> bool {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
attribs := win32.GetFileAttributesW(wpath);
|
||||
|
||||
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != win32.FILE_ATTRIBUTE_DIRECTORY;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
|
||||
@private cwd_lock := win32.SRWLOCK{}; // zero is initialized
|
||||
|
||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
win32.AcquireSRWLockExclusive(&cwd_lock);
|
||||
|
||||
sz_utf16 := win32.GetCurrentDirectoryW(0, nil);
|
||||
dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator); // the first time, it _includes_ the NUL.
|
||||
|
||||
sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr));
|
||||
assert(int(sz_utf16)+1 == len(dir_buf_wstr)); // the second time, it _excludes_ the NUL.
|
||||
|
||||
win32.ReleaseSRWLockExclusive(&cwd_lock);
|
||||
|
||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator);
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
wstr := win32.utf8_to_wstring(path);
|
||||
|
||||
win32.AcquireSRWLockExclusive(&cwd_lock);
|
||||
|
||||
if !win32.SetCurrentDirectoryW(wstr) {
|
||||
err = Errno(win32.GetLastError());
|
||||
}
|
||||
|
||||
win32.ReleaseSRWLockExclusive(&cwd_lock);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
change_directory :: proc(path: string) -> Errno {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
return Errno(win32.SetCurrentDirectoryW(wpath));
|
||||
}
|
||||
|
||||
make_directory :: proc(path: string, mode: u32) -> Errno {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
return Errno(win32.CreateDirectoryW(wpath, nil));
|
||||
}
|
||||
|
||||
|
||||
remove_directory :: proc(path: string) -> Errno {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
return Errno(win32.RemoveDirectoryW(wpath));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
is_abs :: proc(path: string) -> bool {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
return true;
|
||||
}
|
||||
when ODIN_OS == "windows" {
|
||||
if len(path) > 2 {
|
||||
switch path[0] {
|
||||
case 'A'..'Z', 'a'..'z':
|
||||
return path[1] == ':' && is_path_separator(path[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@(private)
|
||||
fix_long_path :: proc(path: string) -> string {
|
||||
if len(path) < 248 {
|
||||
return path;
|
||||
}
|
||||
|
||||
if len(path) >= 2 && path[:2] == `\\` {
|
||||
return path;
|
||||
}
|
||||
if !is_abs(path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
prefix :: `\\?`;
|
||||
|
||||
path_buf := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator);
|
||||
copy(path_buf, prefix);
|
||||
n := len(path);
|
||||
r, w := 0, len(prefix);
|
||||
for r < n {
|
||||
switch {
|
||||
case is_path_separator(path[r]):
|
||||
r += 1;
|
||||
case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
|
||||
r += 1;
|
||||
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
|
||||
return path;
|
||||
case:
|
||||
path_buf[w] = '\\';
|
||||
w += 1;
|
||||
for ; r < n && !is_path_separator(path[r]); r += 1 {
|
||||
path_buf[w] = path[r];
|
||||
w += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if w == len(`\\?\c:`) {
|
||||
path_buf[w] = '\\';
|
||||
w += 1;
|
||||
}
|
||||
return string(path_buf[:w]);
|
||||
}
|
||||
|
||||
|
||||
link :: proc(old_name, new_name: string) -> Errno {
|
||||
n := win32.utf8_to_wstring(fix_long_path(new_name));
|
||||
o := win32.utf8_to_wstring(fix_long_path(old_name));
|
||||
return Errno(win32.CreateHardLinkW(n, o, nil));
|
||||
}
|
||||
|
||||
unlink :: proc(path: string) -> Errno {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator);
|
||||
return Errno(win32.DeleteFileW(wpath));
|
||||
}
|
||||
|
||||
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> Errno {
|
||||
from := win32.utf8_to_wstring(old_path, context.temp_allocator);
|
||||
to := win32.utf8_to_wstring(new_path, context.temp_allocator);
|
||||
return Errno(win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING));
|
||||
}
|
||||
|
||||
|
||||
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) {
|
||||
curr_off, e := seek(fd, 0, 1);
|
||||
if e != 0 {
|
||||
return e;
|
||||
}
|
||||
defer seek(fd, curr_off, 0);
|
||||
_, e = seek(fd, length, 0);
|
||||
if e != 0 {
|
||||
return e;
|
||||
}
|
||||
ok := win32.SetEndOfFile(win32.HANDLE(fd));
|
||||
if !ok {
|
||||
return Errno(win32.GetLastError());
|
||||
}
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
truncate :: proc(path: string, length: i64) -> (err: Errno) {
|
||||
fd: Handle;
|
||||
fd, err = open(path, O_WRONLY|O_CREATE, 0o666);
|
||||
if err != 0 {
|
||||
return;
|
||||
}
|
||||
defer close(fd);
|
||||
err = ftruncate(fd, length);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
remove :: proc(name: string) -> Errno {
|
||||
p := win32.utf8_to_wstring(fix_long_path(name));
|
||||
err, err1: win32.DWORD;
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = win32.GetLastError();
|
||||
}
|
||||
if err == 0 {
|
||||
return 0;
|
||||
}
|
||||
if !win32.RemoveDirectoryW(p) {
|
||||
err1 = win32.GetLastError();
|
||||
}
|
||||
if err1 == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if err != err1 {
|
||||
a := win32.GetFileAttributesW(p);
|
||||
if a == ~u32(0) {
|
||||
err = win32.GetLastError();
|
||||
} else {
|
||||
if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
err = err1;
|
||||
} else if a & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) {
|
||||
err = 0;
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = win32.GetLastError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Errno(err);
|
||||
}
|
||||
|
||||
|
||||
pipe :: proc() -> (r, w: Handle, err: Errno) {
|
||||
sa: win32.SECURITY_ATTRIBUTES;
|
||||
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES);
|
||||
sa.bInheritHandle = true;
|
||||
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
|
||||
err = Errno(win32.GetLastError());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
+13
-12
@@ -70,7 +70,7 @@ file_size_from_path :: proc(path: string) -> i64 {
|
||||
return length;
|
||||
}
|
||||
|
||||
read_entire_file :: proc(name: string) -> (data: []byte, success: bool) {
|
||||
read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, success: bool) {
|
||||
fd, err := open(name, O_RDONLY, 0);
|
||||
if err != 0 {
|
||||
return nil, false;
|
||||
@@ -86,7 +86,7 @@ read_entire_file :: proc(name: string) -> (data: []byte, success: bool) {
|
||||
return nil, true;
|
||||
}
|
||||
|
||||
data = make([]byte, int(length));
|
||||
data = make([]byte, int(length), allocator);
|
||||
if data == nil {
|
||||
return nil, false;
|
||||
}
|
||||
@@ -133,8 +133,7 @@ read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
|
||||
|
||||
heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
|
||||
//
|
||||
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||
@@ -142,7 +141,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> rawptr {
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr));
|
||||
space := size + a - 1;
|
||||
|
||||
@@ -159,13 +158,13 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
|
||||
diff := int(aligned_ptr - ptr);
|
||||
if (size + diff) > space {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr);
|
||||
mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
|
||||
|
||||
return aligned_mem;
|
||||
return mem.byte_slice(aligned_mem, size), nil;
|
||||
}
|
||||
|
||||
aligned_free :: proc(p: rawptr) {
|
||||
@@ -174,8 +173,10 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
}
|
||||
}
|
||||
|
||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr {
|
||||
if p == nil do return nil;
|
||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, mem.Allocator_Error) {
|
||||
if p == nil {
|
||||
return nil, nil;
|
||||
}
|
||||
return aligned_alloc(new_size, new_alignment, p);
|
||||
}
|
||||
|
||||
@@ -200,13 +201,13 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
heap_allocator :: proc() -> mem.Allocator {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// Package os provides a platform-independent interface to operating system functionality.
|
||||
// The design is UNIX-like but with Odin-like error handling. Failing calls return values with a specific error type rather than error number.
|
||||
//
|
||||
// The package os interface is intended to be uniform across all operating systems.
|
||||
// Features not generally available appear in the system-specific packages under core:sys/*.
|
||||
//
|
||||
//
|
||||
// IMPORTANT NOTE from Bill: this is purely a mockup of what I want the new package os to be, and NON-FUNCTIONING.
|
||||
// It is not complete but should give designers a better idea of the general interface and how to write things.
|
||||
// This entire interface is subject to change.
|
||||
package os2
|
||||
@@ -0,0 +1,43 @@
|
||||
package os2
|
||||
|
||||
// get_env retrieves the value of the environment variable named by the key
|
||||
// It returns the value, which will be empty if the variable is not present
|
||||
// To distinguish between an empty value and an unset value, use lookup_env
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> string {
|
||||
value, _ := lookup_env(key, allocator);
|
||||
return value;
|
||||
}
|
||||
|
||||
// lookup_env gets the value of the environment variable named by the key
|
||||
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
|
||||
// Otherwise the returned value will be empty and the boolean will be false
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
return _lookup_env(key, allocator);
|
||||
}
|
||||
|
||||
// set_env sets the value of the environment variable named by the key
|
||||
// Returns true on success, false on failure
|
||||
set_env :: proc(key, value: string) -> bool {
|
||||
return _set_env(key, value);
|
||||
}
|
||||
|
||||
// unset_env unsets a single environment variable
|
||||
// Returns true on success, false on failure
|
||||
unset_env :: proc(key: string) -> bool {
|
||||
return _unset_env(key);
|
||||
}
|
||||
|
||||
clear_env :: proc() {
|
||||
_clear_env();
|
||||
}
|
||||
|
||||
|
||||
// environ returns a copy of strings representing the environment, in the form "key=value"
|
||||
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
|
||||
environ :: proc(allocator := context.allocator) -> []string {
|
||||
return _environ(allocator);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:mem"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
if key == "" {
|
||||
return;
|
||||
}
|
||||
wkey := win32.utf8_to_wstring(key);
|
||||
b := make([dynamic]u16, 100, context.temp_allocator);
|
||||
for {
|
||||
n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)));
|
||||
if n == 0 {
|
||||
err := win32.GetLastError();
|
||||
if err == win32.ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false;
|
||||
}
|
||||
}
|
||||
|
||||
if n <= u32(len(b)) {
|
||||
value = win32.utf16_to_utf8(b[:n], allocator);
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
|
||||
resize(&b, len(b)*2);
|
||||
}
|
||||
}
|
||||
|
||||
_set_env :: proc(key, value: string) -> bool {
|
||||
k := win32.utf8_to_wstring(key);
|
||||
v := win32.utf8_to_wstring(value);
|
||||
|
||||
return bool(win32.SetEnvironmentVariableW(k, v));
|
||||
}
|
||||
|
||||
_unset_env :: proc(key: string) -> bool {
|
||||
k := win32.utf8_to_wstring(key);
|
||||
return bool(win32.SetEnvironmentVariableW(k, nil));
|
||||
}
|
||||
|
||||
_clear_env :: proc() {
|
||||
envs := environ(context.temp_allocator);
|
||||
for env in envs {
|
||||
for j in 1..<len(env) {
|
||||
if env[j] == '=' {
|
||||
unset_env(env[0:j]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_environ :: proc(allocator := context.allocator) -> []string {
|
||||
envs := win32.GetEnvironmentStringsW();
|
||||
if envs == nil {
|
||||
return nil;
|
||||
}
|
||||
defer win32.FreeEnvironmentStringsW(envs);
|
||||
|
||||
r := make([dynamic]string, 0, 50, allocator);
|
||||
for from, i, p := 0, 0, envs; true; i += 1 {
|
||||
c := (^u16)(uintptr(p) + uintptr(i*2))^;
|
||||
if c == 0 {
|
||||
if i <= from {
|
||||
break;
|
||||
}
|
||||
w := mem.slice_ptr(mem.ptr_offset(p, from), i-from);
|
||||
|
||||
append(&r, win32.utf16_to_utf8(w, allocator));
|
||||
from = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return r[:];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package os2
|
||||
|
||||
Platform_Error_Min_Bits :: 32;
|
||||
|
||||
Error :: enum u64 {
|
||||
None = 0,
|
||||
|
||||
// General Errors
|
||||
Invalid_Argument,
|
||||
|
||||
Permission_Denied,
|
||||
Exist,
|
||||
Not_Exist,
|
||||
Closed,
|
||||
|
||||
// Timeout Errors
|
||||
Timeout,
|
||||
|
||||
// I/O Errors
|
||||
// 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,
|
||||
|
||||
// Platform Specific Errors
|
||||
Platform_Minimum = 1<<Platform_Error_Min_Bits,
|
||||
}
|
||||
|
||||
Path_Error :: struct {
|
||||
op: string,
|
||||
path: string,
|
||||
err: Error,
|
||||
}
|
||||
|
||||
Link_Error :: struct {
|
||||
op: string,
|
||||
old: string,
|
||||
new: string,
|
||||
err: Error,
|
||||
}
|
||||
|
||||
path_error_delete :: proc(perr: Maybe(Path_Error)) {
|
||||
if err, ok := perr.?; ok {
|
||||
context.allocator = error_allocator();
|
||||
delete(err.op);
|
||||
delete(err.path);
|
||||
}
|
||||
}
|
||||
|
||||
link_error_delete :: proc(lerr: Maybe(Link_Error)) {
|
||||
if err, ok := lerr.?; ok {
|
||||
context.allocator = error_allocator();
|
||||
delete(err.op);
|
||||
delete(err.old);
|
||||
delete(err.new);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
|
||||
if ferr >= .Platform_Minimum {
|
||||
err = i32(u64(ferr)>>Platform_Error_Min_Bits);
|
||||
ok = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
error_from_platform_error :: proc(errno: i32) -> Error {
|
||||
return Error(u64(errno) << Platform_Error_Min_Bits);
|
||||
}
|
||||
|
||||
error_string :: proc(ferr: Error) -> string {
|
||||
#partial switch ferr {
|
||||
case .None: return "";
|
||||
case .Invalid_Argument: return "invalid argument";
|
||||
case .Permission_Denied: return "permission denied";
|
||||
case .Exist: return "file already exists";
|
||||
case .Not_Exist: return "file does not exist";
|
||||
case .Closed: return "file already closed";
|
||||
case .Timeout: return "i/o timeout";
|
||||
case .EOF: return "eof";
|
||||
case .Unexpected_EOF: return "unexpected eof";
|
||||
case .Short_Write: return "short write";
|
||||
case .Invalid_Write: return "invalid write result";
|
||||
case .Short_Buffer: return "short buffer";
|
||||
case .No_Progress: return "multiple read calls return no data or error";
|
||||
case .Invalid_Whence: return "invalid whence";
|
||||
case .Invalid_Offset: return "invalid offset";
|
||||
case .Invalid_Unread: return "invalid unread";
|
||||
case .Negative_Read: return "negative read";
|
||||
case .Negative_Write: return "negative write";
|
||||
case .Negative_Count: return "negative count";
|
||||
case .Buffer_Full: return "buffer full";
|
||||
}
|
||||
|
||||
if errno, ok := is_platform_error(ferr); ok {
|
||||
return _error_string(errno);
|
||||
}
|
||||
|
||||
return "unknown error";
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_error_string :: proc(errno: i32) -> string {
|
||||
e := win32.DWORD(errno);
|
||||
if e == 0 {
|
||||
return "";
|
||||
}
|
||||
// TODO(bill): _error_string for windows
|
||||
// FormatMessageW
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
|
||||
Handle :: distinct uintptr;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
File_Mode :: distinct u32;
|
||||
File_Mode_Dir :: File_Mode(1<<16);
|
||||
File_Mode_Named_Pipe :: File_Mode(1<<17);
|
||||
File_Mode_Device :: File_Mode(1<<18);
|
||||
File_Mode_Char_Device :: File_Mode(1<<19);
|
||||
File_Mode_Sym_Link :: File_Mode(1<<20);
|
||||
|
||||
|
||||
O_RDONLY :: int( 0);
|
||||
O_WRONLY :: int( 1);
|
||||
O_RDWR :: int( 2);
|
||||
O_APPEND :: int( 4);
|
||||
O_CREATE :: int( 8);
|
||||
O_EXCL :: int(16);
|
||||
O_SYNC :: int(32);
|
||||
O_TRUNC :: int(64);
|
||||
|
||||
|
||||
|
||||
stdin: Handle = 0; // OS-Specific
|
||||
stdout: Handle = 1; // OS-Specific
|
||||
stderr: Handle = 2; // OS-Specific
|
||||
|
||||
|
||||
create :: proc(name: string) -> (Handle, Error) {
|
||||
return _create(name);
|
||||
}
|
||||
|
||||
open :: proc(name: string) -> (Handle, Error) {
|
||||
return _open(name);
|
||||
}
|
||||
|
||||
open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) {
|
||||
return _open_file(name, flag, perm);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Error {
|
||||
return _close(fd);
|
||||
}
|
||||
|
||||
name :: proc(fd: Handle, allocator := context.allocator) -> string {
|
||||
return _name(fd);
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
return _seek(fd, offset, whence);
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
return _read(fd, p);
|
||||
}
|
||||
|
||||
read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return _read_at(fd, p, offset);
|
||||
}
|
||||
|
||||
read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) {
|
||||
return _read_from(fd, r);
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
return _write(fd, p);
|
||||
}
|
||||
|
||||
write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return _write_at(fd, p, offset);
|
||||
}
|
||||
|
||||
write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) {
|
||||
return _write_to(fd, w);
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
|
||||
return _file_size(fd);
|
||||
}
|
||||
|
||||
|
||||
sync :: proc(fd: Handle) -> Error {
|
||||
return _sync(fd);
|
||||
}
|
||||
|
||||
flush :: proc(fd: Handle) -> Error {
|
||||
return _flush(fd);
|
||||
}
|
||||
|
||||
truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) {
|
||||
return _truncate(fd, size);
|
||||
}
|
||||
|
||||
remove :: proc(name: string) -> Maybe(Path_Error) {
|
||||
return _remove(name);
|
||||
}
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) {
|
||||
return _rename(old_path, new_path);
|
||||
}
|
||||
|
||||
|
||||
link :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
|
||||
return _link(old_name, new_name);
|
||||
}
|
||||
|
||||
symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
|
||||
return _symlink(old_name, new_name);
|
||||
}
|
||||
|
||||
read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
|
||||
return _read_link(name);
|
||||
}
|
||||
|
||||
|
||||
chdir :: proc(fd: Handle) -> Error {
|
||||
return _chdir(fd);
|
||||
}
|
||||
|
||||
chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
|
||||
return _chmod(fd, mode);
|
||||
}
|
||||
|
||||
chown :: proc(fd: Handle, uid, gid: int) -> Error {
|
||||
return _chown(fd, uid, gid);
|
||||
}
|
||||
|
||||
|
||||
lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return _lchown(name, uid, gid);
|
||||
}
|
||||
|
||||
|
||||
chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) {
|
||||
return _chtimes(name, atime, mtime);
|
||||
}
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
return _exists(path);
|
||||
}
|
||||
|
||||
is_file :: proc(path: string) -> bool {
|
||||
return _is_file(path);
|
||||
}
|
||||
|
||||
is_dir :: proc(path: string) -> bool {
|
||||
return _is_dir(path);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package os2
|
||||
|
||||
import "core:io"
|
||||
|
||||
file_to_stream :: proc(fd: Handle) -> (s: io.Stream) {
|
||||
s.stream_data = rawptr(uintptr(fd));
|
||||
s.stream_vtable = _file_stream_vtable;
|
||||
return;
|
||||
}
|
||||
|
||||
@(private)
|
||||
error_to_io_error :: proc(ferr: Error) -> io.Error {
|
||||
#partial switch ferr {
|
||||
case .None: return .None;
|
||||
case .EOF: return .EOF;
|
||||
case .Unexpected_EOF: return .Unexpected_EOF;
|
||||
case .Short_Write: return .Short_Write;
|
||||
case .Invalid_Write: return .Invalid_Write;
|
||||
case .Short_Buffer: return .Short_Buffer;
|
||||
case .No_Progress: return .No_Progress;
|
||||
case .Invalid_Whence: return .Invalid_Whence;
|
||||
case .Invalid_Offset: return .Invalid_Offset;
|
||||
case .Invalid_Unread: return .Invalid_Unread;
|
||||
case .Negative_Read: return .Negative_Read;
|
||||
case .Negative_Write: return .Negative_Write;
|
||||
case .Negative_Count: return .Negative_Count;
|
||||
case .Buffer_Full: return .Buffer_Full;
|
||||
}
|
||||
return .Unknown;
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_file_stream_vtable := &io.Stream_VTable{
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data));
|
||||
ferr: Error;
|
||||
n, ferr = read(fd, p);
|
||||
err = error_to_io_error(ferr);
|
||||
return;
|
||||
},
|
||||
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data));
|
||||
ferr: Error;
|
||||
n, ferr = read_at(fd, p, offset);
|
||||
err = error_to_io_error(ferr);
|
||||
return;
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data));
|
||||
ferr: Error;
|
||||
n, ferr = write_to(fd, w);
|
||||
err = error_to_io_error(ferr);
|
||||
return;
|
||||
},
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data));
|
||||
ferr: Error;
|
||||
n, ferr = write(fd, p);
|
||||
err = error_to_io_error(ferr);
|
||||
return;
|
||||
},
|
||||
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data));
|
||||
ferr: Error;
|
||||
n, ferr = write_at(fd, p, offset);
|
||||
err = error_to_io_error(ferr);
|
||||
return;
|
||||
},
|
||||
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data));
|
||||
ferr: Error;
|
||||
n, ferr = read_from(fd, r);
|
||||
err = error_to_io_error(ferr);
|
||||
return;
|
||||
},
|
||||
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data));
|
||||
n, ferr := seek(fd, offset, Seek_From(whence));
|
||||
err := error_to_io_error(ferr);
|
||||
return n, err;
|
||||
},
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
fd := Handle(uintptr(s.stream_data));
|
||||
sz, _ := file_size(fd);
|
||||
return sz;
|
||||
},
|
||||
impl_flush = proc(s: io.Stream) -> io.Error {
|
||||
fd := Handle(uintptr(s.stream_data));
|
||||
ferr := flush(fd);
|
||||
return error_to_io_error(ferr);
|
||||
},
|
||||
impl_close = proc(s: io.Stream) -> io.Error {
|
||||
fd := Handle(uintptr(s.stream_data));
|
||||
ferr := close(fd);
|
||||
return error_to_io_error(ferr);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,122 @@
|
||||
package os2
|
||||
|
||||
import "core:mem"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
write_string :: proc(fd: Handle, s: string) -> (n: int, err: Error) {
|
||||
return write(fd, transmute([]byte)s);
|
||||
}
|
||||
|
||||
write_byte :: proc(fd: Handle, b: byte) -> (n: int, err: Error) {
|
||||
return write(fd, []byte{b});
|
||||
}
|
||||
|
||||
write_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) {
|
||||
if r < utf8.RUNE_SELF {
|
||||
return write_byte(fd, byte(r));
|
||||
}
|
||||
|
||||
b: [4]byte;
|
||||
b, n = utf8.encode_rune(r);
|
||||
return write(fd, b[:n]);
|
||||
}
|
||||
|
||||
write_encoded_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) {
|
||||
wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool {
|
||||
n^ += m;
|
||||
if merr != nil {
|
||||
err^ = merr;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if wrap(write_byte(fd, '\''), &n, &err) { return; }
|
||||
|
||||
switch r {
|
||||
case '\a': if wrap(write_string(fd, "\\a"), &n, &err) { return; }
|
||||
case '\b': if wrap(write_string(fd, "\\b"), &n, &err) { return; }
|
||||
case '\e': if wrap(write_string(fd, "\\e"), &n, &err) { return; }
|
||||
case '\f': if wrap(write_string(fd, "\\f"), &n, &err) { return; }
|
||||
case '\n': if wrap(write_string(fd, "\\n"), &n, &err) { return; }
|
||||
case '\r': if wrap(write_string(fd, "\\r"), &n, &err) { return; }
|
||||
case '\t': if wrap(write_string(fd, "\\t"), &n, &err) { return; }
|
||||
case '\v': if wrap(write_string(fd, "\\v"), &n, &err) { return; }
|
||||
case:
|
||||
if r < 32 {
|
||||
if wrap(write_string(fd, "\\x"), &n, &err) { return; }
|
||||
b: [2]byte;
|
||||
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil);
|
||||
switch len(s) {
|
||||
case 0: if wrap(write_string(fd, "00"), &n, &err) { return; }
|
||||
case 1: if wrap(write_rune(fd, '0'), &n, &err) { return; }
|
||||
case 2: if wrap(write_string(fd, s), &n, &err) { return; }
|
||||
}
|
||||
} else {
|
||||
if wrap(write_rune(fd, r), &n, &err) { return; }
|
||||
}
|
||||
}
|
||||
_ = wrap(write_byte(fd, '\''), &n, &err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
s := transmute([]byte)mem.Raw_Slice{data, len};
|
||||
return write(fd, s);
|
||||
}
|
||||
|
||||
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
s := transmute([]byte)mem.Raw_Slice{data, len};
|
||||
return read(fd, s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byte, Error) {
|
||||
f, ferr := open(name);
|
||||
if ferr != nil {
|
||||
return nil, ferr;
|
||||
}
|
||||
defer close(f);
|
||||
|
||||
size: int;
|
||||
if size64, err := file_size(f); err == nil {
|
||||
if i64(int(size64)) != size64 {
|
||||
size = int(size64);
|
||||
}
|
||||
}
|
||||
size += 1; // for EOF
|
||||
|
||||
// TODO(bill): Is this correct logic?
|
||||
total: int;
|
||||
data := make([]byte, size, allocator);
|
||||
for {
|
||||
n, err := read(f, data[total:]);
|
||||
total += n;
|
||||
if err != nil {
|
||||
if err == .EOF {
|
||||
err = nil;
|
||||
}
|
||||
return data[:total], err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate := true) -> Error {
|
||||
flags := O_WRONLY|O_CREATE;
|
||||
if truncate {
|
||||
flags |= O_TRUNC;
|
||||
}
|
||||
f, err := open_file(name, flags, perm);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
_, err = write(f, data);
|
||||
if cerr := close(f); cerr != nil && err == nil {
|
||||
err = cerr;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
|
||||
_create :: proc(name: string) -> (Handle, Error) {
|
||||
return 0, .None;
|
||||
}
|
||||
|
||||
_open :: proc(name: string) -> (Handle, Error) {
|
||||
return 0, .None;
|
||||
}
|
||||
|
||||
_open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) {
|
||||
return 0, .None;
|
||||
}
|
||||
|
||||
_close :: proc(fd: Handle) -> Error {
|
||||
return .None;
|
||||
}
|
||||
|
||||
_name :: proc(fd: Handle, allocator := context.allocator) -> string {
|
||||
return "";
|
||||
}
|
||||
|
||||
_seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
_read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
_read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
_read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
_write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
_write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
_write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
_file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_sync :: proc(fd: Handle) -> Error {
|
||||
return .None;
|
||||
}
|
||||
|
||||
_flush :: proc(fd: Handle) -> Error {
|
||||
return .None;
|
||||
}
|
||||
|
||||
_truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_remove :: proc(name: string) -> Maybe(Path_Error) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_rename :: proc(old_path, new_path: string) -> Maybe(Path_Error) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
_link :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_symlink :: proc(old_name, new_name: string) -> Maybe(Link_Error) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
|
||||
return "", nil;
|
||||
}
|
||||
|
||||
|
||||
_chdir :: proc(fd: Handle) -> Error {
|
||||
return .None;
|
||||
}
|
||||
|
||||
_chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
|
||||
return .None;
|
||||
}
|
||||
|
||||
_chown :: proc(fd: Handle, uid, gid: int) -> Error {
|
||||
return .None;
|
||||
}
|
||||
|
||||
|
||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return .None;
|
||||
}
|
||||
|
||||
|
||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Maybe(Path_Error) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
_exists :: proc(path: string) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
_is_file :: proc(path: string) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
_is_dir :: proc(path: string) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
_path_error_delete :: proc(perr: Maybe(Path_Error)) {
|
||||
|
||||
}
|
||||
|
||||
_link_error_delete :: proc(lerr: Maybe(Link_Error)) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
heap_allocator :: proc() -> runtime.Allocator {
|
||||
return runtime.Allocator{
|
||||
procedure = heap_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
return _heap_allocator_proc(allocator_data, mode, size, alignment, old_memory, old_size, flags, loc);
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
error_allocator := heap_allocator;
|
||||
@@ -0,0 +1,107 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
import "core:mem"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size));
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
heap_free(ptr);
|
||||
return nil;
|
||||
}
|
||||
if ptr == nil {
|
||||
return heap_alloc(new_size);
|
||||
}
|
||||
|
||||
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, uint(new_size));
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
if ptr == nil {
|
||||
return;
|
||||
}
|
||||
win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
|
||||
}
|
||||
|
||||
_heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
//
|
||||
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||
// padding. We also store the original pointer returned by heap_alloc right before
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> rawptr {
|
||||
a := max(alignment, align_of(rawptr));
|
||||
space := size + a - 1;
|
||||
|
||||
allocated_mem: rawptr;
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^;
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr));
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr));
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)));
|
||||
|
||||
ptr := uintptr(aligned_mem);
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
|
||||
diff := int(aligned_ptr - ptr);
|
||||
if (size + diff) > space {
|
||||
return nil;
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr);
|
||||
mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
|
||||
|
||||
return aligned_mem;
|
||||
}
|
||||
|
||||
aligned_free :: proc(p: rawptr) {
|
||||
if p != nil {
|
||||
heap_free(mem.ptr_offset((^rawptr)(p), -1)^);
|
||||
}
|
||||
}
|
||||
|
||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr {
|
||||
if p == nil {
|
||||
return nil;
|
||||
}
|
||||
return aligned_alloc(new_size, new_alignment, p);
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment);
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory);
|
||||
|
||||
case .Free_All:
|
||||
// NOTE(tetra): Do nothing.
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return aligned_alloc(size, alignment);
|
||||
}
|
||||
return aligned_resize(old_memory, old_size, size, alignment);
|
||||
|
||||
case .Query_Features:
|
||||
set := (^runtime.Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package os2
|
||||
|
||||
Path_Separator :: _Path_Separator; // OS-Specific
|
||||
Path_List_Separator :: _Path_List_Separator; // OS-Specific
|
||||
|
||||
is_path_separator :: proc(c: byte) -> bool {
|
||||
return _is_path_separator(c);
|
||||
}
|
||||
|
||||
mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) {
|
||||
return _mkdir(name, perm);
|
||||
}
|
||||
|
||||
mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) {
|
||||
return _mkdir_all(path, perm);
|
||||
}
|
||||
|
||||
remove_all :: proc(path: string) -> Maybe(Path_Error) {
|
||||
return _remove_all(path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
|
||||
return _getwd(allocator);
|
||||
}
|
||||
setwd :: proc(dir: string) -> (err: Error) {
|
||||
return _setwd(dir);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
_Path_Separator :: '\\';
|
||||
_Path_List_Separator :: ';';
|
||||
|
||||
_is_path_separator :: proc(c: byte) -> bool {
|
||||
return c == '\\' || c == '/';
|
||||
}
|
||||
|
||||
_mkdir :: proc(name: string, perm: File_Mode) -> Maybe(Path_Error) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
_mkdir_all :: proc(path: string, perm: File_Mode) -> Maybe(Path_Error) {
|
||||
// TODO(bill): _mkdir_all for windows
|
||||
return nil;
|
||||
}
|
||||
|
||||
_remove_all :: proc(path: string) -> Maybe(Path_Error) {
|
||||
// TODO(bill): _remove_all for windows
|
||||
return nil;
|
||||
}
|
||||
|
||||
_getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
|
||||
return "", nil;
|
||||
}
|
||||
|
||||
_setwd :: proc(dir: string) -> (err: Error) {
|
||||
return nil;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package os2
|
||||
|
||||
pipe :: proc() -> (r, w: Handle, err: Error) {
|
||||
return _pipe();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user