mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-07 20:11:37 -07:00
Compare commits
924 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| af9ae4897a | |||
| 9f813a6263 | |||
| 5d57fe77be | |||
| ab54ad3a75 | |||
| 9919a0cacd | |||
| 91dece1656 | |||
| 1e1904e9ab | |||
| d6f6099419 | |||
| d712ea43eb | |||
| 962af780ae | |||
| cf7d705c1f | |||
| a7d7c92a53 | |||
| 4b6410e225 | |||
| b173fa1b9a | |||
| 225ffdec36 | |||
| 7f9cfd1579 | |||
| e3eea1b9fa | |||
| fe8325f0ad | |||
| 38fedf2631 | |||
| 253edb51f7 | |||
| 82aefd4203 | |||
| 3c80f15a7a | |||
| 085b725774 | |||
| 3039131a5f | |||
| daa3bb9a3b | |||
| 152479052e | |||
| 90cb62c5ac | |||
| 4a616d97df | |||
| 642ecc363b | |||
| bd45900257 | |||
| eccb548935 | |||
| a82f26f4c0 | |||
| eeaa9f048b | |||
| ca9cfc7167 | |||
| 0e446e1d68 | |||
| 90d024fb70 | |||
| d830069e5c | |||
| 7deb28c810 | |||
| 2b5c2cab1f | |||
| 39c8749d8f | |||
| 4f2915a9b5 | |||
| d3bff23bce | |||
| 007730bfbc | |||
| e54b1560ee | |||
| abd52529a6 | |||
| acfac3cf2d | |||
| 842f1ae304 | |||
| 8371ef6681 | |||
| 987faa3f18 | |||
| e397fb6a7f | |||
| 3337d6b264 | |||
| fdc56c4d9a | |||
| 791b05b14f | |||
| 45d26dc7f9 | |||
| dbad23385d | |||
| e9e7863033 | |||
| 5bced00f5d | |||
| 357c8f6f34 | |||
| baf5a06fba | |||
| 3d202da63f | |||
| 5a6f761535 | |||
| f7d74ff3a8 | |||
| 95721fe296 | |||
| ba3d545c96 | |||
| 66e83ef30d | |||
| 94288161e9 | |||
| 130318d003 | |||
| 1ae57d8d20 | |||
| 26d00925cc | |||
| 634fa7aa30 | |||
| 096258b5d5 | |||
| 6bdd572a30 | |||
| c39b934e7f | |||
| a1349d8776 | |||
| 32e13f17ae | |||
| d08b3d3b82 | |||
| a89d22b291 | |||
| 84700e09c9 | |||
| 355f84d0cd | |||
| b33f6ba1b0 | |||
| c1264c2a79 | |||
| 9a8fb2fb27 | |||
| 2c5be4e054 | |||
| 02884207d5 | |||
| b116e8ff55 | |||
| a4dd489284 | |||
| 290561a39b | |||
| a7fe647c8c | |||
| 327ca2ab71 | |||
| 68960e7d0a | |||
| 6bbeb0a243 | |||
| a52b1e0321 | |||
| d560dad0d8 | |||
| 5441620b6c | |||
| 244907149a | |||
| bec09d82df | |||
| acbf5c8d97 | |||
| e0bb07728d | |||
| 0ca5e70c7d | |||
| 9456c36684 | |||
| 80622bcb3a | |||
| 18fd1fefc3 | |||
| 568705069c | |||
| cbf0471c72 | |||
| 35f0dcbb92 | |||
| 6e979a96a1 | |||
| 7491b3c332 | |||
| 4ff836609c | |||
| 8814170edf | |||
| e17dfcf7a2 | |||
| 80cc2f5416 | |||
| 9973e0255c | |||
| 4dd3248507 | |||
| 833157ecc9 | |||
| 652557bfcd | |||
| 29fedc1808 | |||
| 093ade0504 | |||
| 6ef779cd5c | |||
| 9508a1f031 | |||
| c794f853e9 | |||
| 0975820c48 | |||
| 0d33df15b4 | |||
| 4a3b4da73c | |||
| 09588836e7 | |||
| 19c1ed154c | |||
| abf6ea7732 | |||
| 6e0f1cc866 | |||
| 4d6f7dcac0 | |||
| aa25714d43 | |||
| fff99c726e | |||
| a16d3b6c9a | |||
| 68619f299e | |||
| 017d6bdf73 | |||
| 16ef59700b | |||
| d38f5ffb49 | |||
| d03d9e49a6 | |||
| d2d2026736 | |||
| 603efa860a | |||
| 3d7b924260 | |||
| 19f0127e55 | |||
| b12d312408 | |||
| 4f3f256375 | |||
| c24e18bf10 | |||
| 8b84b9a4a2 | |||
| 86e291235d | |||
| 466e29bb38 | |||
| 016d1a84d4 | |||
| 3ed2ab6e2c | |||
| e90f5d2528 | |||
| 129d85fe7d | |||
| 18a63037d1 | |||
| 3166c7bef9 | |||
| 275f1cc0de | |||
| 387f56634d | |||
| c94829f4d3 | |||
| 201a7b90bb | |||
| 27ed10746d | |||
| 3b22c0854c | |||
| 1025b9e6c0 | |||
| 244a4acfa1 | |||
| 788e4b2b6a | |||
| 7f7cfebc91 | |||
| 16cd16b91e | |||
| 2938655a3d | |||
| b1db33b519 | |||
| a1435a6a90 | |||
| fec1ccd7a3 | |||
| 9b06ea5bfd | |||
| b379d25a12 | |||
| a15afccd36 | |||
| 83b5f82a92 | |||
| 496d732aaf | |||
| 26db1c8860 | |||
| 7ff424d148 | |||
| f4322886f3 | |||
| 005cadb52c | |||
| 90ba78cdfc | |||
| a66524b0b2 | |||
| f16ed256ea | |||
| b442ea8601 | |||
| 309ea50a7c | |||
| 5c4f241f22 | |||
| fce7bcec49 | |||
| 459de29a87 | |||
| b2c2235e58 | |||
| 3a60109180 | |||
| 074314b887 | |||
| 8a14a656fb | |||
| e9a6a34480 | |||
| 026aef69e3 | |||
| 73f5ab473c | |||
| 0a594147af | |||
| beb10cce63 | |||
| 4df668fa22 | |||
| 564c7821c5 | |||
| fdd4882568 | |||
| c3f363cfbc | |||
| dbb783fbf2 | |||
| 45da009377 | |||
| 4d14b4257e | |||
| cbd4d5e765 | |||
| 74b28f1ff9 | |||
| 9d6f71fd2e | |||
| f9de8fdaba | |||
| 580f0599cd | |||
| cc724ff5d2 | |||
| 957cd64699 | |||
| 3637dcbd04 | |||
| 954dd86f7a | |||
| 6298b23e07 | |||
| 053425e8d7 | |||
| 5ae27c6ebc | |||
| d783bca297 | |||
| ce3f6b60d2 | |||
| a25a9e6ebe | |||
| 0dddd2b97e | |||
| aa75909418 | |||
| 0f379d7378 | |||
| ae020ffb2e | |||
| 036d7edda9 | |||
| 75dd562a0a | |||
| 0a17525791 | |||
| 499a5d1f9c | |||
| e72d0ba804 | |||
| f3a2b625ae | |||
| 167ced8ad1 | |||
| 05df34f99c | |||
| 299accb717 | |||
| 3b30bc305c | |||
| 73e4954346 | |||
| f1f5dc614e | |||
| 6eb80831b5 | |||
| b78d546010 | |||
| c719a86312 | |||
| dc767da12b | |||
| 300b01d77d | |||
| 1ab0745ca8 | |||
| 8c636ac37b | |||
| 466a90010f | |||
| 5a4746c3a0 | |||
| 1842cd6297 | |||
| c4c2282595 | |||
| d2202416d2 | |||
| 0a08a65202 | |||
| f8cd13767e | |||
| 3a351ec407 | |||
| 7c9d2f61f5 | |||
| 6017a20e1c | |||
| 2d988bbc5f | |||
| 64814f4199 | |||
| c0112d1c70 | |||
| c0e17808d4 | |||
| 6d3cffa13c | |||
| b350a35b77 | |||
| 03f6b9bbf6 | |||
| f8641ddd1b | |||
| aea3e9a585 | |||
| 4843db0960 | |||
| de220a9aa5 | |||
| 9750b64096 | |||
| 834f082dba | |||
| e5106e48a8 | |||
| da6213196d | |||
| 2b9096517c | |||
| 90358f97d5 | |||
| c19da21d81 | |||
| 78506b97a3 | |||
| 490f8c1568 | |||
| a99e57c62c | |||
| ddf5ca7adf | |||
| 6778598bc6 | |||
| 0aa971207b | |||
| a4fd0c133e | |||
| ce018b4e6f | |||
| 1a7c1d107a | |||
| d2d5ac33f4 | |||
| 578de09775 | |||
| dcf339517e | |||
| b2c5998e78 | |||
| 7487d507be | |||
| c6b551d2c3 | |||
| 2f1228baa0 | |||
| 73eea33b56 | |||
| c3bd94a27e | |||
| 645207b8b0 | |||
| d86e56089a | |||
| 597ba796b7 | |||
| 288312a812 | |||
| 6bbc165121 | |||
| b8f8cb9582 | |||
| 001b2b9d8f | |||
| 33735ba5cc | |||
| 3da77bcd67 | |||
| 996175753c | |||
| 0e6109e171 | |||
| 5b9b21e756 | |||
| d93aca647b | |||
| 6ed4bfeba1 | |||
| 99a3e9a510 | |||
| c0154bffad | |||
| 9276476760 | |||
| c86bbc61bc | |||
| 02da1ac9af | |||
| b618b665c6 | |||
| 806c87d71d | |||
| 309953e0f2 | |||
| f6f2c67f37 | |||
| 71b2527df0 | |||
| b24157738f | |||
| 60321d98d4 | |||
| 195259e88b | |||
| 35731e66cf | |||
| 54b46247bd | |||
| 16c5c69a40 | |||
| 39b49cb6fb | |||
| 9871ad5fc8 | |||
| 28c643d23f | |||
| dd1f151696 | |||
| c1605b5b4f | |||
| 6e150d1d58 | |||
| 722b638e2c | |||
| c1cb1a3d7e | |||
| a4ac50a5b4 | |||
| 6ba1506aa9 | |||
| b4bdb73158 | |||
| 476030dd28 | |||
| e0a2e52601 | |||
| 7f3d8e115f | |||
| 584e8859bf | |||
| 8e855155fd | |||
| 7624ecf4ba | |||
| bfedcd7035 | |||
| 6ef49d4f24 | |||
| 8ad4427a25 | |||
| db3bcd2cea | |||
| 3072a02d5d | |||
| 773703bc83 | |||
| a4e865f90b | |||
| 9197a126fc | |||
| 38ea276231 | |||
| b020b91df2 | |||
| 291048cb3b | |||
| 3135c89a0a | |||
| 47f423c123 | |||
| a7a2611efc | |||
| 9866c83d61 | |||
| 655610ec87 | |||
| a28d574272 | |||
| f93779d425 | |||
| a10f988020 | |||
| 39bdf4d710 | |||
| 7862460374 | |||
| 92821300e4 | |||
| 94a1a7aed5 | |||
| 5a22d49b72 | |||
| 942017b958 | |||
| fd2ad20cd4 | |||
| cca385209b | |||
| fe2d256468 | |||
| 0f2ad95014 | |||
| 7fa2e56cd9 | |||
| 24a53c246f | |||
| b11fa90ed2 | |||
| f453054aff | |||
| ef99373c31 | |||
| 1cd5cbb0e4 | |||
| 56f232e5fc | |||
| de1432b315 | |||
| e7d6e2d163 | |||
| 8b40be50b9 | |||
| 6798d15ecb | |||
| 6aedb2695a | |||
| 0243647e15 | |||
| 3ec4db212b | |||
| 7683c1f4bb | |||
| ef2cd9d97f | |||
| 5b9e9fb822 | |||
| 741ccd7ff5 | |||
| eb92a2df71 | |||
| d60f404bb4 | |||
| 6c4806835c | |||
| e1555e04ad | |||
| 7061032db6 | |||
| 861d892ffe | |||
| 5c8c63ae04 | |||
| 1ced76cdd1 | |||
| e83b982afe | |||
| 7c6cc81041 | |||
| 521e47ffee | |||
| 8251f4d7d0 | |||
| da49f7eb4e | |||
| d66486c17e | |||
| 981a2e1a00 | |||
| eb6e5ee3a1 | |||
| 5e850e24d6 | |||
| 17d10b72d5 | |||
| b08c14b710 | |||
| d3fca60e4d | |||
| d602e2cc74 | |||
| 831e26cb6f | |||
| 7b276cbc7e | |||
| 7e4750c303 | |||
| 967b6d46b2 | |||
| fcafd83506 | |||
| 21dab9673d | |||
| 16503c3b91 | |||
| 1e6419b5b7 | |||
| ef66f96cf2 | |||
| 9684ade23e | |||
| 8694fa5f78 | |||
| fb248056c9 | |||
| ade4e312fe | |||
| 12bd07d2df | |||
| 91f8c626bb | |||
| c1684d6335 | |||
| 1a4faff9c9 | |||
| 6c17642b91 | |||
| ef254ec055 | |||
| 1f04f541cf | |||
| dd86a8f013 | |||
| 43ec2b9253 | |||
| 60cd0da2ed | |||
| c21bbae427 | |||
| d7e977069a | |||
| ef98aa1fe1 | |||
| 4e2bce0c83 | |||
| d43c6e39f6 | |||
| f56b895c05 | |||
| 54a420f3eb | |||
| 3e0f6bda91 | |||
| 9fac03b84c | |||
| 0a8f85e879 | |||
| 49606ec3ea | |||
| d338642dc4 | |||
| 95d1c29c2a | |||
| c424c94030 | |||
| 8de1e88c4f | |||
| 3119e0489f | |||
| db94e646e0 | |||
| 4737485f09 | |||
| d299d4e1cd | |||
| 9aeb0d0fb6 | |||
| 0ea6809b49 | |||
| a0fe6da10b | |||
| 8c952878fb | |||
| eda58357b6 | |||
| 7b14c267fb | |||
| cc2ed2b302 | |||
| 17cc423a17 | |||
| fa84e86766 | |||
| 0d7e6430eb | |||
| 4cf1af3f7d | |||
| eef4dffa12 | |||
| 25e8a6353d | |||
| ae5ef0ce9c | |||
| 574a5015d6 | |||
| 0a825fc44d | |||
| aa659a637a | |||
| 0e71e93b22 | |||
| 61b3af5b08 | |||
| 19b95349a6 | |||
| d0eaf7642d | |||
| eb799393d5 | |||
| 8ba87e01bd | |||
| daccd72162 | |||
| 8b248673c1 | |||
| faec52623d | |||
| b6d9a0c32e | |||
| a4cc207022 | |||
| 683dde1fa0 | |||
| ca4d91a8a3 | |||
| 65543b993f | |||
| 00fb60d3d9 | |||
| 68a83abcd6 | |||
| 4458ca4585 | |||
| 5ef8a092f6 | |||
| c969bee86d | |||
| 426367c522 | |||
| d410281766 | |||
| ce53805d94 | |||
| 963e8544f4 | |||
| c2a7c29ce8 | |||
| b3f22133f3 | |||
| caef37bc18 | |||
| 574dc5efe6 | |||
| a66520ba57 | |||
| b9043db434 | |||
| 58e5078b66 | |||
| 70932dc478 | |||
| a8bc6f08a9 | |||
| 07aedb0b89 | |||
| 171d917b7e | |||
| 63cd9a031a | |||
| c77e8ca401 | |||
| 58e811eea5 | |||
| c0125f3192 | |||
| 1bcc074223 | |||
| e7b8e61c68 | |||
| bbe4c32e32 | |||
| 0d39f52b4d | |||
| 14169ae5b4 | |||
| 6b4b0cea5d | |||
| 85a33a1c9b | |||
| 8f2d3dc955 | |||
| daa0779c01 | |||
| d7e81e86f5 | |||
| 06fb500dfe | |||
| 5ef58049dc | |||
| 5b22bfa2b7 | |||
| ca6ef95b03 | |||
| 53500699a9 | |||
| 29838da782 | |||
| 97795ff3a2 | |||
| 6df51b42a8 | |||
| 3228178d87 | |||
| 3fc1f6f1cd | |||
| 2999c02f65 | |||
| 9d7ab8d5ca | |||
| 2a0785037b | |||
| 9e65e229d0 | |||
| 34a9e20531 | |||
| 0fa24ac3c4 | |||
| 17eb0b5ee0 | |||
| b49b80bdf9 | |||
| 2906f2aa7e | |||
| f96991364a | |||
| 359a212a78 | |||
| ea7e8f075d | |||
| d99a6ad003 | |||
| 95c3b8a8de | |||
| ae41779366 | |||
| bb3f848b4a | |||
| a51b9c6fb0 | |||
| 8e52a52580 | |||
| b7b3ada3b1 | |||
| 9a895a318c | |||
| b2e64b7ce0 | |||
| 7020e9b66a | |||
| f49ebae956 | |||
| 0e82a46047 | |||
| c4e0cbcd87 | |||
| f82bf6cd42 | |||
| 60b2979cfd | |||
| 17740966e5 | |||
| 5c06fcd346 | |||
| 22a82e73d6 | |||
| bbb5593f87 | |||
| d00057c803 | |||
| acfd9dcd1b | |||
| 9553bc3689 | |||
| ebbb70f11d | |||
| 142bda2804 | |||
| 478f529744 | |||
| 536a342873 | |||
| 43dc7704a5 | |||
| e8933e43ec | |||
| f7d7d65bc0 | |||
| 970dc7a1f2 | |||
| a07878be71 | |||
| 14e207088a | |||
| 6c46c9e04b | |||
| 40b8150a23 | |||
| 31bb3dc4f0 | |||
| 65ce7687d7 | |||
| 6fc39c1b5d | |||
| 07a9c69714 | |||
| 004036dc59 | |||
| d91fa162d8 | |||
| 6e91f6ab31 | |||
| b16d5198a3 | |||
| 12813e5d48 | |||
| 6422e49255 | |||
| f7234e2ed3 | |||
| e810c3eace | |||
| 18b6af1858 | |||
| 03dd38f203 | |||
| eb6f2078cf | |||
| df61096317 | |||
| 8abe054665 | |||
| 324d9c2284 | |||
| f657b4cc0c | |||
| c98c95fcf0 | |||
| fd5376ba88 | |||
| 78fa675c1a | |||
| 4c19f507ff | |||
| 651122e9db | |||
| d1e114742e | |||
| 29a6959a56 | |||
| 491def9d5b | |||
| 427c48221d | |||
| 1c97739ca3 | |||
| 0b26115805 | |||
| 1c8672ac6c | |||
| 0f052dbde7 | |||
| 4bb51249d1 | |||
| e29f0a0f40 | |||
| f837e35f4b | |||
| 61ee2efa35 | |||
| e94c4e1e18 | |||
| b07d0b38b1 | |||
| a73677d21a | |||
| 67ea7bb65a | |||
| 175f5b0bb1 | |||
| 939ba4cf08 | |||
| f00f68ef6f | |||
| de9abe1f7b | |||
| bd808f9ec6 | |||
| 7474db6a34 | |||
| 00eb702c4a | |||
| ea5783c2ac | |||
| e05fddc001 | |||
| 406e60f5dd | |||
| 5115aee23f | |||
| 72220a855f | |||
| 4a61bac100 | |||
| 726891588f | |||
| 2a5ceff667 | |||
| 9e2c5acb9d | |||
| 7c9ca60ff0 | |||
| 379cd6fe66 | |||
| ffff3c3c88 | |||
| a4d459f651 | |||
| ff0ca0bd53 | |||
| 07b9f7d280 | |||
| 9f80191f60 | |||
| efe68c2e24 | |||
| 3847d03248 | |||
| bb54a0a972 | |||
| ac68a9d52c | |||
| d4d46df2fc | |||
| 660b6ff0f1 | |||
| 9ccaca7f1a | |||
| 926c419ef8 | |||
| 22b32ab0fa | |||
| 40361f877e | |||
| f2ba3da895 | |||
| 55be3e60a0 | |||
| 62911539cd | |||
| c7af8af76a | |||
| e3d5bbe62c | |||
| 2584c6bcd7 | |||
| 66d3082a9a | |||
| 09a07bd7e0 | |||
| 848f7e117f | |||
| d93364ce52 | |||
| 4c4f24b5f4 | |||
| 069ad446cd | |||
| 02b4bb8491 | |||
| 2808ecc5b6 | |||
| a1d518442a | |||
| 0d916a659e | |||
| 3a32250b80 | |||
| 053e65a1c8 | |||
| 7af0291c7d | |||
| 0af025d056 | |||
| 9eb7186cda | |||
| d68cc41782 | |||
| fb09ec06d3 | |||
| a4ac3cc6e8 | |||
| 4f56822204 | |||
| f22ff21039 | |||
| b71e0c2e36 | |||
| fd9a008e1e | |||
| 450fc3ec77 | |||
| 0a118a14ab | |||
| f42a22369e | |||
| 675add4d90 | |||
| 1761802330 | |||
| 99aa0d3a35 | |||
| dec97cbddb | |||
| f0840ed24e | |||
| b9e68ee3c1 | |||
| d6336e7252 | |||
| c6a0d302b3 | |||
| 26fa3aca44 | |||
| e3f375afd8 | |||
| 8a67221bd4 | |||
| 67251ad5b8 | |||
| 938e81e341 | |||
| f5fe0de0fd | |||
| 7fd8b9c55b | |||
| a2cd763dbb | |||
| cf8f6afeff | |||
| afbdc78f85 | |||
| 6918d8aaa6 | |||
| e226d37803 | |||
| 4f816aabb3 | |||
| d98f848247 | |||
| 1242b6e82f | |||
| 9759d56c81 | |||
| d73ad8eb1e | |||
| a52f7c129f | |||
| e54d6e5a11 | |||
| f7a73b9555 | |||
| 9d2b4b2f03 | |||
| 5d5addd48f | |||
| c69fa87d53 | |||
| bf53d2f6db | |||
| d267735d99 | |||
| 554b0e5bf7 | |||
| ba1ad82c2b | |||
| 38aea1f907 | |||
| b381791f42 | |||
| 14ceb0b19d | |||
| 8efc98ce90 | |||
| 1f3107e693 | |||
| 708f053fe6 | |||
| ac7f44b1b8 | |||
| b1d6e4139e | |||
| dfc4df9807 | |||
| b9293334ca | |||
| b68311d777 | |||
| c0f9655ec4 | |||
| 7875e4a1ab | |||
| be7a1f671c | |||
| c9ca192f33 | |||
| e7e7fe766a | |||
| 0d29cc3375 | |||
| c8a62ee4ec | |||
| 12dd0cb72a | |||
| 912f99abc8 | |||
| baacc512e3 | |||
| c5ed7083d2 | |||
| d69e0bfa00 | |||
| fa6f343261 | |||
| c4d43bbab0 | |||
| cc24d2de3e | |||
| bc6deab175 | |||
| b5a612202f | |||
| 1e09ff3b2e | |||
| 933f9f9bd1 | |||
| b82cfc5f15 | |||
| a05b73c632 | |||
| 4d27898418 | |||
| 80d1e1ba82 | |||
| dda89a69bf | |||
| f328929939 | |||
| 3d0519fe6c | |||
| a4eeb6ed02 | |||
| 94c62fb630 | |||
| a7fe9eec73 | |||
| d93f3c63d8 | |||
| ada3df303e | |||
| 8b98fff98e | |||
| c3ba8fbd09 | |||
| 4b9ca39522 | |||
| 26633bbce6 | |||
| 9625798513 | |||
| 796feeead9 | |||
| 2bf055ec6e | |||
| c9b69d76b0 | |||
| 69a15ca5b6 | |||
| 6fe938b946 | |||
| e5a478d393 | |||
| 793811b219 | |||
| 0418d27bdf | |||
| 28c98c2e7a | |||
| f66fcd9acb | |||
| 8deeb40e5d | |||
| 39d557bcb4 | |||
| e27a424f4d | |||
| 7a367c9c08 | |||
| 4423aac3de | |||
| 94ded29b5f | |||
| 6a6f078186 | |||
| bed18a17e6 | |||
| 17ebaffce8 | |||
| 4902288a5a | |||
| a1c3c38f04 | |||
| 4c0ab09c9a | |||
| 6cc7f3b451 | |||
| 3512d7c672 | |||
| eba0774bf3 | |||
| 7c3461b0df | |||
| 2cc3795686 | |||
| b63657d293 | |||
| 6175efde3d | |||
| 030220eb22 | |||
| 2d32b819dc | |||
| f56abf3780 | |||
| 78919f8524 | |||
| 80ea4e0aeb | |||
| 84ac56f778 | |||
| 7e701d1677 | |||
| b67ed78afd | |||
| 90fc52c2ee | |||
| 9a01a13914 | |||
| eeb92e2644 | |||
| fd06be2243 | |||
| a06cb8ba46 | |||
| d0d4f19097 | |||
| 8f5b838a07 | |||
| ba0581ae79 | |||
| d9404d928c | |||
| fe754af13d | |||
| 61e770d943 | |||
| 2b63684ccf | |||
| 5ece6980eb | |||
| 42a2297d31 | |||
| 8de48d81ea | |||
| 32fb1fb61c | |||
| 05c50561ae | |||
| 46455dd0a6 | |||
| 3f9ddfe029 | |||
| 14858309f0 | |||
| e17fc8272b | |||
| dde42f0ebc | |||
| ca7e46d56f | |||
| 743480b1a4 | |||
| 1ccb0b2558 | |||
| babdc432e9 | |||
| d3a51e208d | |||
| cd8272557f | |||
| 7e0fa795e4 | |||
| 6252712363 | |||
| 60bc7f53d2 | |||
| cf3c1a85ec | |||
| 046f72befd | |||
| f19436fb4d | |||
| d0709a7de2 | |||
| f427f040fd | |||
| c078b2dd1b | |||
| bf948ab8ae | |||
| acb1ebddf6 | |||
| 0bedd3357a | |||
| 93fabf8628 | |||
| fc10b781af | |||
| 6a6b5061db | |||
| b67817517e | |||
| fff1d509d5 | |||
| e8d26c5797 | |||
| 3d992e2704 | |||
| 0c5b645dde | |||
| cf5ec25873 | |||
| 7663a2036a | |||
| c32f345c68 | |||
| 649b25fba6 | |||
| bdbbbf5c95 | |||
| e8b6d15db9 | |||
| 5f7fef92fa | |||
| 66b86bc7e0 | |||
| 5187bb68bb | |||
| 97c499dbb4 | |||
| 1d75a612d5 | |||
| 160048eaee | |||
| def2e2e271 | |||
| 28666414bc | |||
| a241168142 | |||
| 29b6eebcd5 | |||
| 7dae38ce89 | |||
| 1826b0c700 | |||
| 9f9abb8fb3 | |||
| e60951a902 | |||
| 71932628cc | |||
| c691c7dc68 | |||
| 2a7db08c20 | |||
| 2b89829b52 | |||
| e77977c8ef | |||
| 38b96a7981 | |||
| 4dd846fa29 | |||
| 61008232e4 | |||
| 90e573c54a | |||
| ac483f72eb | |||
| 99d9e8f8b1 | |||
| 9f5854e24e | |||
| 772dce7e42 | |||
| e55d09bdfa | |||
| f8e0cda113 | |||
| c7eb2ae6bb | |||
| 792640df1f | |||
| 278a63caaa | |||
| a03dffcd1a | |||
| 90f1f7fbdf | |||
| ff492e615c | |||
| 042f6de478 | |||
| c52a8a5f86 | |||
| 16b644ad79 | |||
| e8537a3134 | |||
| e642be8550 | |||
| a5fa93e06d | |||
| 215b21811e | |||
| 95a8a4e7f0 | |||
| ecdd3887b2 | |||
| 16bdc6d240 | |||
| c98bb7da39 | |||
| ba81a81ca8 | |||
| 57dc6c2e94 | |||
| 95412df129 | |||
| 2a2bedc85c | |||
| 0455e4b60f | |||
| 82deaa59ad | |||
| 3c7d1f35db | |||
| f9ef951b22 | |||
| e8e51db9ff | |||
| a429603195 | |||
| 42833d0471 | |||
| f3f08a4b47 | |||
| b8f3d0fb53 | |||
| be38ba6c5e | |||
| 3e49ceb82a | |||
| 730e10bd6f | |||
| cb0704d51c | |||
| 8b915aae5e | |||
| f0cf1052d0 | |||
| ba05c74936 | |||
| e61d893a74 | |||
| 10ce76fcc2 | |||
| c61f5bbfe7 | |||
| 43e8da2e06 | |||
| 1758bd683e | |||
| 4b7f32c287 | |||
| 24f9e16dfd | |||
| 56a29685b4 | |||
| cf949e541f | |||
| 44167800ad | |||
| 9c6574e053 | |||
| fc113315f6 | |||
| d931bfcf83 | |||
| b58627490b | |||
| dbaa82b581 | |||
| 84123cc879 | |||
| 7b1f58a06a | |||
| fe268a9dd2 | |||
| 3c25d93eae |
+68
-26
@@ -18,10 +18,11 @@ jobs:
|
|||||||
usesh: true
|
usesh: true
|
||||||
copyback: false
|
copyback: false
|
||||||
prepare: |
|
prepare: |
|
||||||
PKG_PATH="https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/$(uname -p)/10.0_2024Q2/All" /usr/sbin/pkg_add pkgin
|
PKG_PATH="https://cdn.NetBSD.org/pub/pkgsrc/packages/NetBSD/amd64/$(uname -r | cut -d_ -f1)_${PKGSRC_BRANCH}/All" /usr/sbin/pkg_add pkgin
|
||||||
pkgin -y in gmake git bash python311 llvm clang
|
pkgin -y in gmake git bash python311 llvm clang
|
||||||
ln -s /usr/pkg/bin/python3.11 /usr/bin/python3
|
ln -s /usr/pkg/bin/python3.11 /usr/bin/python3
|
||||||
run: |
|
run: |
|
||||||
|
set -e -x
|
||||||
git config --global --add safe.directory $(pwd)
|
git config --global --add safe.directory $(pwd)
|
||||||
gmake release
|
gmake release
|
||||||
./odin version
|
./odin version
|
||||||
@@ -31,10 +32,9 @@ jobs:
|
|||||||
gmake -C vendor/miniaudio/src
|
gmake -C vendor/miniaudio/src
|
||||||
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_amd64
|
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_amd64
|
||||||
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_arm64
|
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_arm64
|
||||||
./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
|
./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false
|
./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
|
|
||||||
(cd tests/issues; ./run.sh)
|
(cd tests/issues; ./run.sh)
|
||||||
build_freebsd:
|
build_freebsd:
|
||||||
name: FreeBSD Build, Check, and Test
|
name: FreeBSD Build, Check, and Test
|
||||||
@@ -60,10 +60,9 @@ jobs:
|
|||||||
gmake -C vendor/cgltf/src
|
gmake -C vendor/cgltf/src
|
||||||
gmake -C vendor/miniaudio/src
|
gmake -C vendor/miniaudio/src
|
||||||
./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
|
./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
|
||||||
./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
|
./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false
|
./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
|
|
||||||
(cd tests/issues; ./run.sh)
|
(cd tests/issues; ./run.sh)
|
||||||
ci:
|
ci:
|
||||||
strategy:
|
strategy:
|
||||||
@@ -117,15 +116,13 @@ jobs:
|
|||||||
- name: Odin check examples/all
|
- name: Odin check examples/all
|
||||||
run: ./odin check examples/all -strict-style
|
run: ./odin check examples/all -strict-style
|
||||||
- name: Normal Core library tests
|
- name: Normal Core library tests
|
||||||
run: ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
|
run: ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
- name: Optimized Core library tests
|
- name: Optimized Core library tests
|
||||||
run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false
|
run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
- name: Vendor library tests
|
- name: Vendor library tests
|
||||||
run: ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
run: ./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
- name: Internals tests
|
- name: Internals tests
|
||||||
run: ./odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false
|
run: ./odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
- name: Core library benchmarks
|
|
||||||
run: ./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
|
|
||||||
- name: GitHub Issue tests
|
- name: GitHub Issue tests
|
||||||
run: |
|
run: |
|
||||||
cd tests/issues
|
cd tests/issues
|
||||||
@@ -179,38 +176,33 @@ jobs:
|
|||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin run examples/demo -debug
|
odin run examples/demo -debug -vet -strict-style -disallow-do
|
||||||
- name: Odin check examples/all
|
- name: Odin check examples/all
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin check examples/all -strict-style
|
odin check examples/all -vet -strict-style -disallow-do
|
||||||
- name: Core library tests
|
- name: Core library tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
|
odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
- name: Optimized core library tests
|
- name: Optimized core library tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false
|
odin test tests/core/speed.odin -o:speed -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
- name: Core library benchmarks
|
|
||||||
shell: cmd
|
|
||||||
run: |
|
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
|
||||||
odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
|
|
||||||
- name: Vendor library tests
|
- name: Vendor library tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
copy vendor\lua\5.4\windows\*.dll .
|
copy vendor\lua\5.4\windows\*.dll .
|
||||||
odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
- name: Odin internals tests
|
- name: Odin internals tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false
|
odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
|
||||||
- name: Odin documentation tests
|
- name: Odin documentation tests
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
@@ -228,3 +220,53 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||||
odin check examples/all -strict-style -target:windows_i386
|
odin check examples/all -strict-style -target:windows_i386
|
||||||
|
|
||||||
|
build_linux_riscv64:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Linux riscv64 (emulated) Build, Check and Test
|
||||||
|
timeout-minutes: 15
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download LLVM (Linux)
|
||||||
|
run: |
|
||||||
|
wget https://apt.llvm.org/llvm.sh
|
||||||
|
chmod +x llvm.sh
|
||||||
|
sudo ./llvm.sh 18
|
||||||
|
echo "/usr/lib/llvm-18/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
|
- name: Build Odin
|
||||||
|
run: ./build_odin.sh release
|
||||||
|
|
||||||
|
- name: Odin version
|
||||||
|
run: ./odin version
|
||||||
|
|
||||||
|
- name: Odin report
|
||||||
|
run: ./odin report
|
||||||
|
|
||||||
|
- name: Compile needed Vendor
|
||||||
|
run: |
|
||||||
|
make -C vendor/stb/src
|
||||||
|
make -C vendor/cgltf/src
|
||||||
|
make -C vendor/miniaudio/src
|
||||||
|
|
||||||
|
- name: Odin check
|
||||||
|
run: ./odin check examples/all -target:linux_riscv64 -vet -strict-style -disallow-do
|
||||||
|
|
||||||
|
- name: Install riscv64 toolchain and qemu
|
||||||
|
run: sudo apt-get install -y qemu-user qemu-user-static gcc-12-riscv64-linux-gnu libc6-riscv64-cross
|
||||||
|
|
||||||
|
- name: Odin run
|
||||||
|
run: ./odin run examples/demo -vet -strict-style -disallow-do -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
|
||||||
|
|
||||||
|
- name: Odin run -debug
|
||||||
|
run: ./odin run examples/demo -debug -vet -strict-style -disallow-do -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
|
||||||
|
|
||||||
|
- name: Normal Core library tests
|
||||||
|
run: ./odin test tests/core/normal.odin -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
|
||||||
|
|
||||||
|
- name: Optimized Core library tests
|
||||||
|
run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
|
||||||
|
|
||||||
|
- name: Internals tests
|
||||||
|
run: ./odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true -target:linux_riscv64 -extra-linker-flags:"-fuse-ld=/usr/bin/riscv64-linux-gnu-gcc-12 -static -Wl,-static" -no-rpath
|
||||||
|
|||||||
@@ -36,44 +36,54 @@ jobs:
|
|||||||
cp -r bin dist
|
cp -r bin dist
|
||||||
cp -r examples dist
|
cp -r examples dist
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: windows_artifacts
|
name: windows_artifacts
|
||||||
path: dist
|
path: dist
|
||||||
build_ubuntu:
|
build_linux:
|
||||||
name: Ubuntu Build
|
name: Linux Build
|
||||||
if: github.repository == 'odin-lang/Odin'
|
if: github.repository == 'odin-lang/Odin'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- uses: jirutka/setup-alpine@v1
|
||||||
|
with:
|
||||||
|
branch: v3.20
|
||||||
- name: (Linux) Download LLVM
|
- name: (Linux) Download LLVM
|
||||||
run: |
|
run: |
|
||||||
wget https://apt.llvm.org/llvm.sh
|
apk add --no-cache \
|
||||||
chmod +x llvm.sh
|
musl-dev llvm18-dev clang18 git mold lz4 \
|
||||||
sudo ./llvm.sh 18
|
libxml2-static llvm18-static zlib-static zstd-static \
|
||||||
echo "/usr/lib/llvm-18/bin" >> $GITHUB_PATH
|
make
|
||||||
|
shell: alpine.sh --root {0}
|
||||||
- name: build odin
|
- name: build odin
|
||||||
run: make nightly
|
# NOTE: this build does slow compile times because of musl
|
||||||
|
run: ci/build_linux_static.sh
|
||||||
|
shell: alpine.sh {0}
|
||||||
- name: Odin run
|
- name: Odin run
|
||||||
run: ./odin run examples/demo
|
run: ./odin run examples/demo
|
||||||
- name: Copy artifacts
|
- name: Copy artifacts
|
||||||
run: |
|
run: |
|
||||||
mkdir dist
|
FILE="odin-linux-amd64-nightly+$(date -I)"
|
||||||
cp odin dist
|
mkdir $FILE
|
||||||
cp LICENSE dist
|
cp odin $FILE
|
||||||
cp libLLVM* dist
|
cp LICENSE $FILE
|
||||||
cp -r shared dist
|
cp -r shared $FILE
|
||||||
cp -r base dist
|
cp -r base $FILE
|
||||||
cp -r core dist
|
cp -r core $FILE
|
||||||
cp -r vendor dist
|
cp -r vendor $FILE
|
||||||
cp -r examples dist
|
cp -r examples $FILE
|
||||||
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
# Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
||||||
zip -r dist.zip dist
|
tar -czvf dist.tar.gz $FILE
|
||||||
|
- name: Odin run
|
||||||
|
run: |
|
||||||
|
FILE="odin-linux-amd64-nightly+$(date -I)"
|
||||||
|
$FILE/odin run examples/demo
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ubuntu_artifacts
|
name: linux_artifacts
|
||||||
path: dist.zip
|
path: dist.tar.gz
|
||||||
build_macos:
|
build_macos:
|
||||||
name: MacOS Build
|
name: MacOS Build
|
||||||
if: github.repository == 'odin-lang/Odin'
|
if: github.repository == 'odin-lang/Odin'
|
||||||
@@ -90,24 +100,27 @@ jobs:
|
|||||||
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
|
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
|
||||||
- name: Bundle
|
- name: Bundle
|
||||||
run: |
|
run: |
|
||||||
mkdir dist
|
FILE="odin-macos-amd64-nightly+$(date -I)"
|
||||||
cp odin dist
|
mkdir $FILE
|
||||||
cp LICENSE dist
|
cp odin $FILE
|
||||||
cp -r shared dist
|
cp LICENSE $FILE
|
||||||
cp -r base dist
|
cp -r shared $FILE
|
||||||
cp -r core dist
|
cp -r base $FILE
|
||||||
cp -r vendor dist
|
cp -r core $FILE
|
||||||
cp -r examples dist
|
cp -r vendor $FILE
|
||||||
dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
|
cp -r examples $FILE
|
||||||
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
dylibbundler -b -x $FILE/odin -d $FILE/libs -od -p @executable_path/libs
|
||||||
zip -r dist.zip dist
|
# Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
||||||
|
tar -czvf dist.tar.gz $FILE
|
||||||
- name: Odin run
|
- name: Odin run
|
||||||
run: ./dist/odin run examples/demo
|
run: |
|
||||||
|
FILE="odin-macos-amd64-nightly+$(date -I)"
|
||||||
|
$FILE/odin run examples/demo
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: macos_artifacts
|
name: macos_artifacts
|
||||||
path: dist.zip
|
path: dist.tar.gz
|
||||||
build_macos_arm:
|
build_macos_arm:
|
||||||
name: MacOS ARM Build
|
name: MacOS ARM Build
|
||||||
if: github.repository == 'odin-lang/Odin'
|
if: github.repository == 'odin-lang/Odin'
|
||||||
@@ -124,30 +137,33 @@ jobs:
|
|||||||
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
|
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
|
||||||
- name: Bundle
|
- name: Bundle
|
||||||
run: |
|
run: |
|
||||||
mkdir dist
|
FILE="odin-macos-arm64-nightly+$(date -I)"
|
||||||
cp odin dist
|
mkdir $FILE
|
||||||
cp LICENSE dist
|
cp odin $FILE
|
||||||
cp -r shared dist
|
cp LICENSE $FILE
|
||||||
cp -r base dist
|
cp -r shared $FILE
|
||||||
cp -r core dist
|
cp -r base $FILE
|
||||||
cp -r vendor dist
|
cp -r core $FILE
|
||||||
cp -r examples dist
|
cp -r vendor $FILE
|
||||||
dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
|
cp -r examples $FILE
|
||||||
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
dylibbundler -b -x $FILE/odin -d $FILE/libs -od -p @executable_path/libs
|
||||||
zip -r dist.zip dist
|
# Creating a tarball so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
||||||
|
tar -czvf dist.tar.gz $FILE
|
||||||
- name: Odin run
|
- name: Odin run
|
||||||
run: ./dist/odin run examples/demo
|
run: |
|
||||||
|
FILE="odin-macos-arm64-nightly+$(date -I)"
|
||||||
|
$FILE/odin run examples/demo
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: macos_arm_artifacts
|
name: macos_arm_artifacts
|
||||||
path: dist.zip
|
path: dist.tar.gz
|
||||||
upload_b2:
|
upload_b2:
|
||||||
runs-on: [ubuntu-latest]
|
runs-on: [ubuntu-latest]
|
||||||
needs: [build_windows, build_macos, build_macos_arm, build_ubuntu]
|
needs: [build_windows, build_macos, build_macos_arm, build_linux]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v2
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.8.x'
|
python-version: '3.8.x'
|
||||||
|
|
||||||
@@ -161,24 +177,33 @@ jobs:
|
|||||||
run: python -c "import sys; print(sys.version)"
|
run: python -c "import sys; print(sys.version)"
|
||||||
|
|
||||||
- name: Download Windows artifacts
|
- name: Download Windows artifacts
|
||||||
uses: actions/download-artifact@v1
|
|
||||||
|
uses: actions/download-artifact@v4.1.7
|
||||||
with:
|
with:
|
||||||
name: windows_artifacts
|
name: windows_artifacts
|
||||||
|
path: windows_artifacts
|
||||||
|
|
||||||
- name: Download Ubuntu artifacts
|
- name: Download Ubuntu artifacts
|
||||||
uses: actions/download-artifact@v1
|
uses: actions/download-artifact@v4.1.7
|
||||||
with:
|
with:
|
||||||
name: ubuntu_artifacts
|
name: linux_artifacts
|
||||||
|
path: linux_artifacts
|
||||||
|
|
||||||
- name: Download macOS artifacts
|
- name: Download macOS artifacts
|
||||||
uses: actions/download-artifact@v1
|
uses: actions/download-artifact@v4.1.7
|
||||||
with:
|
with:
|
||||||
name: macos_artifacts
|
name: macos_artifacts
|
||||||
|
path: macos_artifacts
|
||||||
|
|
||||||
- name: Download macOS arm artifacts
|
- name: Download macOS arm artifacts
|
||||||
uses: actions/download-artifact@v1
|
uses: actions/download-artifact@v4.1.7
|
||||||
with:
|
with:
|
||||||
name: macos_arm_artifacts
|
name: macos_arm_artifacts
|
||||||
|
path: macos_arm_artifacts
|
||||||
|
|
||||||
|
- name: Debug
|
||||||
|
run: |
|
||||||
|
tree -L 2
|
||||||
|
|
||||||
- name: Create archives and upload
|
- name: Create archives and upload
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -188,9 +213,10 @@ jobs:
|
|||||||
BUCKET: ${{ secrets.B2_BUCKET }}
|
BUCKET: ${{ secrets.B2_BUCKET }}
|
||||||
DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
|
DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
|
||||||
run: |
|
run: |
|
||||||
|
file linux_artifacts/dist.tar.gz
|
||||||
python3 ci/nightly.py artifact windows-amd64 windows_artifacts/
|
python3 ci/nightly.py artifact windows-amd64 windows_artifacts/
|
||||||
python3 ci/nightly.py artifact ubuntu-amd64 ubuntu_artifacts/dist.zip
|
python3 ci/nightly.py artifact linux-amd64 linux_artifacts/dist.tar.gz
|
||||||
python3 ci/nightly.py artifact macos-amd64 macos_artifacts/dist.zip
|
python3 ci/nightly.py artifact macos-amd64 macos_artifacts/dist.tar.gz
|
||||||
python3 ci/nightly.py artifact macos-arm64 macos_arm_artifacts/dist.zip
|
python3 ci/nightly.py artifact macos-arm64 macos_arm_artifacts/dist.tar.gz
|
||||||
python3 ci/nightly.py prune
|
python3 ci/nightly.py prune
|
||||||
python3 ci/nightly.py json
|
python3 ci/nightly.py json
|
||||||
|
|||||||
+4
-3
@@ -17,13 +17,12 @@
|
|||||||
[Rr]eleases/
|
[Rr]eleases/
|
||||||
x64/
|
x64/
|
||||||
x86/
|
x86/
|
||||||
|
!/core/simd/x86
|
||||||
bld/
|
bld/
|
||||||
[Bb]in/
|
[Bb]in/
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
[Ll]og/
|
[Ll]og/
|
||||||
![Cc]ore/[Ll]og/
|
![Cc]ore/[Ll]og/
|
||||||
tests/documentation/verify/
|
|
||||||
tests/documentation/all.odin-doc
|
|
||||||
# Visual Studio 2015 cache/options directory
|
# Visual Studio 2015 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
# Visual Studio Code options directory
|
# Visual Studio Code options directory
|
||||||
@@ -31,7 +30,6 @@ tests/documentation/all.odin-doc
|
|||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
#wwwroot/
|
#wwwroot/
|
||||||
demo
|
demo
|
||||||
benchmark
|
|
||||||
|
|
||||||
# MSTest test Results
|
# MSTest test Results
|
||||||
[Tt]est[Rr]esult*/
|
[Tt]est[Rr]esult*/
|
||||||
@@ -268,6 +266,9 @@ bin/
|
|||||||
*.exe
|
*.exe
|
||||||
*.obj
|
*.obj
|
||||||
*.pdb
|
*.pdb
|
||||||
|
*.res
|
||||||
|
desktop.ini
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
# - Linux/MacOS
|
# - Linux/MacOS
|
||||||
odin
|
odin
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
all: debug
|
all: default
|
||||||
|
|
||||||
demo:
|
demo:
|
||||||
./odin run examples/demo/demo.odin -file
|
./odin run examples/demo/demo.odin -file
|
||||||
@@ -6,12 +6,18 @@ demo:
|
|||||||
report:
|
report:
|
||||||
./odin report
|
./odin report
|
||||||
|
|
||||||
|
default:
|
||||||
|
PROGRAM=make ./build_odin.sh # debug
|
||||||
|
|
||||||
debug:
|
debug:
|
||||||
./build_odin.sh debug
|
./build_odin.sh debug
|
||||||
|
|
||||||
release:
|
release:
|
||||||
./build_odin.sh release
|
./build_odin.sh release
|
||||||
|
|
||||||
|
release-native:
|
||||||
|
./build_odin.sh release-native
|
||||||
|
|
||||||
release_native:
|
release_native:
|
||||||
./build_odin.sh release-native
|
./build_odin.sh release-native
|
||||||
|
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ Answers to common questions about Odin.
|
|||||||
|
|
||||||
Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
|
Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
|
||||||
|
|
||||||
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
|
#### [Odin Documentation](https://odin-lang.org/docs/)
|
||||||
|
|
||||||
A wiki maintained by the Odin community.
|
Documentation for the Odin language itself.
|
||||||
|
|
||||||
#### [Odin Discord](https://discord.gg/sVBPHEv)
|
#### [Odin Discord](https://discord.gg/sVBPHEv)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// This is purely for documentation
|
// This is purely for documentation
|
||||||
//+build ignore
|
#+build ignore
|
||||||
package intrinsics
|
package intrinsics
|
||||||
|
|
||||||
// Package-Related
|
// Package-Related
|
||||||
@@ -42,8 +42,8 @@ overflow_add :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #option
|
|||||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
||||||
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
||||||
|
|
||||||
add_sat :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
|
saturating_add :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
|
||||||
sub_sat :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
|
saturating_sub :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
|
||||||
|
|
||||||
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
||||||
|
|
||||||
@@ -219,14 +219,21 @@ type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
|
|||||||
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||||
type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
|
type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
|
||||||
|
|
||||||
|
type_has_shared_fields :: proc($U, $V: typeid) -> bool typeid where type_is_struct(U), type_is_struct(V) ---
|
||||||
|
|
||||||
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||||
|
|
||||||
|
constant_log2 :: proc($v: $T) -> T where type_is_integer(T) ---
|
||||||
|
|
||||||
// SIMD related
|
// SIMD related
|
||||||
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) ---
|
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) ---
|
||||||
|
|
||||||
|
simd_saturating_add :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_integer(T) ---
|
||||||
|
simd_saturating_sub :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_integer(T) ---
|
||||||
|
|
||||||
// Keeps Odin's Behaviour
|
// Keeps Odin's Behaviour
|
||||||
// (x << y) if y <= mask else 0
|
// (x << y) if y <= mask else 0
|
||||||
simd_shl :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
simd_shl :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||||
@@ -237,9 +244,6 @@ simd_shr :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
|||||||
simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||||
simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||||
|
|
||||||
simd_add_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
|
||||||
simd_sub_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
|
||||||
|
|
||||||
simd_bit_and :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_bit_and :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_bit_or :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_bit_or :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
simd_bit_xor :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
simd_bit_xor :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||||
@@ -268,13 +272,28 @@ simd_lanes_ge :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
|||||||
simd_extract :: proc(a: #simd[N]T, idx: uint) -> T ---
|
simd_extract :: proc(a: #simd[N]T, idx: uint) -> T ---
|
||||||
simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T ---
|
simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T ---
|
||||||
|
|
||||||
simd_reduce_add_ordered :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_add_ordered :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_mul_ordered :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_mul_ordered :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_min :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_min :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_max :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_max :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_and :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_and :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_or :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_or :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
simd_reduce_xor :: proc(a: #simd[N]T) -> T ---
|
simd_reduce_xor :: proc(a: #simd[N]T) -> T where type_is_integer(T) || type_is_float(T)---
|
||||||
|
|
||||||
|
simd_reduce_any :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
|
||||||
|
simd_reduce_all :: proc(a: #simd[N]T) -> T where type_is_boolean(T) ---
|
||||||
|
|
||||||
|
|
||||||
|
simd_gather :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
simd_scatter :: proc(ptr: #simd[N]rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
|
||||||
|
simd_masked_load :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
simd_masked_store :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
|
||||||
|
simd_masked_expand_load :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) -> #simd[N]T where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
simd_masked_compress_store :: proc(ptr: rawptr, val: #simd[N]T, mask: #simd[N]U) where type_is_integer(U) || type_is_boolean(U) ---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T ---
|
simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T ---
|
||||||
simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T ---
|
simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T ---
|
||||||
@@ -288,11 +307,11 @@ simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
|||||||
|
|
||||||
simd_to_bits :: proc(v: #simd[N]T) -> #simd[N]Integer where size_of(T) == size_of(Integer), type_is_unsigned(Integer) ---
|
simd_to_bits :: proc(v: #simd[N]T) -> #simd[N]Integer where size_of(T) == size_of(Integer), type_is_unsigned(Integer) ---
|
||||||
|
|
||||||
// equivalent a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0)
|
// equivalent to a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0)
|
||||||
simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
|
simd_lanes_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
|
||||||
|
|
||||||
simd_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
simd_lanes_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||||
simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
simd_lanes_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||||
|
|
||||||
// Checks if the current target supports the given target features.
|
// Checks if the current target supports the given target features.
|
||||||
//
|
//
|
||||||
|
|||||||
+34
-1
@@ -18,7 +18,7 @@
|
|||||||
// This could change at a later date if the all these data structures are
|
// This could change at a later date if the all these data structures are
|
||||||
// implemented within the compiler rather than in this "preload" file
|
// implemented within the compiler rather than in this "preload" file
|
||||||
//
|
//
|
||||||
//+no-instrumentation
|
#+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
@@ -546,10 +546,23 @@ Odin_OS_Type :: type_of(ODIN_OS)
|
|||||||
arm64,
|
arm64,
|
||||||
wasm32,
|
wasm32,
|
||||||
wasm64p32,
|
wasm64p32,
|
||||||
|
riscv64,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
Odin_Arch_Type :: type_of(ODIN_ARCH)
|
Odin_Arch_Type :: type_of(ODIN_ARCH)
|
||||||
|
|
||||||
|
Odin_Arch_Types :: bit_set[Odin_Arch_Type]
|
||||||
|
|
||||||
|
ALL_ODIN_ARCH_TYPES :: Odin_Arch_Types{
|
||||||
|
.amd64,
|
||||||
|
.i386,
|
||||||
|
.arm32,
|
||||||
|
.arm64,
|
||||||
|
.wasm32,
|
||||||
|
.wasm64p32,
|
||||||
|
.riscv64,
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Defined internally by the compiler
|
// Defined internally by the compiler
|
||||||
Odin_Build_Mode_Type :: enum int {
|
Odin_Build_Mode_Type :: enum int {
|
||||||
@@ -573,6 +586,22 @@ Odin_Build_Mode_Type :: type_of(ODIN_BUILD_MODE)
|
|||||||
*/
|
*/
|
||||||
Odin_Endian_Type :: type_of(ODIN_ENDIAN)
|
Odin_Endian_Type :: type_of(ODIN_ENDIAN)
|
||||||
|
|
||||||
|
Odin_OS_Types :: bit_set[Odin_OS_Type]
|
||||||
|
|
||||||
|
ALL_ODIN_OS_TYPES :: Odin_OS_Types{
|
||||||
|
.Windows,
|
||||||
|
.Darwin,
|
||||||
|
.Linux,
|
||||||
|
.Essence,
|
||||||
|
.FreeBSD,
|
||||||
|
.OpenBSD,
|
||||||
|
.NetBSD,
|
||||||
|
.Haiku,
|
||||||
|
.WASI,
|
||||||
|
.JS,
|
||||||
|
.Orca,
|
||||||
|
.Freestanding,
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Defined internally by the compiler
|
// Defined internally by the compiler
|
||||||
@@ -750,6 +779,10 @@ __init_context :: proc "contextless" (c: ^Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
|
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
|
||||||
|
default_assertion_contextless_failure_proc(prefix, message, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
default_assertion_contextless_failure_proc :: proc "contextless" (prefix, message: string, loc: Source_Code_Location) -> ! {
|
||||||
when ODIN_OS == .Freestanding {
|
when ODIN_OS == .Freestanding {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -6,6 +6,39 @@ import "base:intrinsics"
|
|||||||
Maybe :: union($T: typeid) {T}
|
Maybe :: union($T: typeid) {T}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Recovers the containing/parent struct from a pointer to one of its fields.
|
||||||
|
Works by "walking back" to the struct's starting address using the offset between the field and the struct.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- ptr: Pointer to the field of a container struct
|
||||||
|
- T: The type of the container struct
|
||||||
|
- field_name: The name of the field in the `T` struct
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- A pointer to the container struct based on a pointer to a field in it
|
||||||
|
|
||||||
|
Example:
|
||||||
|
package container_of
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
|
Node :: struct {
|
||||||
|
value: int,
|
||||||
|
prev: ^Node,
|
||||||
|
next: ^Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
node: Node
|
||||||
|
field_ptr := &node.next
|
||||||
|
container_struct_ptr: ^Node = runtime.container_of(field_ptr, Node, "next")
|
||||||
|
assert(container_struct_ptr == &node)
|
||||||
|
assert(uintptr(field_ptr) - uintptr(container_struct_ptr) == size_of(node.value) + size_of(node.prev))
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
^Node
|
||||||
|
*/
|
||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
|
container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
|
||||||
where intrinsics.type_has_field(T, field_name),
|
where intrinsics.type_has_field(T, field_name),
|
||||||
@@ -68,7 +101,7 @@ copy :: proc{copy_slice, copy_from_string}
|
|||||||
// Note: If you want the elements to remain in their order, use `ordered_remove`.
|
// Note: If you want the elements to remain in their order, use `ordered_remove`.
|
||||||
// Note: If the index is out of bounds, this procedure will panic.
|
// Note: If the index is out of bounds, this procedure will panic.
|
||||||
@builtin
|
@builtin
|
||||||
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
|
unordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||||
bounds_check_error_loc(loc, index, len(array))
|
bounds_check_error_loc(loc, index, len(array))
|
||||||
n := len(array)-1
|
n := len(array)-1
|
||||||
if index != n {
|
if index != n {
|
||||||
@@ -82,7 +115,7 @@ unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_loca
|
|||||||
// Note: If the elements do not have to remain in their order, prefer `unordered_remove`.
|
// Note: If the elements do not have to remain in their order, prefer `unordered_remove`.
|
||||||
// Note: If the index is out of bounds, this procedure will panic.
|
// Note: If the index is out of bounds, this procedure will panic.
|
||||||
@builtin
|
@builtin
|
||||||
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
|
ordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||||
bounds_check_error_loc(loc, index, len(array))
|
bounds_check_error_loc(loc, index, len(array))
|
||||||
if index+1 < len(array) {
|
if index+1 < len(array) {
|
||||||
copy(array[index:], array[index+1:])
|
copy(array[index:], array[index+1:])
|
||||||
@@ -95,7 +128,7 @@ ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_locati
|
|||||||
// Note: This is an O(N) operation.
|
// Note: This is an O(N) operation.
|
||||||
// Note: If the range is out of bounds, this procedure will panic.
|
// Note: If the range is out of bounds, this procedure will panic.
|
||||||
@builtin
|
@builtin
|
||||||
remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) #no_bounds_check {
|
remove_range :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #caller_location) #no_bounds_check {
|
||||||
slice_expr_error_lo_hi_loc(loc, lo, hi, len(array))
|
slice_expr_error_lo_hi_loc(loc, lo, hi, len(array))
|
||||||
n := max(hi-lo, 0)
|
n := max(hi-lo, 0)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
@@ -350,7 +383,7 @@ _make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, ali
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// `make_map` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
// `make_map` allocates and initializes a map. Like `new`, the first argument is a type, not a value.
|
||||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||||
//
|
//
|
||||||
// Note: Prefer using the procedure group `make`.
|
// Note: Prefer using the procedure group `make`.
|
||||||
@@ -362,7 +395,7 @@ make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_
|
|||||||
err = reserve_map(&m, capacity, loc)
|
err = reserve_map(&m, capacity, loc)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// `make_multi_pointer` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
|
// `make_multi_pointer` allocates and initializes a multi-pointer. Like `new`, the first argument is a type, not a value.
|
||||||
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
|
||||||
//
|
//
|
||||||
// This is "similar" to doing `raw_data(make([]E, len, allocator))`.
|
// This is "similar" to doing `raw_data(make([]E, len, allocator))`.
|
||||||
@@ -602,7 +635,7 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -620,7 +653,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast arg: E,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -643,7 +676,7 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args:
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -668,7 +701,7 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
assign_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||||
if index < len(array) {
|
if index < len(array) {
|
||||||
array[index] = arg
|
array[index] = arg
|
||||||
ok = true
|
ok = true
|
||||||
@@ -682,7 +715,7 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
assign_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||||
new_size := index + len(args)
|
new_size := index + len(args)
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
ok = true
|
ok = true
|
||||||
@@ -699,7 +732,7 @@ assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args:
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||||
new_size := index + len(arg)
|
new_size := index + len(arg)
|
||||||
if len(arg) == 0 {
|
if len(arg) == 0 {
|
||||||
ok = true
|
ok = true
|
||||||
@@ -838,7 +871,7 @@ non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: i
|
|||||||
|
|
||||||
Note: Prefer the procedure group `shrink`
|
Note: Prefer the procedure group `shrink`
|
||||||
*/
|
*/
|
||||||
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
|
||||||
return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc)
|
return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -913,7 +946,7 @@ card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
|||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
@(disabled=ODIN_DISABLE_ASSERT)
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
assert :: proc(condition: bool, message := "", loc := #caller_location) {
|
assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||||
if !condition {
|
if !condition {
|
||||||
// NOTE(bill): This is wrapped in a procedure call
|
// NOTE(bill): This is wrapped in a procedure call
|
||||||
// to improve performance to make the CPU not
|
// to improve performance to make the CPU not
|
||||||
@@ -948,3 +981,30 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
|||||||
}
|
}
|
||||||
p("not yet implemented", message, loc)
|
p("not yet implemented", message, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
@(disabled=ODIN_DISABLE_ASSERT)
|
||||||
|
assert_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
|
||||||
|
if !condition {
|
||||||
|
// NOTE(bill): This is wrapped in a procedure call
|
||||||
|
// to improve performance to make the CPU not
|
||||||
|
// execute speculatively, making it about an order of
|
||||||
|
// magnitude faster
|
||||||
|
@(cold)
|
||||||
|
internal :: proc "contextless" (message: string, loc: Source_Code_Location) {
|
||||||
|
default_assertion_contextless_failure_proc("runtime assertion", message, loc)
|
||||||
|
}
|
||||||
|
internal(message, loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
panic_contextless :: proc "contextless" (message: string, loc := #caller_location) -> ! {
|
||||||
|
default_assertion_contextless_failure_proc("panic", message, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
unimplemented_contextless :: proc "contextless" (message := "", loc := #caller_location) -> ! {
|
||||||
|
default_assertion_contextless_failure_proc("not yet implemented", message, loc)
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ raw_soa_footer :: proc{
|
|||||||
|
|
||||||
|
|
||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
make_soa_aligned :: proc($T: typeid/#soa[]$E, #any_int length, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||||
if length <= 0 {
|
if length <= 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(builtin, require_results)
|
@(builtin, require_results)
|
||||||
make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
make_soa_slice :: proc($T: typeid/#soa[]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||||
return make_soa_aligned(T, length, align_of(E), allocator, loc)
|
return make_soa_aligned(T, length, align_of(E), allocator, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ make_soa :: proc{
|
|||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
resize_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -183,7 +183,7 @@ resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_locat
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
non_zero_resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
non_zero_resize_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -194,12 +194,12 @@ non_zero_resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #cal
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
return _reserve_soa(array, capacity, true, loc)
|
return _reserve_soa(array, capacity, true, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
non_zero_reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
non_zero_reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
return _reserve_soa(array, capacity, false, loc)
|
return _reserve_soa(array, capacity, false, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,7 +484,7 @@ into_dynamic_soa :: proc(array: $T/#soa[]$E) -> #soa[dynamic]E {
|
|||||||
// Note: If you the elements to remain in their order, use `ordered_remove_soa`.
|
// Note: If you the elements to remain in their order, use `ordered_remove_soa`.
|
||||||
// Note: If the index is out of bounds, this procedure will panic.
|
// Note: If the index is out of bounds, this procedure will panic.
|
||||||
@builtin
|
@builtin
|
||||||
unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #caller_location) #no_bounds_check {
|
unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||||
bounds_check_error_loc(loc, index, len(array))
|
bounds_check_error_loc(loc, index, len(array))
|
||||||
if index+1 < len(array) {
|
if index+1 < len(array) {
|
||||||
ti := type_info_of(typeid_of(T))
|
ti := type_info_of(typeid_of(T))
|
||||||
@@ -512,7 +512,7 @@ unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #cal
|
|||||||
// Note: If you the elements do not have to remain in their order, prefer `unordered_remove_soa`.
|
// Note: If you the elements do not have to remain in their order, prefer `unordered_remove_soa`.
|
||||||
// Note: If the index is out of bounds, this procedure will panic.
|
// Note: If the index is out of bounds, this procedure will panic.
|
||||||
@builtin
|
@builtin
|
||||||
ordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #caller_location) #no_bounds_check {
|
ordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, #any_int index: int, loc := #caller_location) #no_bounds_check {
|
||||||
bounds_check_error_loc(loc, index, len(array))
|
bounds_check_error_loc(loc, index, len(array))
|
||||||
if index+1 < len(array) {
|
if index+1 < len(array) {
|
||||||
ti := type_info_of(typeid_of(T))
|
ti := type_info_of(typeid_of(T))
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||||
size, alignment: int,
|
size, alignment: int,
|
||||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc, .Alloc_Non_Zeroed:
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
return nil, .Out_Of_Memory
|
return nil, .Out_Of_Memory
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// `arena_init` will initialize the arena with a usuable block.
|
// `arena_init` will initialize the arena with a usable block.
|
||||||
// This procedure is not necessary to use the Arena as the default zero as `arena_alloc` will set things up if necessary
|
// This procedure is not necessary to use the Arena as the default zero as `arena_alloc` will set things up if necessary
|
||||||
@(require_results)
|
@(require_results)
|
||||||
arena_init :: proc(arena: ^Arena, size: uint, backing_allocator: Allocator, loc := #caller_location) -> Allocator_Error {
|
arena_init :: proc(arena: ^Arena, size: uint, backing_allocator: Allocator, loc := #caller_location) -> Allocator_Error {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//+private
|
#+private
|
||||||
//+build linux, darwin, freebsd, openbsd, netbsd, haiku
|
#+build linux, darwin, freebsd, openbsd, netbsd, haiku
|
||||||
//+no-instrumentation
|
#+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
@@ -34,6 +34,9 @@ when ODIN_BUILD_MODE == .Dynamic {
|
|||||||
} else when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
|
} else when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
|
||||||
@require foreign import entry "entry_unix_no_crt_darwin_arm64.asm"
|
@require foreign import entry "entry_unix_no_crt_darwin_arm64.asm"
|
||||||
SYS_exit :: 1
|
SYS_exit :: 1
|
||||||
|
} else when ODIN_ARCH == .riscv64 {
|
||||||
|
@require foreign import entry "entry_unix_no_crt_riscv64.asm"
|
||||||
|
SYS_exit :: 93
|
||||||
}
|
}
|
||||||
@(link_name="_start_odin", linkage="strong", require)
|
@(link_name="_start_odin", linkage="strong", require)
|
||||||
_start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! {
|
_start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! {
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
.text
|
||||||
|
|
||||||
|
.globl _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
ld a0, 0(sp)
|
||||||
|
addi a1, sp, 8
|
||||||
|
addi sp, sp, ~15
|
||||||
|
call _start_odin
|
||||||
|
ebreak
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
//+private
|
#+private
|
||||||
//+build wasm32, wasm64p32
|
#+build wasm32, wasm64p32
|
||||||
//+no-instrumentation
|
#+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//+private
|
#+private
|
||||||
//+build windows
|
#+build windows
|
||||||
//+no-instrumentation
|
#+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|||||||
@@ -19,29 +19,41 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
// the pointer we return to the user.
|
// the pointer we return to the user.
|
||||||
//
|
//
|
||||||
|
|
||||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, Allocator_Error) {
|
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr, old_size: int, zero_memory := true) -> ([]byte, Allocator_Error) {
|
||||||
|
// Not(flysand): We need to reserve enough space for alignment, which
|
||||||
|
// includes the user data itself, the space to store the pointer to
|
||||||
|
// allocation start, as well as the padding required to align both
|
||||||
|
// the user data and the pointer.
|
||||||
a := max(alignment, align_of(rawptr))
|
a := max(alignment, align_of(rawptr))
|
||||||
space := size + a - 1
|
space := a-1 + size_of(rawptr) + size
|
||||||
|
|
||||||
allocated_mem: rawptr
|
allocated_mem: rawptr
|
||||||
if old_ptr != nil {
|
|
||||||
|
force_copy := old_ptr != nil && alignment > align_of(rawptr)
|
||||||
|
|
||||||
|
if old_ptr != nil && !force_copy {
|
||||||
original_old_ptr := ([^]rawptr)(old_ptr)[-1]
|
original_old_ptr := ([^]rawptr)(old_ptr)[-1]
|
||||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
allocated_mem = heap_resize(original_old_ptr, space)
|
||||||
} else {
|
} else {
|
||||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
allocated_mem = heap_alloc(space, zero_memory)
|
||||||
}
|
}
|
||||||
aligned_mem := rawptr(([^]u8)(allocated_mem)[size_of(rawptr):])
|
aligned_mem := rawptr(([^]u8)(allocated_mem)[size_of(rawptr):])
|
||||||
|
|
||||||
ptr := uintptr(aligned_mem)
|
ptr := uintptr(aligned_mem)
|
||||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
|
aligned_ptr := (ptr + uintptr(a)-1) & ~(uintptr(a)-1)
|
||||||
diff := int(aligned_ptr - ptr)
|
if allocated_mem == nil {
|
||||||
if (size + diff) > space || allocated_mem == nil {
|
aligned_free(old_ptr)
|
||||||
|
aligned_free(allocated_mem)
|
||||||
return nil, .Out_Of_Memory
|
return nil, .Out_Of_Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
aligned_mem = rawptr(aligned_ptr)
|
aligned_mem = rawptr(aligned_ptr)
|
||||||
([^]rawptr)(aligned_mem)[-1] = allocated_mem
|
([^]rawptr)(aligned_mem)[-1] = allocated_mem
|
||||||
|
|
||||||
|
if force_copy {
|
||||||
|
mem_copy_non_overlapping(aligned_mem, old_ptr, min(old_size, size))
|
||||||
|
aligned_free(old_ptr)
|
||||||
|
}
|
||||||
|
|
||||||
return byte_slice(aligned_mem, size), nil
|
return byte_slice(aligned_mem, size), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,10 +65,10 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
|
|
||||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int, zero_memory := true) -> (new_memory: []byte, err: Allocator_Error) {
|
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int, zero_memory := true) -> (new_memory: []byte, err: Allocator_Error) {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return nil, nil
|
return aligned_alloc(new_size, new_alignment, nil, old_size, zero_memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
new_memory = aligned_alloc(new_size, new_alignment, p, zero_memory) or_return
|
new_memory = aligned_alloc(new_size, new_alignment, p, old_size, zero_memory) or_return
|
||||||
|
|
||||||
// NOTE: heap_resize does not zero the new memory, so we do it
|
// NOTE: heap_resize does not zero the new memory, so we do it
|
||||||
if zero_memory && new_size > old_size {
|
if zero_memory && new_size > old_size {
|
||||||
@@ -68,7 +80,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
|
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Alloc, .Alloc_Non_Zeroed:
|
case .Alloc, .Alloc_Non_Zeroed:
|
||||||
return aligned_alloc(size, alignment, nil, mode == .Alloc)
|
return aligned_alloc(size, alignment, nil, 0, mode == .Alloc)
|
||||||
|
|
||||||
case .Free:
|
case .Free:
|
||||||
aligned_free(old_memory)
|
aligned_free(old_memory)
|
||||||
@@ -77,9 +89,6 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return nil, .Mode_Not_Implemented
|
return nil, .Mode_Not_Implemented
|
||||||
|
|
||||||
case .Resize, .Resize_Non_Zeroed:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
if old_memory == nil {
|
|
||||||
return aligned_alloc(size, alignment, nil, mode == .Resize)
|
|
||||||
}
|
|
||||||
return aligned_resize(old_memory, old_size, size, alignment, mode == .Resize)
|
return aligned_resize(old_memory, old_size, size, alignment, mode == .Resize)
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build orca
|
#+build orca
|
||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign {
|
foreign {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build js, wasi, freestanding, essence
|
#+build js, wasi, freestanding, essence
|
||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build linux, darwin, freebsd, openbsd, netbsd, haiku
|
#+build linux, darwin, freebsd, openbsd, netbsd, haiku
|
||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
when ODIN_OS == .Darwin {
|
when ODIN_OS == .Darwin {
|
||||||
|
|||||||
+32
-20
@@ -1,4 +1,4 @@
|
|||||||
//+vet !cast
|
#+vet !cast
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
@@ -8,10 +8,9 @@ IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32
|
|||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
RUNTIME_LINKAGE :: "strong" when (
|
RUNTIME_LINKAGE :: "strong" when (
|
||||||
(ODIN_USE_SEPARATE_MODULES ||
|
ODIN_USE_SEPARATE_MODULES ||
|
||||||
ODIN_BUILD_MODE == .Dynamic ||
|
ODIN_BUILD_MODE == .Dynamic ||
|
||||||
!ODIN_NO_CRT) &&
|
!ODIN_NO_CRT) else "internal"
|
||||||
!IS_WASM) else "internal"
|
|
||||||
RUNTIME_REQUIRE :: false // !ODIN_TILDE
|
RUNTIME_REQUIRE :: false // !ODIN_TILDE
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
@@ -119,16 +118,15 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
|
|||||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr)
|
DEFAULT_ALIGNMENT :: 2*align_of(rawptr)
|
||||||
|
|
||||||
mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
if size == 0 {
|
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
|
||||||
return nil, nil
|
if size == 0 || allocator.procedure == nil{
|
||||||
}
|
|
||||||
if allocator.procedure == nil {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
|
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
|
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
|
||||||
if size == 0 || allocator.procedure == nil {
|
if size == 0 || allocator.procedure == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -136,6 +134,7 @@ mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, a
|
|||||||
}
|
}
|
||||||
|
|
||||||
mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
|
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
|
||||||
if size == 0 || allocator.procedure == nil {
|
if size == 0 || allocator.procedure == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -175,6 +174,7 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
|
|||||||
}
|
}
|
||||||
|
|
||||||
_mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, should_zero: bool, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
_mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, should_zero: bool, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
|
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
|
||||||
if allocator.procedure == nil {
|
if allocator.procedure == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -216,9 +216,11 @@ _mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignmen
|
|||||||
}
|
}
|
||||||
|
|
||||||
mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
|
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
|
||||||
return _mem_resize(ptr, old_size, new_size, alignment, allocator, true, loc)
|
return _mem_resize(ptr, old_size, new_size, alignment, allocator, true, loc)
|
||||||
}
|
}
|
||||||
non_zero_mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
non_zero_mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
|
assert(is_power_of_two_int(alignment), "Alignment must be a power of two", loc)
|
||||||
return _mem_resize(ptr, old_size, new_size, alignment, allocator, false, loc)
|
return _mem_resize(ptr, old_size, new_size, alignment, allocator, false, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -879,9 +881,6 @@ extendhfsf2 :: proc "c" (value: __float16) -> f32 {
|
|||||||
|
|
||||||
@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
floattidf :: proc "c" (a: i128) -> f64 {
|
floattidf :: proc "c" (a: i128) -> f64 {
|
||||||
when IS_WASM {
|
|
||||||
return 0
|
|
||||||
} else {
|
|
||||||
DBL_MANT_DIG :: 53
|
DBL_MANT_DIG :: 53
|
||||||
if a == 0 {
|
if a == 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -921,14 +920,10 @@ when IS_WASM {
|
|||||||
fb[0] = u32(a) // mantissa-low
|
fb[0] = u32(a) // mantissa-low
|
||||||
return transmute(f64)fb
|
return transmute(f64)fb
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
floattidf_unsigned :: proc "c" (a: u128) -> f64 {
|
floattidf_unsigned :: proc "c" (a: u128) -> f64 {
|
||||||
when IS_WASM {
|
|
||||||
return 0
|
|
||||||
} else {
|
|
||||||
DBL_MANT_DIG :: 53
|
DBL_MANT_DIG :: 53
|
||||||
if a == 0 {
|
if a == 0 {
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -966,7 +961,6 @@ when IS_WASM {
|
|||||||
fb[0] = u32(a) // mantissa-low
|
fb[0] = u32(a) // mantissa-low
|
||||||
return transmute(f64)fb
|
return transmute(f64)fb
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1023,14 +1017,32 @@ modti3 :: proc "c" (a, b: i128) -> i128 {
|
|||||||
|
|
||||||
@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
|
||||||
u := udivmod128(u128(a), u128(b), (^u128)(rem))
|
s_a := a >> (128 - 1) // -1 if negative or 0
|
||||||
return i128(u)
|
s_b := b >> (128 - 1)
|
||||||
|
an := (a ~ s_a) - s_a // absolute
|
||||||
|
bn := (b ~ s_b) - s_b
|
||||||
|
|
||||||
|
s_b ~= s_a // quotient sign
|
||||||
|
u_s_b := u128(s_b)
|
||||||
|
u_s_a := u128(s_a)
|
||||||
|
|
||||||
|
r: u128 = ---
|
||||||
|
u := i128((udivmodti4(u128(an), u128(bn), &r) ~ u_s_b) - u_s_b) // negate if negative
|
||||||
|
rem^ = i128((r ~ u_s_a) - u_s_a)
|
||||||
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||||
divti3 :: proc "c" (a, b: i128) -> i128 {
|
divti3 :: proc "c" (a, b: i128) -> i128 {
|
||||||
u := udivmodti4(u128(a), u128(b), nil)
|
s_a := a >> (128 - 1) // -1 if negative or 0
|
||||||
return i128(u)
|
s_b := b >> (128 - 1)
|
||||||
|
an := (a ~ s_a) - s_a // absolute
|
||||||
|
bn := (b ~ s_b) - s_b
|
||||||
|
|
||||||
|
s_a ~= s_b // quotient sign
|
||||||
|
u_s_a := u128(s_a)
|
||||||
|
|
||||||
|
return i128((udivmodti4(u128(an), u128(bn), nil) ~ u_s_a) - u_s_a) // negate if negative
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build freebsd, openbsd, netbsd
|
#+build freebsd, openbsd, netbsd
|
||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build darwin
|
#+build darwin
|
||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build freestanding
|
#+build freestanding
|
||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
// TODO(bill): reimplement `os.write`
|
// TODO(bill): reimplement `os.write`
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build haiku
|
#+build haiku
|
||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build js
|
#+build js
|
||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import "odin_env"
|
foreign import "odin_env"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
@@ -12,6 +12,8 @@ _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
|||||||
SYS_write :: uintptr(4)
|
SYS_write :: uintptr(4)
|
||||||
} else when ODIN_ARCH == .arm32 {
|
} else when ODIN_ARCH == .arm32 {
|
||||||
SYS_write :: uintptr(4)
|
SYS_write :: uintptr(4)
|
||||||
|
} else when ODIN_ARCH == .riscv64 {
|
||||||
|
SYS_write :: uintptr(64)
|
||||||
}
|
}
|
||||||
|
|
||||||
stderr :: 2
|
stderr :: 2
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build orca
|
#+build orca
|
||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build wasi
|
#+build wasi
|
||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import wasi "wasi_snapshot_preview1"
|
foreign import wasi "wasi_snapshot_preview1"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+build windows
|
#+build windows
|
||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import kernel32 "system:Kernel32.lib"
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+private
|
#+private
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import "system:Foundation.framework"
|
foreign import "system:Foundation.framework"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build js
|
#+build js
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
init_default_context_for_js: Context
|
init_default_context_for_js: Context
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build wasm32, wasm64p32
|
#+build wasm32, wasm64p32
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@@ -14,33 +14,57 @@ ti_uint :: struct #raw_union {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(link_name="__ashlti3", linkage="strong")
|
@(link_name="__ashlti3", linkage="strong")
|
||||||
__ashlti3 :: proc "contextless" (la, ha: u64, b_: u32) -> i128 {
|
__ashlti3 :: proc "contextless" (a: i128, b: u32) -> i128 {
|
||||||
bits_in_dword :: size_of(u32)*8
|
bits :: 64
|
||||||
b := u32(b_)
|
|
||||||
|
|
||||||
input, result: ti_int
|
input: ti_int = ---
|
||||||
input.lo, input.hi = la, ha
|
result: ti_int = ---
|
||||||
if b & bits_in_dword != 0 {
|
input.all = a
|
||||||
|
if b & bits != 0 {
|
||||||
result.lo = 0
|
result.lo = 0
|
||||||
result.hi = input.lo << (b-bits_in_dword)
|
result.hi = input.lo << (b-bits)
|
||||||
} else {
|
} else {
|
||||||
if b == 0 {
|
if b == 0 {
|
||||||
return input.all
|
return a
|
||||||
}
|
}
|
||||||
result.lo = input.lo<<b
|
result.lo = input.lo<<b
|
||||||
result.hi = (input.hi<<b) | (input.lo>>(bits_in_dword-b))
|
result.hi = (input.hi<<b) | (input.lo>>(bits-b))
|
||||||
}
|
}
|
||||||
return result.all
|
return result.all
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__ashlti3_unsigned :: proc "contextless" (a: u128, b: u32) -> u128 {
|
||||||
|
return cast(u128)__ashlti3(cast(i128)a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(link_name="__mulddi3", linkage="strong")
|
||||||
|
__mulddi3 :: proc "contextless" (a, b: u64) -> i128 {
|
||||||
|
r: ti_int
|
||||||
|
bits :: 32
|
||||||
|
|
||||||
|
mask :: ~u64(0) >> bits
|
||||||
|
r.lo = (a & mask) * (b & mask)
|
||||||
|
t := r.lo >> bits
|
||||||
|
r.lo &= mask
|
||||||
|
t += (a >> bits) * (b & mask)
|
||||||
|
r.lo += (t & mask) << bits
|
||||||
|
r.hi = t >> bits
|
||||||
|
t = r.lo >> bits
|
||||||
|
r.lo &= mask
|
||||||
|
t += (b >> bits) * (a & mask)
|
||||||
|
r.lo += (t & mask) << bits
|
||||||
|
r.hi += t >> bits
|
||||||
|
r.hi += (a >> bits) * (b >> bits)
|
||||||
|
return r.all
|
||||||
|
}
|
||||||
|
|
||||||
@(link_name="__multi3", linkage="strong")
|
@(link_name="__multi3", linkage="strong")
|
||||||
__multi3 :: proc "contextless" (la, ha, lb, hb: u64) -> i128 {
|
__multi3 :: proc "contextless" (a, b: i128) -> i128 {
|
||||||
x, y, r: ti_int
|
x, y, r: ti_int
|
||||||
|
|
||||||
x.lo, x.hi = la, ha
|
x.all = a
|
||||||
y.lo, y.hi = lb, hb
|
y.all = b
|
||||||
r.all = i128(x.lo * y.lo) // TODO this is incorrect
|
r.all = __mulddi3(x.lo, y.lo)
|
||||||
r.hi += x.hi*y.lo + x.lo*y.hi
|
r.hi += x.hi*y.lo + x.lo*y.hi
|
||||||
return r.all
|
return r.all
|
||||||
}
|
}
|
||||||
@@ -52,3 +76,22 @@ udivti3 :: proc "c" (la, ha, lb, hb: u64) -> u128 {
|
|||||||
b.lo, b.hi = lb, hb
|
b.lo, b.hi = lb, hb
|
||||||
return udivmodti4(a.all, b.all, nil)
|
return udivmodti4(a.all, b.all, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(link_name="__lshrti3", linkage="strong")
|
||||||
|
__lshrti3 :: proc "c" (a: i128, b: u32) -> i128 {
|
||||||
|
bits :: 64
|
||||||
|
|
||||||
|
input, result: ti_int
|
||||||
|
input.all = a
|
||||||
|
if b & bits != 0 {
|
||||||
|
result.hi = 0
|
||||||
|
result.lo = input.hi >> (b - bits)
|
||||||
|
} else if b == 0 {
|
||||||
|
return a
|
||||||
|
} else {
|
||||||
|
result.hi = input.hi >> b
|
||||||
|
result.lo = (input.hi << (bits - b)) | (input.lo >> b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.all
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+private
|
#+private
|
||||||
//+no-instrumentation
|
#+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
foreign import kernel32 "system:Kernel32.lib"
|
foreign import kernel32 "system:Kernel32.lib"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//+private
|
#+private
|
||||||
//+no-instrumentation
|
#+no-instrumentation
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
@require foreign import "system:int64.lib"
|
@require foreign import "system:int64.lib"
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package runtime
|
||||||
|
|
||||||
|
Thread_Local_Cleaner :: #type proc "odin" ()
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
thread_local_cleaners: [8]Thread_Local_Cleaner
|
||||||
|
|
||||||
|
// Add a procedure that will be run at the end of a thread for the purpose of
|
||||||
|
// deallocating state marked as `thread_local`.
|
||||||
|
//
|
||||||
|
// Intended to be called in an `init` procedure of a package with
|
||||||
|
// dynamically-allocated memory that is stored in `thread_local` variables.
|
||||||
|
add_thread_local_cleaner :: proc "contextless" (p: Thread_Local_Cleaner) {
|
||||||
|
for &v in thread_local_cleaners {
|
||||||
|
if v == nil {
|
||||||
|
v = p
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic_contextless("There are no more thread-local cleaner slots available.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all of the thread-local cleaner procedures.
|
||||||
|
//
|
||||||
|
// Intended to be called by the internals of a threading API at the end of a
|
||||||
|
// thread's lifetime.
|
||||||
|
run_thread_local_cleaners :: proc "odin" () {
|
||||||
|
for p in thread_local_cleaners {
|
||||||
|
if p == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build wasm32, wasm64p32
|
#+build wasm32, wasm64p32
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
|||||||
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
||||||
set CURR_DATE_TIME=%%j
|
set CURR_DATE_TIME=%%j
|
||||||
)
|
)
|
||||||
|
|
||||||
set curr_year=%CURR_DATE_TIME:~0,4%
|
set curr_year=%CURR_DATE_TIME:~0,4%
|
||||||
set curr_month=%CURR_DATE_TIME:~4,2%
|
set curr_month=%CURR_DATE_TIME:~4,2%
|
||||||
|
set curr_day=%CURR_DATE_TIME:~6,2%
|
||||||
|
|
||||||
:: Make sure this is a decent name and not generic
|
:: Make sure this is a decent name and not generic
|
||||||
set exe_name=odin.exe
|
set exe_name=odin.exe
|
||||||
@@ -45,7 +45,19 @@ if "%2" == "1" (
|
|||||||
set nightly=0
|
set nightly=0
|
||||||
)
|
)
|
||||||
|
|
||||||
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
if %release_mode% equ 0 (
|
||||||
|
set V1=%curr_year%
|
||||||
|
set V2=%curr_month%
|
||||||
|
set V3=%curr_day%
|
||||||
|
) else (
|
||||||
|
set V1=%curr_year%
|
||||||
|
set V2=%curr_month%
|
||||||
|
set V3=0
|
||||||
|
)
|
||||||
|
set V4=0
|
||||||
|
set odin_version_full="%V1%.%V2%.%V3%.%V4%"
|
||||||
|
set odin_version_raw="dev-%V1%-%V2%"
|
||||||
|
|
||||||
|
|
||||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||||
rem Parse source code as utf-8 even on shift-jis and other codepages
|
rem Parse source code as utf-8 even on shift-jis and other codepages
|
||||||
@@ -53,18 +65,29 @@ rem See https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-a
|
|||||||
set compiler_flags= %compiler_flags% /utf-8
|
set compiler_flags= %compiler_flags% /utf-8
|
||||||
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
||||||
|
|
||||||
|
rem fileversion is defined as {Major,Minor,Build,Private: u16} so a bit limited
|
||||||
|
set rc_flags=-nologo ^
|
||||||
|
-DV1=%V1% -DV2=%V2% -DV3=%V3% -DV4=%V4% ^
|
||||||
|
-DVF=%odin_version_full% -DNIGHTLY=%nightly%
|
||||||
|
|
||||||
if not exist .git\ goto skip_git_hash
|
if not exist .git\ goto skip_git_hash
|
||||||
for /f "tokens=1,2" %%i IN ('git show "--pretty=%%cd %%h" "--date=format:%%Y-%%m" --no-patch --no-notes HEAD') do (
|
for /f "tokens=1,2" %%i IN ('git show "--pretty=%%cd %%h" "--date=format:%%Y-%%m" --no-patch --no-notes HEAD') do (
|
||||||
set odin_version_raw=dev-%%i
|
set odin_version_raw=dev-%%i
|
||||||
set GIT_SHA=%%j
|
set GIT_SHA=%%j
|
||||||
)
|
)
|
||||||
if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
|
if %ERRORLEVEL% equ 0 (
|
||||||
|
set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
|
||||||
|
set rc_flags=%rc_flags% -DGIT_SHA=%GIT_SHA% -DVP=%odin_version_raw%:%GIT_SHA%
|
||||||
|
) else (
|
||||||
|
set rc_flags=%rc_flags% -DVP=%odin_version_raw%
|
||||||
|
)
|
||||||
:skip_git_hash
|
:skip_git_hash
|
||||||
|
|
||||||
if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
|
if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
|
||||||
|
|
||||||
if %release_mode% EQU 0 ( rem Debug
|
if %release_mode% EQU 0 ( rem Debug
|
||||||
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
||||||
|
set rc_flags=%rc_flags% -D_DEBUG
|
||||||
) else ( rem Release
|
) else ( rem Release
|
||||||
set compiler_flags=%compiler_flags% -O2 -MT -Z7
|
set compiler_flags=%compiler_flags% -O2 -MT -Z7
|
||||||
set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
|
set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
|
||||||
@@ -82,6 +105,8 @@ set libs= ^
|
|||||||
kernel32.lib ^
|
kernel32.lib ^
|
||||||
Synchronization.lib ^
|
Synchronization.lib ^
|
||||||
bin\llvm\windows\LLVM-C.lib
|
bin\llvm\windows\LLVM-C.lib
|
||||||
|
set odin_res=misc\odin.res
|
||||||
|
set odin_rc=misc\odin.rc
|
||||||
|
|
||||||
rem DO NOT TOUCH!
|
rem DO NOT TOUCH!
|
||||||
rem THIS TILDE STUFF IS FOR DEVELOPMENT ONLY!
|
rem THIS TILDE STUFF IS FOR DEVELOPMENT ONLY!
|
||||||
@@ -93,7 +118,7 @@ if %tilde_backend% EQU 1 (
|
|||||||
rem DO NOT TOUCH!
|
rem DO NOT TOUCH!
|
||||||
|
|
||||||
|
|
||||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
set linker_flags= -incremental:no -opt:ref -subsystem:console -MANIFEST:EMBED
|
||||||
|
|
||||||
if %release_mode% EQU 0 ( rem Debug
|
if %release_mode% EQU 0 ( rem Debug
|
||||||
set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
|
set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
|
||||||
@@ -102,19 +127,24 @@ if %release_mode% EQU 0 ( rem Debug
|
|||||||
)
|
)
|
||||||
|
|
||||||
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines%
|
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines%
|
||||||
set linker_settings=%libs% %linker_flags%
|
set linker_settings=%libs% %odin_res% %linker_flags%
|
||||||
|
|
||||||
del *.pdb > NUL 2> NUL
|
del *.pdb > NUL 2> NUL
|
||||||
del *.ilk > NUL 2> NUL
|
del *.ilk > NUL 2> NUL
|
||||||
|
|
||||||
|
rc %rc_flags% %odin_rc%
|
||||||
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
|
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
|
||||||
|
mt -nologo -inputresource:%exe_name%;#1 -manifest misc\odin.manifest -outputresource:%exe_name%;#1 -validate_manifest -identity:"odin, processorArchitecture=amd64, version=%odin_version_full%, type=win32"
|
||||||
if %errorlevel% neq 0 goto end_of_build
|
if %errorlevel% neq 0 goto end_of_build
|
||||||
|
|
||||||
call build_vendor.bat
|
call build_vendor.bat
|
||||||
if %errorlevel% neq 0 goto end_of_build
|
if %errorlevel% neq 0 goto end_of_build
|
||||||
|
|
||||||
rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
|
rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
|
||||||
if %release_mode% EQU 0 odin run examples/demo -vet -strict-style -- Hellope World
|
if %release_mode% EQU 0 odin run examples/demo -vet -strict-style -resource:examples/demo/demo.rc -- Hellope World
|
||||||
|
|
||||||
|
rem Many non-compiler devs seem to run debug build but don't realize.
|
||||||
|
if %release_mode% EQU 0 echo: & echo Debug compiler built. Note: run "build.bat release" if you want a faster, release mode compiler.
|
||||||
|
|
||||||
del *.obj > NUL 2> NUL
|
del *.obj > NUL 2> NUL
|
||||||
|
|
||||||
|
|||||||
+15
-2
@@ -23,6 +23,14 @@ error() {
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Brew advises people not to add llvm to their $PATH, so try and use brew to find it.
|
||||||
|
if [ -z "$LLVM_CONFIG" ] && [ -n "$(command -v brew)" ]; then
|
||||||
|
if [ -n "$(command -v $(brew --prefix llvm@18)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@18)/bin/llvm-config"
|
||||||
|
elif [ -n "$(command -v $(brew --prefix llvm@17)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@17)/bin/llvm-config"
|
||||||
|
elif [ -n "$(command -v $(brew --prefix llvm@14)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@14)/bin/llvm-config"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$LLVM_CONFIG" ]; then
|
if [ -z "$LLVM_CONFIG" ]; then
|
||||||
# darwin, linux, openbsd
|
# darwin, linux, openbsd
|
||||||
if [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
|
if [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
|
||||||
@@ -95,7 +103,7 @@ Linux)
|
|||||||
LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
|
LDFLAGS="$LDFLAGS -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
|
||||||
# Copy libLLVM*.so into current directory for linking
|
# Copy libLLVM*.so into current directory for linking
|
||||||
# NOTE: This is needed by the Linux release pipeline!
|
# NOTE: This is needed by the Linux release pipeline!
|
||||||
cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
|
# cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
|
||||||
LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
|
LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
|
||||||
;;
|
;;
|
||||||
OpenBSD)
|
OpenBSD)
|
||||||
@@ -144,12 +152,17 @@ build_odin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
run_demo() {
|
run_demo() {
|
||||||
./odin run examples/demo -vet -strict-style -- Hellope World
|
if [ $# -eq 0 ] || [ "$1" = "debug" ]; then
|
||||||
|
./odin run examples/demo -vet -strict-style -- Hellope World
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
if [ $# -eq 0 ]; then
|
||||||
build_odin debug
|
build_odin debug
|
||||||
run_demo
|
run_demo
|
||||||
|
|
||||||
|
: ${PROGRAM:=$0}
|
||||||
|
printf "\nDebug compiler built. Note: run \"$PROGRAM release\" or \"$PROGRAM release-native\" if you want a faster, release mode compiler.\n"
|
||||||
elif [ $# -eq 1 ]; then
|
elif [ $# -eq 1 ]; then
|
||||||
case $1 in
|
case $1 in
|
||||||
report)
|
report)
|
||||||
|
|||||||
Executable
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# Intended for use in Alpine containers, see the "nightly" Github action for a list of dependencies
|
||||||
|
|
||||||
|
CXX="clang++-18"
|
||||||
|
LLVM_CONFIG="llvm-config-18"
|
||||||
|
|
||||||
|
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
||||||
|
|
||||||
|
CPPFLAGS="-DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
|
||||||
|
CXXFLAGS="-std=c++14 $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||||
|
|
||||||
|
LDFLAGS="-static -lm -lzstd -lz -lffi -pthread -ldl -fuse-ld=mold"
|
||||||
|
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --link-static --ldflags --libs --system-libs --libfiles)"
|
||||||
|
LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
|
||||||
|
|
||||||
|
EXTRAFLAGS="-DNIGHTLY -O3"
|
||||||
|
|
||||||
|
set -x
|
||||||
|
$CXX src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CPPFLAGS $CXXFLAGS $EXTRAFLAGS $LDFLAGS -o odin
|
||||||
+24
-19
@@ -2,7 +2,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from zipfile import ZipFile, ZIP_DEFLATED
|
from zipfile import ZipFile, ZIP_DEFLATED
|
||||||
from b2sdk.v2 import InMemoryAccountInfo, B2Api
|
from b2sdk.v2 import InMemoryAccountInfo, B2Api
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
import json
|
import json
|
||||||
|
|
||||||
UPLOAD_FOLDER = "nightly/"
|
UPLOAD_FOLDER = "nightly/"
|
||||||
@@ -22,7 +22,7 @@ def auth() -> bool:
|
|||||||
pass # Not yet authenticated
|
pass # Not yet authenticated
|
||||||
|
|
||||||
err = b2_api.authorize_account("production", application_key_id, application_key)
|
err = b2_api.authorize_account("production", application_key_id, application_key)
|
||||||
return err == None
|
return err is None
|
||||||
|
|
||||||
def get_bucket():
|
def get_bucket():
|
||||||
if not auth(): sys.exit(1)
|
if not auth(): sys.exit(1)
|
||||||
@@ -32,30 +32,35 @@ def remove_prefix(text: str, prefix: str) -> str:
|
|||||||
return text[text.startswith(prefix) and len(prefix):]
|
return text[text.startswith(prefix) and len(prefix):]
|
||||||
|
|
||||||
def create_and_upload_artifact_zip(platform: str, artifact: str) -> int:
|
def create_and_upload_artifact_zip(platform: str, artifact: str) -> int:
|
||||||
now = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
|
now = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
destination_zip_name = "odin-{}-nightly+{}.zip".format(platform, now.strftime("%Y-%m-%d"))
|
|
||||||
|
|
||||||
source_zip_name = artifact
|
source_archive: str
|
||||||
if not artifact.endswith(".zip"):
|
destination_name = f'odin-{platform}-nightly+{now.strftime("%Y-%m-%d")}'
|
||||||
print(f"Creating archive {destination_zip_name} from {artifact} and uploading to {bucket_name}")
|
|
||||||
|
|
||||||
source_zip_name = destination_zip_name
|
if platform.startswith("linux") or platform.startswith("macos"):
|
||||||
with ZipFile(source_zip_name, mode='w', compression=ZIP_DEFLATED, compresslevel=9) as z:
|
destination_name += ".tar.gz"
|
||||||
|
source_archive = artifact
|
||||||
|
else:
|
||||||
|
destination_name += ".zip"
|
||||||
|
source_archive = destination_name
|
||||||
|
|
||||||
|
print(f"Creating archive {destination_name} from {artifact} and uploading to {bucket_name}")
|
||||||
|
with ZipFile(source_archive, mode='w', compression=ZIP_DEFLATED, compresslevel=9) as z:
|
||||||
for root, directory, filenames in os.walk(artifact):
|
for root, directory, filenames in os.walk(artifact):
|
||||||
for file in filenames:
|
for file in filenames:
|
||||||
file_path = os.path.join(root, file)
|
file_path = os.path.join(root, file)
|
||||||
zip_path = os.path.join("dist", os.path.relpath(file_path, artifact))
|
zip_path = os.path.join("dist", os.path.relpath(file_path, artifact))
|
||||||
z.write(file_path, zip_path)
|
z.write(file_path, zip_path)
|
||||||
|
|
||||||
if not os.path.exists(source_zip_name):
|
if not os.path.exists(source_archive):
|
||||||
print(f"Error: Newly created ZIP archive {source_zip_name} not found.")
|
print(f"Error: archive {source_archive} not found.")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
print("Uploading {} to {}".format(source_zip_name, UPLOAD_FOLDER + destination_zip_name))
|
print("Uploading {} to {}".format(source_archive, UPLOAD_FOLDER + destination_name))
|
||||||
bucket = get_bucket()
|
bucket = get_bucket()
|
||||||
res = bucket.upload_local_file(
|
res = bucket.upload_local_file(
|
||||||
source_zip_name, # Local file to upload
|
source_archive, # Local file to upload
|
||||||
"nightly/" + destination_zip_name, # B2 destination path
|
"nightly/" + destination_name, # B2 destination path
|
||||||
)
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@@ -65,8 +70,8 @@ def prune_artifacts():
|
|||||||
bucket = get_bucket()
|
bucket = get_bucket()
|
||||||
for file, _ in bucket.ls(UPLOAD_FOLDER, latest_only=False):
|
for file, _ in bucket.ls(UPLOAD_FOLDER, latest_only=False):
|
||||||
# Timestamp is in milliseconds
|
# Timestamp is in milliseconds
|
||||||
date = datetime.fromtimestamp(file.upload_timestamp / 1_000.0).replace(hour=0, minute=0, second=0, microsecond=0)
|
date = datetime.fromtimestamp(file.upload_timestamp / 1_000.0, tz=timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
now = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
|
now = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||||
delta = now - date
|
delta = now - date
|
||||||
|
|
||||||
if delta.days > int(days_to_keep):
|
if delta.days > int(days_to_keep):
|
||||||
@@ -100,7 +105,7 @@ def update_nightly_json():
|
|||||||
'sizeInBytes': size,
|
'sizeInBytes': size,
|
||||||
})
|
})
|
||||||
|
|
||||||
now = datetime.utcnow().isoformat()
|
now = datetime.now(timezone.utc).isoformat()
|
||||||
|
|
||||||
nightly = json.dumps({
|
nightly = json.dumps({
|
||||||
'last_updated' : now,
|
'last_updated' : now,
|
||||||
@@ -137,4 +142,4 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
elif command == "json":
|
elif command == "json":
|
||||||
res = update_nightly_json()
|
res = update_nightly_json()
|
||||||
sys.exit(res)
|
sys.exit(res)
|
||||||
|
|||||||
+33
-3
@@ -144,6 +144,9 @@ buffer_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int, loc := #caller_location) -> (n: int, err: io.Error) {
|
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int, loc := #caller_location) -> (n: int, err: io.Error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
b.last_read = .Invalid
|
b.last_read = .Invalid
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
err = .Invalid_Offset
|
err = .Invalid_Offset
|
||||||
@@ -246,10 +249,13 @@ buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.
|
|||||||
}
|
}
|
||||||
|
|
||||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
b.last_read = .Invalid
|
b.last_read = .Invalid
|
||||||
|
|
||||||
if uint(offset) >= len(b.buf) {
|
if uint(offset) >= len(b.buf) {
|
||||||
err = .Invalid_Offset
|
err = .EOF
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n = copy(p, b.buf[offset:])
|
n = copy(p, b.buf[offset:])
|
||||||
@@ -310,6 +316,27 @@ buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buffer_seek :: proc(b: ^Buffer, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||||
|
abs: i64
|
||||||
|
switch whence {
|
||||||
|
case .Start:
|
||||||
|
abs = offset
|
||||||
|
case .Current:
|
||||||
|
abs = i64(b.off) + offset
|
||||||
|
case .End:
|
||||||
|
abs = i64(len(b.buf)) + offset
|
||||||
|
case:
|
||||||
|
return 0, .Invalid_Whence
|
||||||
|
}
|
||||||
|
|
||||||
|
abs_int := int(abs)
|
||||||
|
if abs_int < 0 {
|
||||||
|
return 0, .Invalid_Offset
|
||||||
|
}
|
||||||
|
b.last_read = .Invalid
|
||||||
|
b.off = abs_int
|
||||||
|
return abs, nil
|
||||||
|
}
|
||||||
|
|
||||||
buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
|
buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
|
||||||
i := index_byte(b.buf[b.off:], delim)
|
i := index_byte(b.buf[b.off:], delim)
|
||||||
@@ -395,14 +422,17 @@ _buffer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offse
|
|||||||
return io._i64_err(buffer_write(b, p))
|
return io._i64_err(buffer_write(b, p))
|
||||||
case .Write_At:
|
case .Write_At:
|
||||||
return io._i64_err(buffer_write_at(b, p, int(offset)))
|
return io._i64_err(buffer_write_at(b, p, int(offset)))
|
||||||
|
case .Seek:
|
||||||
|
n, err = buffer_seek(b, offset, whence)
|
||||||
|
return
|
||||||
case .Size:
|
case .Size:
|
||||||
n = i64(buffer_capacity(b))
|
n = i64(buffer_length(b))
|
||||||
return
|
return
|
||||||
case .Destroy:
|
case .Destroy:
|
||||||
buffer_destroy(b)
|
buffer_destroy(b)
|
||||||
return
|
return
|
||||||
case .Query:
|
case .Query:
|
||||||
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Size, .Destroy})
|
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Destroy, .Query})
|
||||||
}
|
}
|
||||||
return 0, .Empty
|
return 0, .Empty
|
||||||
}
|
}
|
||||||
|
|||||||
+284
-6
@@ -1,9 +1,38 @@
|
|||||||
package bytes
|
package bytes
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
import "core:simd"
|
||||||
import "core:unicode"
|
import "core:unicode"
|
||||||
import "core:unicode/utf8"
|
import "core:unicode/utf8"
|
||||||
|
|
||||||
|
when ODIN_ARCH == .amd64 && intrinsics.has_target_feature("avx2") {
|
||||||
|
@(private)
|
||||||
|
SCANNER_INDICES_256 : simd.u8x32 : {
|
||||||
|
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, 26, 27, 28, 29, 30, 31,
|
||||||
|
}
|
||||||
|
@(private)
|
||||||
|
SCANNER_SENTINEL_MAX_256: simd.u8x32 : u8(0x00)
|
||||||
|
@(private)
|
||||||
|
SCANNER_SENTINEL_MIN_256: simd.u8x32 : u8(0xff)
|
||||||
|
@(private)
|
||||||
|
SIMD_REG_SIZE_256 :: 32
|
||||||
|
}
|
||||||
|
@(private)
|
||||||
|
SCANNER_INDICES_128 : simd.u8x16 : {
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7,
|
||||||
|
8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
}
|
||||||
|
@(private)
|
||||||
|
SCANNER_SENTINEL_MAX_128: simd.u8x16 : u8(0x00)
|
||||||
|
@(private)
|
||||||
|
SCANNER_SENTINEL_MIN_128: simd.u8x16 : u8(0xff)
|
||||||
|
@(private)
|
||||||
|
SIMD_REG_SIZE_128 :: 16
|
||||||
|
|
||||||
clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||||
c := make([]byte, len(s), allocator, loc)
|
c := make([]byte, len(s), allocator, loc)
|
||||||
copy(c, s)
|
copy(c, s)
|
||||||
@@ -293,28 +322,277 @@ split_after_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
|
|||||||
return _split_iterator(s, sep, len(sep))
|
return _split_iterator(s, sep, len(sep))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Scan a slice of bytes for a specific byte.
|
||||||
|
|
||||||
index_byte :: proc(s: []byte, c: byte) -> int {
|
This procedure safely handles slices of any length, including empty slices.
|
||||||
for i := 0; i < len(s); i += 1 {
|
|
||||||
|
Inputs:
|
||||||
|
- data: A slice of bytes.
|
||||||
|
- c: The byte to search for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- index: The index of the byte `c`, or -1 if it was not found.
|
||||||
|
*/
|
||||||
|
index_byte :: proc "contextless" (s: []byte, c: byte) -> (index: int) #no_bounds_check {
|
||||||
|
i, l := 0, len(s)
|
||||||
|
|
||||||
|
// Guard against small strings. On modern systems, it is ALWAYS
|
||||||
|
// worth vectorizing assuming there is a hardware vector unit, and
|
||||||
|
// the data size is large enough.
|
||||||
|
if l < SIMD_REG_SIZE_128 {
|
||||||
|
for /**/; i < l; i += 1 {
|
||||||
|
if s[i] == c {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
c_vec: simd.u8x16 = c
|
||||||
|
when !simd.IS_EMULATED {
|
||||||
|
// Note: While this is something that could also logically take
|
||||||
|
// advantage of AVX512, the various downclocking and power
|
||||||
|
// consumption related woes make premature to have a dedicated
|
||||||
|
// code path.
|
||||||
|
when ODIN_ARCH == .amd64 && intrinsics.has_target_feature("avx2") {
|
||||||
|
c_vec_256: simd.u8x32 = c
|
||||||
|
|
||||||
|
s_vecs: [4]simd.u8x32 = ---
|
||||||
|
c_vecs: [4]simd.u8x32 = ---
|
||||||
|
m_vec: [4]u8 = ---
|
||||||
|
|
||||||
|
// Scan 128-byte chunks, using 256-bit SIMD.
|
||||||
|
for nr_blocks := l / (4 * SIMD_REG_SIZE_256); nr_blocks > 0; nr_blocks -= 1 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
s_vecs[j] = intrinsics.unaligned_load(cast(^simd.u8x32)raw_data(s[i+j*SIMD_REG_SIZE_256:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec_256)
|
||||||
|
m_vec[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vec[0] | m_vec[1] | m_vec[2] | m_vec[3] > 0 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
if m_vec[j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[j], SCANNER_INDICES_256, SCANNER_SENTINEL_MIN_256)
|
||||||
|
off := simd.reduce_min(sel)
|
||||||
|
return i + j * SIMD_REG_SIZE_256 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 4 * SIMD_REG_SIZE_256
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan 64-byte chunks, using 256-bit SIMD.
|
||||||
|
for nr_blocks := (l - i) / (2 * SIMD_REG_SIZE_256); nr_blocks > 0; nr_blocks -= 1 {
|
||||||
|
#unroll for j in 0..<2 {
|
||||||
|
s_vecs[j] = intrinsics.unaligned_load(cast(^simd.u8x32)raw_data(s[i+j*SIMD_REG_SIZE_256:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec_256)
|
||||||
|
m_vec[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vec[0] | m_vec[1] > 0 {
|
||||||
|
#unroll for j in 0..<2 {
|
||||||
|
if m_vec[j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[j], SCANNER_INDICES_256, SCANNER_SENTINEL_MIN_256)
|
||||||
|
off := simd.reduce_min(sel)
|
||||||
|
return i + j * SIMD_REG_SIZE_256 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 2 * SIMD_REG_SIZE_256
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_vecs: [4]simd.u8x16 = ---
|
||||||
|
c_vecs: [4]simd.u8x16 = ---
|
||||||
|
m_vecs: [4]u8 = ---
|
||||||
|
|
||||||
|
// Scan 64-byte chunks, using 128-bit SIMD.
|
||||||
|
for nr_blocks := l / (4 * SIMD_REG_SIZE_128); nr_blocks > 0; nr_blocks -= 1 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
s_vecs[j]= intrinsics.unaligned_load(cast(^simd.u8x16)raw_data(s[i+j*SIMD_REG_SIZE_128:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec)
|
||||||
|
m_vecs[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vecs[0] | m_vecs[1] | m_vecs[2] | m_vecs[3] > 0 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
if m_vecs[j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[j], SCANNER_INDICES_128, SCANNER_SENTINEL_MIN_128)
|
||||||
|
off := simd.reduce_min(sel)
|
||||||
|
return i + j * SIMD_REG_SIZE_128 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 4 * SIMD_REG_SIZE_128
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan the remaining SIMD register sized chunks.
|
||||||
|
//
|
||||||
|
// Apparently LLVM does ok with 128-bit SWAR, so this path is also taken
|
||||||
|
// on potato targets. Scanning more at a time when LLVM is emulating SIMD
|
||||||
|
// likely does not buy much, as all that does is increase GP register
|
||||||
|
// pressure.
|
||||||
|
for nr_blocks := (l - i) / SIMD_REG_SIZE_128; nr_blocks > 0; nr_blocks -= 1 {
|
||||||
|
s0 := intrinsics.unaligned_load(cast(^simd.u8x16)raw_data(s[i:]))
|
||||||
|
c0 := simd.lanes_eq(s0, c_vec)
|
||||||
|
if simd.reduce_or(c0) > 0 {
|
||||||
|
sel := simd.select(c0, SCANNER_INDICES_128, SCANNER_SENTINEL_MIN_128)
|
||||||
|
off := simd.reduce_min(sel)
|
||||||
|
return i + int(off)
|
||||||
|
}
|
||||||
|
|
||||||
|
i += SIMD_REG_SIZE_128
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan serially for the remainder.
|
||||||
|
for /**/; i < l; i += 1 {
|
||||||
if s[i] == c {
|
if s[i] == c {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns -1 if c is not present
|
/*
|
||||||
last_index_byte :: proc(s: []byte, c: byte) -> int {
|
Scan a slice of bytes for a specific byte, starting from the end and working
|
||||||
for i := len(s)-1; i >= 0; i -= 1 {
|
backwards to the start.
|
||||||
|
|
||||||
|
This procedure safely handles slices of any length, including empty slices.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- data: A slice of bytes.
|
||||||
|
- c: The byte to search for.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- index: The index of the byte `c`, or -1 if it was not found.
|
||||||
|
*/
|
||||||
|
last_index_byte :: proc "contextless" (s: []byte, c: byte) -> int #no_bounds_check {
|
||||||
|
i := len(s)
|
||||||
|
|
||||||
|
// Guard against small strings. On modern systems, it is ALWAYS
|
||||||
|
// worth vectorizing assuming there is a hardware vector unit, and
|
||||||
|
// the data size is large enough.
|
||||||
|
if i < SIMD_REG_SIZE_128 {
|
||||||
|
#reverse for ch, j in s {
|
||||||
|
if ch == c {
|
||||||
|
return j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
c_vec: simd.u8x16 = c
|
||||||
|
when !simd.IS_EMULATED {
|
||||||
|
// Note: While this is something that could also logically take
|
||||||
|
// advantage of AVX512, the various downclocking and power
|
||||||
|
// consumption related woes make premature to have a dedicated
|
||||||
|
// code path.
|
||||||
|
when ODIN_ARCH == .amd64 && intrinsics.has_target_feature("avx2") {
|
||||||
|
c_vec_256: simd.u8x32 = c
|
||||||
|
|
||||||
|
s_vecs: [4]simd.u8x32 = ---
|
||||||
|
c_vecs: [4]simd.u8x32 = ---
|
||||||
|
m_vec: [4]u8 = ---
|
||||||
|
|
||||||
|
// Scan 128-byte chunks, using 256-bit SIMD.
|
||||||
|
for i >= 4 * SIMD_REG_SIZE_256 {
|
||||||
|
i -= 4 * SIMD_REG_SIZE_256
|
||||||
|
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
s_vecs[j] = intrinsics.unaligned_load(cast(^simd.u8x32)raw_data(s[i+j*SIMD_REG_SIZE_256:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec_256)
|
||||||
|
m_vec[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vec[0] | m_vec[1] | m_vec[2] | m_vec[3] > 0 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
if m_vec[3-j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[3-j], SCANNER_INDICES_256, SCANNER_SENTINEL_MAX_256)
|
||||||
|
off := simd.reduce_max(sel)
|
||||||
|
return i + (3-j) * SIMD_REG_SIZE_256 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan 64-byte chunks, using 256-bit SIMD.
|
||||||
|
for i >= 2 * SIMD_REG_SIZE_256 {
|
||||||
|
i -= 2 * SIMD_REG_SIZE_256
|
||||||
|
|
||||||
|
#unroll for j in 0..<2 {
|
||||||
|
s_vecs[j] = intrinsics.unaligned_load(cast(^simd.u8x32)raw_data(s[i+j*SIMD_REG_SIZE_256:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec_256)
|
||||||
|
m_vec[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vec[0] | m_vec[1] > 0 {
|
||||||
|
#unroll for j in 0..<2 {
|
||||||
|
if m_vec[1-j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[1-j], SCANNER_INDICES_256, SCANNER_SENTINEL_MAX_256)
|
||||||
|
off := simd.reduce_max(sel)
|
||||||
|
return i + (1-j) * SIMD_REG_SIZE_256 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_vecs: [4]simd.u8x16 = ---
|
||||||
|
c_vecs: [4]simd.u8x16 = ---
|
||||||
|
m_vecs: [4]u8 = ---
|
||||||
|
|
||||||
|
// Scan 64-byte chunks, using 128-bit SIMD.
|
||||||
|
for i >= 4 * SIMD_REG_SIZE_128 {
|
||||||
|
i -= 4 * SIMD_REG_SIZE_128
|
||||||
|
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
s_vecs[j] = intrinsics.unaligned_load(cast(^simd.u8x16)raw_data(s[i+j*SIMD_REG_SIZE_128:]))
|
||||||
|
c_vecs[j] = simd.lanes_eq(s_vecs[j], c_vec)
|
||||||
|
m_vecs[j] = simd.reduce_or(c_vecs[j])
|
||||||
|
}
|
||||||
|
if m_vecs[0] | m_vecs[1] | m_vecs[2] | m_vecs[3] > 0 {
|
||||||
|
#unroll for j in 0..<4 {
|
||||||
|
if m_vecs[3-j] > 0 {
|
||||||
|
sel := simd.select(c_vecs[3-j], SCANNER_INDICES_128, SCANNER_SENTINEL_MAX_128)
|
||||||
|
off := simd.reduce_max(sel)
|
||||||
|
return i + (3-j) * SIMD_REG_SIZE_128 + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan the remaining SIMD register sized chunks.
|
||||||
|
//
|
||||||
|
// Apparently LLVM does ok with 128-bit SWAR, so this path is also taken
|
||||||
|
// on potato targets. Scanning more at a time when LLVM is emulating SIMD
|
||||||
|
// likely does not buy much, as all that does is increase GP register
|
||||||
|
// pressure.
|
||||||
|
for i >= SIMD_REG_SIZE_128 {
|
||||||
|
i -= SIMD_REG_SIZE_128
|
||||||
|
|
||||||
|
s0 := intrinsics.unaligned_load(cast(^simd.u8x16)raw_data(s[i:]))
|
||||||
|
c0 := simd.lanes_eq(s0, c_vec)
|
||||||
|
if simd.reduce_or(c0) > 0 {
|
||||||
|
sel := simd.select(c0, SCANNER_INDICES_128, SCANNER_SENTINEL_MAX_128)
|
||||||
|
off := simd.reduce_max(sel)
|
||||||
|
return i + int(off)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan serially for the remainder.
|
||||||
|
for i > 0 {
|
||||||
|
i -= 1
|
||||||
if s[i] == c {
|
if s[i] == c {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@private PRIME_RABIN_KARP :: 16777619
|
@private PRIME_RABIN_KARP :: 16777619
|
||||||
|
|
||||||
index :: proc(s, substr: []byte) -> int {
|
index :: proc(s, substr: []byte) -> int {
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ Reader :: struct {
|
|||||||
prev_rune: int, // previous reading index of rune or < 0
|
prev_rune: int, // previous reading index of rune or < 0
|
||||||
}
|
}
|
||||||
|
|
||||||
reader_init :: proc(r: ^Reader, s: []byte) {
|
reader_init :: proc(r: ^Reader, s: []byte) -> io.Stream {
|
||||||
r.s = s
|
r.s = s
|
||||||
r.i = 0
|
r.i = 0
|
||||||
r.prev_rune = -1
|
r.prev_rune = -1
|
||||||
|
return reader_to_stream(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
||||||
@@ -33,6 +34,9 @@ reader_size :: proc(r: ^Reader) -> i64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
if r.i >= i64(len(r.s)) {
|
if r.i >= i64(len(r.s)) {
|
||||||
return 0, .EOF
|
return 0, .EOF
|
||||||
}
|
}
|
||||||
@@ -42,6 +46,9 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
if off < 0 {
|
if off < 0 {
|
||||||
return 0, .Invalid_Offset
|
return 0, .Invalid_Offset
|
||||||
}
|
}
|
||||||
@@ -97,7 +104,6 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||||
r.prev_rune = -1
|
|
||||||
abs: i64
|
abs: i64
|
||||||
switch whence {
|
switch whence {
|
||||||
case .Start:
|
case .Start:
|
||||||
@@ -114,6 +120,7 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E
|
|||||||
return 0, .Invalid_Offset
|
return 0, .Invalid_Offset
|
||||||
}
|
}
|
||||||
r.i = abs
|
r.i = abs
|
||||||
|
r.prev_rune = -1
|
||||||
return abs, nil
|
return abs, nil
|
||||||
}
|
}
|
||||||
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||||
|
|||||||
@@ -47,8 +47,8 @@ foreign libc {
|
|||||||
clogf :: proc(z: complex_float) -> complex_float ---
|
clogf :: proc(z: complex_float) -> complex_float ---
|
||||||
|
|
||||||
// 7.3.8 Power and absolute-value functions
|
// 7.3.8 Power and absolute-value functions
|
||||||
cabs :: proc(z: complex_double) -> complex_double ---
|
cabs :: proc(z: complex_double) -> double ---
|
||||||
cabsf :: proc(z: complex_float) -> complex_float ---
|
cabsf :: proc(z: complex_float) -> float ---
|
||||||
cpow :: proc(x, y: complex_double) -> complex_double ---
|
cpow :: proc(x, y: complex_double) -> complex_double ---
|
||||||
cpowf :: proc(x, y: complex_float) -> complex_float ---
|
cpowf :: proc(x, y: complex_float) -> complex_float ---
|
||||||
csqrt :: proc(z: complex_double) -> complex_double ---
|
csqrt :: proc(z: complex_double) -> complex_double ---
|
||||||
|
|||||||
@@ -98,10 +98,18 @@ when ODIN_OS == .Haiku {
|
|||||||
ERANGE :: B_POSIX_ERROR_BASE + 17
|
ERANGE :: B_POSIX_ERROR_BASE + 17
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .JS {
|
||||||
|
_ :: libc
|
||||||
|
_get_errno :: proc "c" () -> ^int {
|
||||||
|
@(static) errno: int
|
||||||
|
return &errno
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Odin has no way to make an identifier "errno" behave as a function call to
|
// Odin has no way to make an identifier "errno" behave as a function call to
|
||||||
// read the value, or to produce an lvalue such that you can assign a different
|
// read the value, or to produce an lvalue such that you can assign a different
|
||||||
// error value to errno. To work around this, just expose it as a function like
|
// error value to errno. To work around this, just expose it as a function like
|
||||||
// it actually is.
|
// it actually is.
|
||||||
errno :: #force_inline proc() -> ^int {
|
errno :: #force_inline proc "contextless" () -> ^int {
|
||||||
return _get_errno()
|
return _get_errno()
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-7
@@ -32,24 +32,21 @@ when ODIN_OS == .Windows {
|
|||||||
// the RDX register will contain zero and correctly set the flag to disable
|
// the RDX register will contain zero and correctly set the flag to disable
|
||||||
// stack unwinding.
|
// stack unwinding.
|
||||||
@(link_name="_setjmp")
|
@(link_name="_setjmp")
|
||||||
setjmp :: proc(env: ^jmp_buf, hack: rawptr = nil) -> int ---
|
setjmp :: proc(env: ^jmp_buf, hack: rawptr = nil) -> int ---
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.13.1 Save calling environment
|
// 7.13.1 Save calling environment
|
||||||
//
|
@(link_name=LSETJMP)
|
||||||
// NOTE(dweiler): C11 requires setjmp be a macro, which means it won't
|
setjmp :: proc(env: ^jmp_buf) -> int ---
|
||||||
// necessarily export a symbol named setjmp but rather _setjmp in the case
|
|
||||||
// of musl, glibc, BSD libc, and msvcrt.
|
|
||||||
@(link_name="_setjmp")
|
|
||||||
setjmp :: proc(env: ^jmp_buf) -> int ---
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.13.2 Restore calling environment
|
// 7.13.2 Restore calling environment
|
||||||
|
@(link_name=LLONGJMP)
|
||||||
longjmp :: proc(env: ^jmp_buf, val: int) -> ! ---
|
longjmp :: proc(env: ^jmp_buf, val: int) -> ! ---
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,3 +61,11 @@ foreign libc {
|
|||||||
// The choice of 4096 bytes for storage of this type is more than enough on all
|
// The choice of 4096 bytes for storage of this type is more than enough on all
|
||||||
// relevant platforms.
|
// relevant platforms.
|
||||||
jmp_buf :: struct #align(16) { _: [4096]char, }
|
jmp_buf :: struct #align(16) { _: [4096]char, }
|
||||||
|
|
||||||
|
when ODIN_OS == .NetBSD {
|
||||||
|
@(private) LSETJMP :: "__setjmp14"
|
||||||
|
@(private) LLONGJMP :: "__longjmp14"
|
||||||
|
} else {
|
||||||
|
@(private) LSETJMP :: "setjmp"
|
||||||
|
@(private) LLONGJMP :: "longjmp"
|
||||||
|
}
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desire
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool {
|
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool {
|
||||||
assert(failure != .release)
|
assert(failure != .release)
|
||||||
assert(failure != .acq_rel)
|
assert(failure != .acq_rel)
|
||||||
|
|
||||||
|
|||||||
+60
-9
@@ -17,6 +17,12 @@ when ODIN_OS == .Windows {
|
|||||||
|
|
||||||
FILE :: struct {}
|
FILE :: struct {}
|
||||||
|
|
||||||
|
Whence :: enum int {
|
||||||
|
SET = SEEK_SET,
|
||||||
|
CUR = SEEK_CUR,
|
||||||
|
END = SEEK_END,
|
||||||
|
}
|
||||||
|
|
||||||
// MSVCRT compatible.
|
// MSVCRT compatible.
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
_IOFBF :: 0x0000
|
_IOFBF :: 0x0000
|
||||||
@@ -83,6 +89,30 @@ when ODIN_OS == .Linux {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .JS {
|
||||||
|
fpos_t :: struct #raw_union { _: [16]char, _: longlong, _: double, }
|
||||||
|
|
||||||
|
_IOFBF :: 0
|
||||||
|
_IOLBF :: 1
|
||||||
|
_IONBF :: 2
|
||||||
|
|
||||||
|
BUFSIZ :: 1024
|
||||||
|
|
||||||
|
EOF :: int(-1)
|
||||||
|
|
||||||
|
FOPEN_MAX :: 1000
|
||||||
|
|
||||||
|
FILENAME_MAX :: 4096
|
||||||
|
|
||||||
|
L_tmpnam :: 20
|
||||||
|
|
||||||
|
SEEK_SET :: 0
|
||||||
|
SEEK_CUR :: 1
|
||||||
|
SEEK_END :: 2
|
||||||
|
|
||||||
|
TMP_MAX :: 308915776
|
||||||
|
}
|
||||||
|
|
||||||
when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||||
fpos_t :: distinct i64
|
fpos_t :: distinct i64
|
||||||
|
|
||||||
@@ -101,6 +131,8 @@ when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
|||||||
SEEK_CUR :: 1
|
SEEK_CUR :: 1
|
||||||
SEEK_END :: 2
|
SEEK_END :: 2
|
||||||
|
|
||||||
|
TMP_MAX :: 308915776
|
||||||
|
|
||||||
foreign libc {
|
foreign libc {
|
||||||
__sF: [3]FILE
|
__sF: [3]FILE
|
||||||
}
|
}
|
||||||
@@ -128,6 +160,8 @@ when ODIN_OS == .FreeBSD {
|
|||||||
SEEK_CUR :: 1
|
SEEK_CUR :: 1
|
||||||
SEEK_END :: 2
|
SEEK_END :: 2
|
||||||
|
|
||||||
|
TMP_MAX :: 308915776
|
||||||
|
|
||||||
foreign libc {
|
foreign libc {
|
||||||
@(link_name="__stderrp") stderr: ^FILE
|
@(link_name="__stderrp") stderr: ^FILE
|
||||||
@(link_name="__stdinp") stdin: ^FILE
|
@(link_name="__stdinp") stdin: ^FILE
|
||||||
@@ -195,10 +229,21 @@ when ODIN_OS == .Haiku {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .NetBSD {
|
||||||
|
@(private) LRENAME :: "__posix_rename"
|
||||||
|
@(private) LFGETPOS :: "__fgetpos50"
|
||||||
|
@(private) LFSETPOS :: "__fsetpos50"
|
||||||
|
} else {
|
||||||
|
@(private) LRENAME :: "rename"
|
||||||
|
@(private) LFGETPOS :: "fgetpos"
|
||||||
|
@(private) LFSETPOS :: "fsetpos"
|
||||||
|
}
|
||||||
|
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.21.4 Operations on files
|
// 7.21.4 Operations on files
|
||||||
remove :: proc(filename: cstring) -> int ---
|
remove :: proc(filename: cstring) -> int ---
|
||||||
|
@(link_name=LRENAME)
|
||||||
rename :: proc(old, new: cstring) -> int ---
|
rename :: proc(old, new: cstring) -> int ---
|
||||||
tmpfile :: proc() -> ^FILE ---
|
tmpfile :: proc() -> ^FILE ---
|
||||||
tmpnam :: proc(s: [^]char) -> [^]char ---
|
tmpnam :: proc(s: [^]char) -> [^]char ---
|
||||||
@@ -240,8 +285,10 @@ foreign libc {
|
|||||||
fwrite :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
fwrite :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
||||||
|
|
||||||
// 7.21.9 File positioning functions
|
// 7.21.9 File positioning functions
|
||||||
|
@(link_name=LFGETPOS)
|
||||||
fgetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
|
fgetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
|
||||||
fseek :: proc(stream: ^FILE, offset: long, whence: int) -> int ---
|
fseek :: proc(stream: ^FILE, offset: long, whence: Whence) -> int ---
|
||||||
|
@(link_name=LFSETPOS)
|
||||||
fsetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
|
fsetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
|
||||||
ftell :: proc(stream: ^FILE) -> long ---
|
ftell :: proc(stream: ^FILE) -> long ---
|
||||||
rewind :: proc(stream: ^FILE) ---
|
rewind :: proc(stream: ^FILE) ---
|
||||||
@@ -288,11 +335,11 @@ to_stream :: proc(file: ^FILE) -> io.Stream {
|
|||||||
return 0, unknown_or_eof(file)
|
return 0, unknown_or_eof(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fseek(file, long(offset), SEEK_SET) != 0 {
|
if fseek(file, long(offset), .SET) != 0 {
|
||||||
return 0, unknown_or_eof(file)
|
return 0, unknown_or_eof(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer fseek(file, long(curr), SEEK_SET)
|
defer fseek(file, long(curr), .SET)
|
||||||
|
|
||||||
n = i64(fread(raw_data(p), size_of(byte), len(p), file))
|
n = i64(fread(raw_data(p), size_of(byte), len(p), file))
|
||||||
if n == 0 { err = unknown_or_eof(file) }
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
@@ -307,17 +354,21 @@ to_stream :: proc(file: ^FILE) -> io.Stream {
|
|||||||
return 0, unknown_or_eof(file)
|
return 0, unknown_or_eof(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fseek(file, long(offset), SEEK_SET) != 0 {
|
if fseek(file, long(offset), .SET) != 0 {
|
||||||
return 0, unknown_or_eof(file)
|
return 0, unknown_or_eof(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer fseek(file, long(curr), SEEK_SET)
|
defer fseek(file, long(curr), .SET)
|
||||||
|
|
||||||
n = i64(fwrite(raw_data(p), size_of(byte), len(p), file))
|
n = i64(fwrite(raw_data(p), size_of(byte), len(p), file))
|
||||||
if n == 0 { err = unknown_or_eof(file) }
|
if n == 0 { err = unknown_or_eof(file) }
|
||||||
|
|
||||||
case .Seek:
|
case .Seek:
|
||||||
if fseek(file, long(offset), int(whence)) != 0 {
|
#assert(int(Whence.SET) == int(io.Seek_From.Start))
|
||||||
|
#assert(int(Whence.CUR) == int(io.Seek_From.Current))
|
||||||
|
#assert(int(Whence.END) == int(io.Seek_From.End))
|
||||||
|
|
||||||
|
if fseek(file, long(offset), Whence(whence)) != 0 {
|
||||||
return 0, unknown_or_eof(file)
|
return 0, unknown_or_eof(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,9 +377,9 @@ to_stream :: proc(file: ^FILE) -> io.Stream {
|
|||||||
if curr == -1 {
|
if curr == -1 {
|
||||||
return 0, unknown_or_eof(file)
|
return 0, unknown_or_eof(file)
|
||||||
}
|
}
|
||||||
defer fseek(file, curr, SEEK_SET)
|
defer fseek(file, curr, .SET)
|
||||||
|
|
||||||
if fseek(file, 0, SEEK_END) != 0 {
|
if fseek(file, 0, .END) != 0 {
|
||||||
return 0, unknown_or_eof(file)
|
return 0, unknown_or_eof(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,7 +392,7 @@ to_stream :: proc(file: ^FILE) -> io.Stream {
|
|||||||
return 0, .Empty
|
return 0, .Empty
|
||||||
|
|
||||||
case .Query:
|
case .Query:
|
||||||
return io.query_utility({ .Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size })
|
return io.query_utility({ .Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query })
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-4
@@ -10,6 +10,9 @@ when ODIN_OS == .Windows {
|
|||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require)
|
||||||
|
import "base:runtime"
|
||||||
|
|
||||||
when ODIN_OS == .Windows {
|
when ODIN_OS == .Windows {
|
||||||
RAND_MAX :: 0x7fff
|
RAND_MAX :: 0x7fff
|
||||||
|
|
||||||
@@ -40,10 +43,9 @@ when ODIN_OS == .Linux {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
when ODIN_OS == .Darwin {
|
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
|
||||||
RAND_MAX :: 0x7fffffff
|
RAND_MAX :: 0x7fffffff
|
||||||
|
|
||||||
// GLIBC and MUSL only
|
|
||||||
@(private="file")
|
@(private="file")
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
@@ -55,6 +57,20 @@ when ODIN_OS == .Darwin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .NetBSD {
|
||||||
|
RAND_MAX :: 0x7fffffff
|
||||||
|
|
||||||
|
@(private="file")
|
||||||
|
@(default_calling_convention="c")
|
||||||
|
foreign libc {
|
||||||
|
__mb_cur_max: size_t
|
||||||
|
}
|
||||||
|
|
||||||
|
MB_CUR_MAX :: #force_inline proc() -> size_t {
|
||||||
|
return __mb_cur_max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// C does not declare what these values should be, as an implementation is free
|
// C does not declare what these values should be, as an implementation is free
|
||||||
// to use any two distinct values it wants to indicate success or failure.
|
// to use any two distinct values it wants to indicate success or failure.
|
||||||
// However, nobody actually does and everyone appears to have agreed upon these
|
// However, nobody actually does and everyone appears to have agreed upon these
|
||||||
@@ -99,7 +115,7 @@ foreign libc {
|
|||||||
at_quick_exit :: proc(func: proc "c" ()) -> int ---
|
at_quick_exit :: proc(func: proc "c" ()) -> int ---
|
||||||
exit :: proc(status: int) -> ! ---
|
exit :: proc(status: int) -> ! ---
|
||||||
_Exit :: proc(status: int) -> ! ---
|
_Exit :: proc(status: int) -> ! ---
|
||||||
getenv :: proc(name: cstring) -> [^]char ---
|
getenv :: proc(name: cstring) -> cstring ---
|
||||||
quick_exit :: proc(status: int) -> ! ---
|
quick_exit :: proc(status: int) -> ! ---
|
||||||
system :: proc(cmd: cstring) -> int ---
|
system :: proc(cmd: cstring) -> int ---
|
||||||
|
|
||||||
@@ -132,6 +148,10 @@ aligned_alloc :: #force_inline proc "c" (alignment, size: size_t) -> rawptr {
|
|||||||
_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
|
_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
|
||||||
}
|
}
|
||||||
return _aligned_malloc(size=size, alignment=alignment)
|
return _aligned_malloc(size=size, alignment=alignment)
|
||||||
|
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
|
||||||
|
context = runtime.default_context()
|
||||||
|
data, _ := runtime.mem_alloc_bytes(auto_cast size, auto_cast alignment)
|
||||||
|
return raw_data(data)
|
||||||
} else {
|
} else {
|
||||||
foreign libc {
|
foreign libc {
|
||||||
aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
|
aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
|
||||||
@@ -147,7 +167,10 @@ aligned_free :: #force_inline proc "c" (ptr: rawptr) {
|
|||||||
_aligned_free :: proc(ptr: rawptr) ---
|
_aligned_free :: proc(ptr: rawptr) ---
|
||||||
}
|
}
|
||||||
_aligned_free(ptr)
|
_aligned_free(ptr)
|
||||||
|
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
|
||||||
|
context = runtime.default_context()
|
||||||
|
runtime.mem_free(ptr)
|
||||||
} else {
|
} else {
|
||||||
free(ptr)
|
free(ptr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ when ODIN_OS == .Windows {
|
|||||||
foreign import libc "system:c"
|
foreign import libc "system:c"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.24.2 Copying functions
|
// 7.24.2 Copying functions
|
||||||
memcpy :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---
|
memcpy :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---
|
||||||
@@ -40,7 +41,7 @@ foreign libc {
|
|||||||
strtok :: proc(s1: [^]char, s2: cstring) -> [^]char ---
|
strtok :: proc(s1: [^]char, s2: cstring) -> [^]char ---
|
||||||
|
|
||||||
// 7.24.6 Miscellaneous functions
|
// 7.24.6 Miscellaneous functions
|
||||||
strerror :: proc(errnum: int) -> [^]char ---
|
strerror :: proc(errnum: int) -> cstring ---
|
||||||
strlen :: proc(s: cstring) -> size_t ---
|
strlen :: proc(s: cstring) -> size_t ---
|
||||||
}
|
}
|
||||||
memset :: proc "c" (s: rawptr, c: int, n: size_t) -> rawptr {
|
memset :: proc "c" (s: rawptr, c: int, n: size_t) -> rawptr {
|
||||||
|
|||||||
+30
-4
@@ -45,35 +45,61 @@ when ODIN_OS == .Windows {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
|
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku || ODIN_OS == .JS {
|
||||||
@(default_calling_convention="c")
|
@(default_calling_convention="c")
|
||||||
foreign libc {
|
foreign libc {
|
||||||
// 7.27.2 Time manipulation functions
|
// 7.27.2 Time manipulation functions
|
||||||
clock :: proc() -> clock_t ---
|
clock :: proc() -> clock_t ---
|
||||||
|
@(link_name=LDIFFTIME)
|
||||||
difftime :: proc(time1, time2: time_t) -> double ---
|
difftime :: proc(time1, time2: time_t) -> double ---
|
||||||
|
@(link_name=LMKTIME)
|
||||||
mktime :: proc(timeptr: ^tm) -> time_t ---
|
mktime :: proc(timeptr: ^tm) -> time_t ---
|
||||||
|
@(link_name=LTIME)
|
||||||
time :: proc(timer: ^time_t) -> time_t ---
|
time :: proc(timer: ^time_t) -> time_t ---
|
||||||
timespec_get :: proc(ts: ^timespec, base: int) -> int ---
|
timespec_get :: proc(ts: ^timespec, base: int) -> int ---
|
||||||
|
|
||||||
// 7.27.3 Time conversion functions
|
// 7.27.3 Time conversion functions
|
||||||
asctime :: proc(timeptr: ^tm) -> [^]char ---
|
asctime :: proc(timeptr: ^tm) -> [^]char ---
|
||||||
|
@(link_name=LCTIME)
|
||||||
ctime :: proc(timer: ^time_t) -> [^]char ---
|
ctime :: proc(timer: ^time_t) -> [^]char ---
|
||||||
|
@(link_name=LGMTIME)
|
||||||
gmtime :: proc(timer: ^time_t) -> ^tm ---
|
gmtime :: proc(timer: ^time_t) -> ^tm ---
|
||||||
|
@(link_name=LLOCALTIME)
|
||||||
localtime :: proc(timer: ^time_t) -> ^tm ---
|
localtime :: proc(timer: ^time_t) -> ^tm ---
|
||||||
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
|
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when ODIN_OS == .NetBSD {
|
||||||
|
@(private) LDIFFTIME :: "__difftime50"
|
||||||
|
@(private) LMKTIME :: "__mktime50"
|
||||||
|
@(private) LTIME :: "__time50"
|
||||||
|
@(private) LCTIME :: "__ctime50"
|
||||||
|
@(private) LGMTIME :: "__gmtime50"
|
||||||
|
@(private) LLOCALTIME :: "__localtime50"
|
||||||
|
} else {
|
||||||
|
@(private) LDIFFTIME :: "difftime"
|
||||||
|
@(private) LMKTIME :: "mktime"
|
||||||
|
@(private) LTIME :: "time"
|
||||||
|
@(private) LCTIME :: "ctime"
|
||||||
|
@(private) LGMTIME :: "gmtime"
|
||||||
|
@(private) LLOCALTIME :: "localtime"
|
||||||
|
}
|
||||||
|
|
||||||
when ODIN_OS == .OpenBSD {
|
when ODIN_OS == .OpenBSD {
|
||||||
CLOCKS_PER_SEC :: 100
|
CLOCKS_PER_SEC :: 100
|
||||||
} else {
|
} else {
|
||||||
CLOCKS_PER_SEC :: 1000000
|
CLOCKS_PER_SEC :: 1000000
|
||||||
}
|
}
|
||||||
|
|
||||||
TIME_UTC :: 1
|
TIME_UTC :: 1
|
||||||
|
|
||||||
time_t :: distinct i64
|
time_t :: distinct i64
|
||||||
|
|
||||||
clock_t :: long
|
when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
|
||||||
|
clock_t :: distinct int32_t
|
||||||
|
} else {
|
||||||
|
clock_t :: distinct long
|
||||||
|
}
|
||||||
|
|
||||||
timespec :: struct {
|
timespec :: struct {
|
||||||
tv_sec: time_t,
|
tv_sec: time_t,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ when ODIN_OS == .Windows {
|
|||||||
wctrans_t :: distinct wchar_t
|
wctrans_t :: distinct wchar_t
|
||||||
wctype_t :: distinct ushort
|
wctype_t :: distinct ushort
|
||||||
|
|
||||||
} else when ODIN_OS == .Linux {
|
} else when ODIN_OS == .Linux || ODIN_OS == .JS {
|
||||||
wctrans_t :: distinct intptr_t
|
wctrans_t :: distinct intptr_t
|
||||||
wctype_t :: distinct ulong
|
wctype_t :: distinct ulong
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
Ginger Bill: Cosmetic changes.
|
||||||
|
|
||||||
|
A small GZIP implementation as an example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Example:
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:os"
|
||||||
|
import "core:compress"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
// Small GZIP file with fextra, fname and fcomment present.
|
||||||
|
@private
|
||||||
|
TEST: []u8 = {
|
||||||
|
0x1f, 0x8b, 0x08, 0x1c, 0xcb, 0x3b, 0x3a, 0x5a,
|
||||||
|
0x02, 0x03, 0x07, 0x00, 0x61, 0x62, 0x03, 0x00,
|
||||||
|
0x63, 0x64, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x6e,
|
||||||
|
0x61, 0x6d, 0x65, 0x00, 0x54, 0x68, 0x69, 0x73,
|
||||||
|
0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f,
|
||||||
|
0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2b, 0x48,
|
||||||
|
0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x01, 0x00, 0x15,
|
||||||
|
0x6a, 0x2c, 0x42, 0x07, 0x00, 0x00, 0x00,
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
// Set up output buffer.
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
|
||||||
|
stdout :: proc(s: string) {
|
||||||
|
os.write_string(os.stdout, s)
|
||||||
|
}
|
||||||
|
stderr :: proc(s: string) {
|
||||||
|
os.write_string(os.stderr, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := os.args
|
||||||
|
|
||||||
|
if len(args) < 2 {
|
||||||
|
stderr("No input file specified.\n")
|
||||||
|
err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||||
|
if err == nil {
|
||||||
|
stdout("Displaying test vector: ")
|
||||||
|
stdout(bytes.buffer_to_string(&buf))
|
||||||
|
stdout("\n")
|
||||||
|
} else {
|
||||||
|
fmt.printf("gzip.load returned %v\n", err)
|
||||||
|
}
|
||||||
|
bytes.buffer_destroy(&buf)
|
||||||
|
os.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The rest are all files.
|
||||||
|
args = args[1:]
|
||||||
|
err: Error
|
||||||
|
|
||||||
|
for file in args {
|
||||||
|
if file == "-" {
|
||||||
|
// Read from stdin
|
||||||
|
s := os.stream_from_handle(os.stdin)
|
||||||
|
ctx := &compress.Context_Stream_Input{
|
||||||
|
input = s,
|
||||||
|
}
|
||||||
|
err = load(ctx, &buf)
|
||||||
|
} else {
|
||||||
|
err = load(file, &buf)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if err != E_General.File_Not_Found {
|
||||||
|
stderr("File not found: ")
|
||||||
|
stderr(file)
|
||||||
|
stderr("\n")
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
|
stderr("GZIP returned an error.\n")
|
||||||
|
bytes.buffer_destroy(&buf)
|
||||||
|
os.exit(2)
|
||||||
|
}
|
||||||
|
stdout(bytes.buffer_to_string(&buf))
|
||||||
|
}
|
||||||
|
bytes.buffer_destroy(&buf)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package compress_gzip
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
//+build ignore
|
|
||||||
package compress_gzip
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
|
||||||
Made available under Odin's BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
Jeroen van Rijn: Initial implementation.
|
|
||||||
Ginger Bill: Cosmetic changes.
|
|
||||||
|
|
||||||
A small GZIP implementation as an example.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:bytes"
|
|
||||||
import "core:os"
|
|
||||||
import "core:compress"
|
|
||||||
import "core:fmt"
|
|
||||||
|
|
||||||
// Small GZIP file with fextra, fname and fcomment present.
|
|
||||||
@private
|
|
||||||
TEST: []u8 = {
|
|
||||||
0x1f, 0x8b, 0x08, 0x1c, 0xcb, 0x3b, 0x3a, 0x5a,
|
|
||||||
0x02, 0x03, 0x07, 0x00, 0x61, 0x62, 0x03, 0x00,
|
|
||||||
0x63, 0x64, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x6e,
|
|
||||||
0x61, 0x6d, 0x65, 0x00, 0x54, 0x68, 0x69, 0x73,
|
|
||||||
0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f,
|
|
||||||
0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2b, 0x48,
|
|
||||||
0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x01, 0x00, 0x15,
|
|
||||||
0x6a, 0x2c, 0x42, 0x07, 0x00, 0x00, 0x00,
|
|
||||||
}
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
// Set up output buffer.
|
|
||||||
buf := bytes.Buffer{}
|
|
||||||
|
|
||||||
stdout :: proc(s: string) {
|
|
||||||
os.write_string(os.stdout, s)
|
|
||||||
}
|
|
||||||
stderr :: proc(s: string) {
|
|
||||||
os.write_string(os.stderr, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := os.args
|
|
||||||
|
|
||||||
if len(args) < 2 {
|
|
||||||
stderr("No input file specified.\n")
|
|
||||||
err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
|
|
||||||
if err == nil {
|
|
||||||
stdout("Displaying test vector: ")
|
|
||||||
stdout(bytes.buffer_to_string(&buf))
|
|
||||||
stdout("\n")
|
|
||||||
} else {
|
|
||||||
fmt.printf("gzip.load returned %v\n", err)
|
|
||||||
}
|
|
||||||
bytes.buffer_destroy(&buf)
|
|
||||||
os.exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The rest are all files.
|
|
||||||
args = args[1:]
|
|
||||||
err: Error
|
|
||||||
|
|
||||||
for file in args {
|
|
||||||
if file == "-" {
|
|
||||||
// Read from stdin
|
|
||||||
s := os.stream_from_handle(os.stdin)
|
|
||||||
ctx := &compress.Context_Stream_Input{
|
|
||||||
input = s,
|
|
||||||
}
|
|
||||||
err = load(ctx, &buf)
|
|
||||||
} else {
|
|
||||||
err = load(file, &buf)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if err != E_General.File_Not_Found {
|
|
||||||
stderr("File not found: ")
|
|
||||||
stderr(file)
|
|
||||||
stderr("\n")
|
|
||||||
os.exit(1)
|
|
||||||
}
|
|
||||||
stderr("GZIP returned an error.\n")
|
|
||||||
bytes.buffer_destroy(&buf)
|
|
||||||
os.exit(2)
|
|
||||||
}
|
|
||||||
stdout(bytes.buffer_to_string(&buf))
|
|
||||||
}
|
|
||||||
bytes.buffer_destroy(&buf)
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
which is an English word model.
|
which is an English word model.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// package shoco is an implementation of the shoco short string compressor
|
|
||||||
package compress_shoco
|
package compress_shoco
|
||||||
|
|
||||||
DEFAULT_MODEL :: Shoco_Model {
|
DEFAULT_MODEL :: Shoco_Model {
|
||||||
@@ -145,4 +144,4 @@ DEFAULT_MODEL :: Shoco_Model {
|
|||||||
{ 0xc0000000, 2, 4, { 25, 22, 19, 16, 16, 16, 16, 16 }, { 15, 7, 7, 7, 0, 0, 0, 0 }, 0xe0, 0xc0 },
|
{ 0xc0000000, 2, 4, { 25, 22, 19, 16, 16, 16, 16, 16 }, { 15, 7, 7, 7, 0, 0, 0, 0 }, 0xe0, 0xc0 },
|
||||||
{ 0xe0000000, 4, 8, { 23, 19, 15, 11, 8, 5, 2, 0 }, { 31, 15, 15, 15, 7, 7, 7, 3 }, 0xf0, 0xe0 },
|
{ 0xe0000000, 4, 8, { 23, 19, 15, 11, 8, 5, 2, 0 }, { 31, 15, 15, 15, 7, 7, 7, 3 }, 0xf0, 0xe0 },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
An implementation of [shoco](https://github.com/Ed-von-Schleck/shoco) by Christian Schramm.
|
An implementation of [shoco](https://github.com/Ed-von-Schleck/shoco) by Christian Schramm.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// package shoco is an implementation of the shoco short string compressor
|
// package shoco is an implementation of the shoco short string compressor.
|
||||||
package compress_shoco
|
package compress_shoco
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
@@ -308,4 +308,4 @@ compress_string :: proc(input: string, model := DEFAULT_MODEL, allocator := cont
|
|||||||
resize(&buf, length) or_return
|
resize(&buf, length) or_return
|
||||||
return buf[:length], result
|
return buf[:length], result
|
||||||
}
|
}
|
||||||
compress :: proc{compress_string_to_buffer, compress_string}
|
compress :: proc{compress_string_to_buffer, compress_string}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
|
Made available under Odin's BSD-3 license.
|
||||||
|
|
||||||
|
List of contributors:
|
||||||
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
|
||||||
|
An example of how to use `zlib.inflate`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Example:
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
ODIN_DEMO := []u8{
|
||||||
|
120, 218, 101, 144, 65, 110, 131, 48, 16, 69, 215, 246, 41, 190, 44, 69, 73, 32, 148, 182,
|
||||||
|
75, 75, 28, 32, 251, 46, 217, 88, 238, 0, 86, 192, 32, 219, 36, 170, 170, 172, 122, 137,
|
||||||
|
238, 122, 197, 30, 161, 70, 162, 20, 81, 203, 139, 25, 191, 255, 191, 60, 51, 40, 125, 81,
|
||||||
|
53, 33, 144, 15, 156, 155, 110, 232, 93, 128, 208, 189, 35, 89, 117, 65, 112, 222, 41, 99,
|
||||||
|
33, 37, 6, 215, 235, 195, 17, 239, 156, 197, 170, 118, 170, 131, 44, 32, 82, 164, 72, 240,
|
||||||
|
253, 245, 249, 129, 12, 185, 224, 76, 105, 61, 118, 99, 171, 66, 239, 38, 193, 35, 103, 85,
|
||||||
|
172, 66, 127, 33, 139, 24, 244, 235, 141, 49, 204, 223, 76, 208, 205, 204, 166, 7, 173, 60,
|
||||||
|
97, 159, 238, 37, 214, 41, 105, 129, 167, 5, 102, 27, 152, 173, 97, 178, 129, 73, 129, 231,
|
||||||
|
5, 230, 27, 152, 175, 225, 52, 192, 127, 243, 170, 157, 149, 18, 121, 142, 115, 109, 227, 122,
|
||||||
|
64, 87, 114, 111, 161, 49, 182, 6, 181, 158, 162, 226, 206, 167, 27, 215, 246, 48, 56, 99,
|
||||||
|
67, 117, 16, 47, 13, 45, 35, 151, 98, 231, 75, 1, 173, 90, 61, 101, 146, 71, 136, 244,
|
||||||
|
170, 218, 145, 176, 123, 45, 173, 56, 113, 134, 191, 51, 219, 78, 235, 95, 28, 249, 253, 7,
|
||||||
|
159, 150, 133, 125,
|
||||||
|
}
|
||||||
|
OUTPUT_SIZE :: 432
|
||||||
|
|
||||||
|
buf: bytes.Buffer
|
||||||
|
|
||||||
|
// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
|
||||||
|
err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
|
||||||
|
defer bytes.buffer_destroy(&buf)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.printf("\nError: %v\n", err)
|
||||||
|
}
|
||||||
|
s := bytes.buffer_to_string(&buf)
|
||||||
|
fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s)
|
||||||
|
assert(len(s) == OUTPUT_SIZE)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package compress_zlib
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
//+build ignore
|
|
||||||
package compress_zlib
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
|
||||||
Made available under Odin's BSD-3 license.
|
|
||||||
|
|
||||||
List of contributors:
|
|
||||||
Jeroen van Rijn: Initial implementation.
|
|
||||||
|
|
||||||
An example of how to use `zlib.inflate`.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import "core:bytes"
|
|
||||||
import "core:fmt"
|
|
||||||
|
|
||||||
main :: proc() {
|
|
||||||
ODIN_DEMO := []u8{
|
|
||||||
120, 218, 101, 144, 65, 110, 131, 48, 16, 69, 215, 246, 41, 190, 44, 69, 73, 32, 148, 182,
|
|
||||||
75, 75, 28, 32, 251, 46, 217, 88, 238, 0, 86, 192, 32, 219, 36, 170, 170, 172, 122, 137,
|
|
||||||
238, 122, 197, 30, 161, 70, 162, 20, 81, 203, 139, 25, 191, 255, 191, 60, 51, 40, 125, 81,
|
|
||||||
53, 33, 144, 15, 156, 155, 110, 232, 93, 128, 208, 189, 35, 89, 117, 65, 112, 222, 41, 99,
|
|
||||||
33, 37, 6, 215, 235, 195, 17, 239, 156, 197, 170, 118, 170, 131, 44, 32, 82, 164, 72, 240,
|
|
||||||
253, 245, 249, 129, 12, 185, 224, 76, 105, 61, 118, 99, 171, 66, 239, 38, 193, 35, 103, 85,
|
|
||||||
172, 66, 127, 33, 139, 24, 244, 235, 141, 49, 204, 223, 76, 208, 205, 204, 166, 7, 173, 60,
|
|
||||||
97, 159, 238, 37, 214, 41, 105, 129, 167, 5, 102, 27, 152, 173, 97, 178, 129, 73, 129, 231,
|
|
||||||
5, 230, 27, 152, 175, 225, 52, 192, 127, 243, 170, 157, 149, 18, 121, 142, 115, 109, 227, 122,
|
|
||||||
64, 87, 114, 111, 161, 49, 182, 6, 181, 158, 162, 226, 206, 167, 27, 215, 246, 48, 56, 99,
|
|
||||||
67, 117, 16, 47, 13, 45, 35, 151, 98, 231, 75, 1, 173, 90, 61, 101, 146, 71, 136, 244,
|
|
||||||
170, 218, 145, 176, 123, 45, 173, 56, 113, 134, 191, 51, 219, 78, 235, 95, 28, 249, 253, 7,
|
|
||||||
159, 150, 133, 125,
|
|
||||||
}
|
|
||||||
OUTPUT_SIZE :: 432
|
|
||||||
|
|
||||||
buf: bytes.Buffer
|
|
||||||
|
|
||||||
// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
|
|
||||||
err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
|
|
||||||
defer bytes.buffer_destroy(&buf)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.printf("\nError: %v\n", err)
|
|
||||||
}
|
|
||||||
s := bytes.buffer_to_string(&buf)
|
|
||||||
fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s)
|
|
||||||
assert(len(s) == OUTPUT_SIZE)
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//+vet !using-param
|
#+vet !using-param
|
||||||
package compress_zlib
|
package compress_zlib
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -12,6 +12,7 @@ package compress_zlib
|
|||||||
|
|
||||||
import "core:compress"
|
import "core:compress"
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:hash"
|
import "core:hash"
|
||||||
@@ -123,13 +124,7 @@ Huffman_Table :: struct {
|
|||||||
@(optimization_mode="favor_size")
|
@(optimization_mode="favor_size")
|
||||||
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
||||||
assert(bits <= 16)
|
assert(bits <= 16)
|
||||||
// NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling
|
r = intrinsics.reverse_bits(n)
|
||||||
// by reversing all of the bits and masking out the unneeded ones.
|
|
||||||
r = n
|
|
||||||
r = ((r & 0xAAAA) >> 1) | ((r & 0x5555) << 1)
|
|
||||||
r = ((r & 0xCCCC) >> 2) | ((r & 0x3333) << 2)
|
|
||||||
r = ((r & 0xF0F0) >> 4) | ((r & 0x0F0F) << 4)
|
|
||||||
r = ((r & 0xFF00) >> 8) | ((r & 0x00FF) << 8)
|
|
||||||
|
|
||||||
r >>= (16 - bits)
|
r >>= (16 - bits)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package container_dynamic_bit_array
|
package container_dynamic_bit_array
|
||||||
|
|
||||||
|
import "base:builtin"
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ NUM_BITS :: 64
|
|||||||
Bit_Array :: struct {
|
Bit_Array :: struct {
|
||||||
bits: [dynamic]u64,
|
bits: [dynamic]u64,
|
||||||
bias: int,
|
bias: int,
|
||||||
max_index: int,
|
length: int,
|
||||||
free_pointer: bool,
|
free_pointer: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,9 +53,9 @@ Returns:
|
|||||||
*/
|
*/
|
||||||
iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) {
|
iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) {
|
||||||
index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
|
index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
|
||||||
if index > it.array.max_index { return false, 0, false }
|
if index >= it.array.length + it.array.bias { return false, 0, false }
|
||||||
|
|
||||||
word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
|
word := it.array.bits[it.word_idx] if builtin.len(it.array.bits) > it.word_idx else 0
|
||||||
set = (word >> it.bit_idx & 1) == 1
|
set = (word >> it.bit_idx & 1) == 1
|
||||||
|
|
||||||
it.bit_idx += 1
|
it.bit_idx += 1
|
||||||
@@ -106,22 +107,22 @@ Returns:
|
|||||||
*/
|
*/
|
||||||
@(private="file")
|
@(private="file")
|
||||||
iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) {
|
iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) {
|
||||||
word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
|
word := it.array.bits[it.word_idx] if builtin.len(it.array.bits) > it.word_idx else 0
|
||||||
when ! ITERATE_SET_BITS { word = ~word }
|
when ! ITERATE_SET_BITS { word = ~word }
|
||||||
|
|
||||||
// If the word is empty or we have already gone over all the bits in it,
|
// If the word is empty or we have already gone over all the bits in it,
|
||||||
// b.bit_idx is greater than the index of any set bit in the word,
|
// b.bit_idx is greater than the index of any set bit in the word,
|
||||||
// meaning that word >> b.bit_idx == 0.
|
// meaning that word >> b.bit_idx == 0.
|
||||||
for it.word_idx < len(it.array.bits) && word >> it.bit_idx == 0 {
|
for it.word_idx < builtin.len(it.array.bits) && word >> it.bit_idx == 0 {
|
||||||
it.word_idx += 1
|
it.word_idx += 1
|
||||||
it.bit_idx = 0
|
it.bit_idx = 0
|
||||||
word = it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
|
word = it.array.bits[it.word_idx] if builtin.len(it.array.bits) > it.word_idx else 0
|
||||||
when ! ITERATE_SET_BITS { word = ~word }
|
when ! ITERATE_SET_BITS { word = ~word }
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are iterating the set bits, reaching the end of the array means we have no more bits to check
|
// If we are iterating the set bits, reaching the end of the array means we have no more bits to check
|
||||||
when ITERATE_SET_BITS {
|
when ITERATE_SET_BITS {
|
||||||
if it.word_idx >= len(it.array.bits) {
|
if it.word_idx >= builtin.len(it.array.bits) {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,7 +136,7 @@ iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) ->
|
|||||||
it.bit_idx = 0
|
it.bit_idx = 0
|
||||||
it.word_idx += 1
|
it.word_idx += 1
|
||||||
}
|
}
|
||||||
return index, index <= it.array.max_index
|
return index, index < it.array.length + it.array.bias
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
Gets the state of a bit in the bit-array
|
Gets the state of a bit in the bit-array
|
||||||
@@ -160,7 +161,7 @@ get :: proc(ba: ^Bit_Array, #any_int index: uint) -> (res: bool, ok: bool) #opti
|
|||||||
If we `get` a bit that doesn't fit in the Bit Array, it's naturally `false`.
|
If we `get` a bit that doesn't fit in the Bit Array, it's naturally `false`.
|
||||||
This early-out prevents unnecessary resizing.
|
This early-out prevents unnecessary resizing.
|
||||||
*/
|
*/
|
||||||
if leg_index + 1 > len(ba.bits) { return false, true }
|
if leg_index + 1 > builtin.len(ba.bits) { return false, true }
|
||||||
|
|
||||||
val := u64(1 << uint(bit_index))
|
val := u64(1 << uint(bit_index))
|
||||||
res = ba.bits[leg_index] & val == val
|
res = ba.bits[leg_index] & val == val
|
||||||
@@ -208,7 +209,7 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, set_to: bool = true, allocator
|
|||||||
|
|
||||||
resize_if_needed(ba, leg_index) or_return
|
resize_if_needed(ba, leg_index) or_return
|
||||||
|
|
||||||
ba.max_index = max(idx, ba.max_index)
|
ba.length = max(1 + idx, ba.length)
|
||||||
|
|
||||||
if set_to {
|
if set_to {
|
||||||
ba.bits[leg_index] |= 1 << uint(bit_index)
|
ba.bits[leg_index] |= 1 << uint(bit_index)
|
||||||
@@ -258,9 +259,13 @@ Inputs:
|
|||||||
unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
|
unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
|
||||||
b.bits[bit >> INDEX_SHIFT] &~= 1 << uint(bit & INDEX_MASK)
|
b.bits[bit >> INDEX_SHIFT] &~= 1 << uint(bit & INDEX_MASK)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||||
|
|
||||||
|
The range of bits created by this procedure is `min_index..<max_index`, and the
|
||||||
|
array will be able to expand beyond `max_index` if needed.
|
||||||
|
|
||||||
*Allocates (`new(Bit_Array) & make(ba.bits)`)*
|
*Allocates (`new(Bit_Array) & make(ba.bits)`)*
|
||||||
|
|
||||||
Inputs:
|
Inputs:
|
||||||
@@ -272,22 +277,50 @@ Returns:
|
|||||||
- ba: Allocates a bit_Array, backing data is set to `max-min / 64` indices, rounded up (eg 65 - 0 allocates for [2]u64).
|
- ba: Allocates a bit_Array, backing data is set to `max-min / 64` indices, rounded up (eg 65 - 0 allocates for [2]u64).
|
||||||
*/
|
*/
|
||||||
create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
|
create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
|
||||||
context.allocator = allocator
|
|
||||||
size_in_bits := max_index - min_index
|
size_in_bits := max_index - min_index
|
||||||
|
|
||||||
if size_in_bits < 1 { return {}, false }
|
if size_in_bits < 0 { return {}, false }
|
||||||
|
|
||||||
legs := size_in_bits >> INDEX_SHIFT
|
res = new(Bit_Array, allocator)
|
||||||
if size_in_bits & INDEX_MASK > 0 {legs+=1}
|
ok = init(res, max_index, min_index, allocator)
|
||||||
bits, err := make([dynamic]u64, legs)
|
|
||||||
ok = err == mem.Allocator_Error.None
|
|
||||||
res = new(Bit_Array)
|
|
||||||
res.bits = bits
|
|
||||||
res.bias = min_index
|
|
||||||
res.max_index = max_index
|
|
||||||
res.free_pointer = true
|
res.free_pointer = true
|
||||||
|
|
||||||
|
if !ok { free(res, allocator) }
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
A helper function to initialize a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||||
|
|
||||||
|
The range of bits created by this procedure is `min_index..<max_index`, and the
|
||||||
|
array will be able to expand beyond `max_index` if needed.
|
||||||
|
|
||||||
|
*Allocates (`make(ba.bits)`)*
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- max_index: maximum starting index
|
||||||
|
- min_index: minimum starting index (used as a bias)
|
||||||
|
- allocator: (default is context.allocator)
|
||||||
|
*/
|
||||||
|
init :: proc(res: ^Bit_Array, max_index: int, min_index: int = 0, allocator := context.allocator) -> (ok: bool) {
|
||||||
|
size_in_bits := max_index - min_index
|
||||||
|
|
||||||
|
if size_in_bits < 0 { return false }
|
||||||
|
|
||||||
|
legs := size_in_bits >> INDEX_SHIFT
|
||||||
|
if size_in_bits & INDEX_MASK > 0 { legs += 1 }
|
||||||
|
|
||||||
|
bits, err := make([dynamic]u64, legs, allocator)
|
||||||
|
ok = err == nil
|
||||||
|
|
||||||
|
res.bits = bits
|
||||||
|
res.bias = min_index
|
||||||
|
res.length = max_index - min_index
|
||||||
|
res.free_pointer = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Sets all values in the Bit_Array to zero.
|
Sets all values in the Bit_Array to zero.
|
||||||
|
|
||||||
@@ -299,6 +332,48 @@ clear :: proc(ba: ^Bit_Array) {
|
|||||||
mem.zero_slice(ba.bits[:])
|
mem.zero_slice(ba.bits[:])
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
Gets the length of set and unset valid bits in the Bit_Array.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- ba: The target Bit_Array
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- length: The length of valid bits.
|
||||||
|
*/
|
||||||
|
len :: proc(ba: ^Bit_Array) -> (length: int) {
|
||||||
|
if ba == nil { return }
|
||||||
|
return ba.length
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Shrinks the Bit_Array's backing storage to the smallest possible size.
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
- ba: The target Bit_Array
|
||||||
|
*/
|
||||||
|
shrink :: proc(ba: ^Bit_Array) #no_bounds_check {
|
||||||
|
if ba == nil { return }
|
||||||
|
legs_needed := builtin.len(ba.bits)
|
||||||
|
for i := legs_needed - 1; i >= 0; i -= 1 {
|
||||||
|
if ba.bits[i] == 0 {
|
||||||
|
legs_needed -= 1
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if legs_needed == builtin.len(ba.bits) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ba.length = 0
|
||||||
|
if legs_needed > 0 {
|
||||||
|
if legs_needed > 1 {
|
||||||
|
ba.length = (legs_needed - 1) * NUM_BITS
|
||||||
|
}
|
||||||
|
ba.length += NUM_BITS - int(intrinsics.count_leading_zeros(ba.bits[legs_needed - 1]))
|
||||||
|
}
|
||||||
|
resize(&ba.bits, legs_needed)
|
||||||
|
builtin.shrink(&ba.bits)
|
||||||
|
}
|
||||||
|
/*
|
||||||
Deallocates the Bit_Array and its backing storage
|
Deallocates the Bit_Array and its backing storage
|
||||||
|
|
||||||
Inputs:
|
Inputs:
|
||||||
@@ -321,8 +396,8 @@ resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocat
|
|||||||
|
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
|
||||||
if legs + 1 > len(ba.bits) {
|
if legs + 1 > builtin.len(ba.bits) {
|
||||||
resize(&ba.bits, legs + 1)
|
resize(&ba.bits, legs + 1)
|
||||||
}
|
}
|
||||||
return len(ba.bits) > legs
|
return builtin.len(ba.bits) > legs
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
The Bit Array can be used in several ways:
|
The Bit Array can be used in several ways:
|
||||||
|
|
||||||
- By default you don't need to instantiate a Bit Array:
|
By default you don't need to instantiate a Bit Array.
|
||||||
|
Example:
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
@@ -22,8 +22,8 @@ The Bit Array can be used in several ways:
|
|||||||
destroy(&bits)
|
destroy(&bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
- A Bit Array can optionally allow for negative indices, if the minimum value was given during creation:
|
A Bit Array can optionally allow for negative indices, if the minimum value was given during creation.
|
||||||
|
Example:
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
/*
|
/*
|
||||||
Package list implements an intrusive doubly-linked list.
|
Package list implements an intrusive doubly-linked list.
|
||||||
|
|
||||||
An intrusive container requires a `Node` to be embedded in your own structure, like this:
|
An intrusive container requires a `Node` to be embedded in your own structure, like this.
|
||||||
|
Example:
|
||||||
My_String :: struct {
|
My_String :: struct {
|
||||||
node: list.Node,
|
node: list.Node,
|
||||||
value: string,
|
value: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
Embedding the members of a `list.Node` in your structure with the `using` keyword is also allowed:
|
Embedding the members of a `list.Node` in your structure with the `using` keyword is also allowed.
|
||||||
|
Example:
|
||||||
My_String :: struct {
|
My_String :: struct {
|
||||||
using node: list.Node,
|
using node: list.Node,
|
||||||
value: string,
|
value: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
Here is a full example:
|
Here is a full example.
|
||||||
|
Example:
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
@@ -42,5 +42,8 @@ Here is a full example:
|
|||||||
value: string,
|
value: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
Hello
|
||||||
|
World
|
||||||
*/
|
*/
|
||||||
package container_intrusive_list
|
package container_intrusive_list
|
||||||
|
|||||||
@@ -139,9 +139,13 @@ clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
|
|||||||
resize(a, 0)
|
resize(a, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) {
|
push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) -> bool {
|
||||||
n := copy(a.data[a.len:], items[:])
|
if a.len + builtin.len(items) <= cap(a^) {
|
||||||
a.len += n
|
n := copy(a.data[a.len:], items[:])
|
||||||
|
a.len += n
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {
|
inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {
|
||||||
|
|||||||
@@ -80,8 +80,8 @@ ghash :: proc "contextless" (dst, key, data: []byte) {
|
|||||||
h2 := h0 ~ h1
|
h2 := h0 ~ h1
|
||||||
h2r := h0r ~ h1r
|
h2r := h0r ~ h1r
|
||||||
|
|
||||||
src: []byte
|
|
||||||
for l > 0 {
|
for l > 0 {
|
||||||
|
src: []byte = ---
|
||||||
if l >= _aes.GHASH_BLOCK_SIZE {
|
if l >= _aes.GHASH_BLOCK_SIZE {
|
||||||
src = buf
|
src = buf
|
||||||
buf = buf[_aes.GHASH_BLOCK_SIZE:]
|
buf = buf[_aes.GHASH_BLOCK_SIZE:]
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//+build amd64
|
#+build amd64
|
||||||
package aes_hw_intel
|
package aes_hw_intel
|
||||||
|
|
||||||
import "core:sys/info"
|
import "core:sys/info"
|
||||||
|
|
||||||
// is_supporte returns true iff hardware accelerated AES
|
// is_supported returns true iff hardware accelerated AES
|
||||||
// is supported.
|
// is supported.
|
||||||
is_supported :: proc "contextless" () -> bool {
|
is_supported :: proc "contextless" () -> bool {
|
||||||
features, ok := info.cpu_features.?
|
features, ok := info.cpu_features.?
|
||||||
|
|||||||
@@ -20,12 +20,11 @@
|
|||||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
//+build amd64
|
#+build amd64
|
||||||
package aes_hw_intel
|
package aes_hw_intel
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
import "core:crypto/_aes"
|
import "core:crypto/_aes"
|
||||||
import "core:simd"
|
|
||||||
import "core:simd/x86"
|
import "core:simd/x86"
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
@@ -58,14 +57,11 @@ GHASH_STRIDE_BYTES_HW :: GHASH_STRIDE_HW * _aes.GHASH_BLOCK_SIZE
|
|||||||
// chunks. We number chunks from 0 to 3 in left to right order.
|
// chunks. We number chunks from 0 to 3 in left to right order.
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
byteswap_index := transmute(x86.__m128i)simd.i8x16{
|
_BYTESWAP_INDEX: x86.__m128i : { 0x08090a0b0c0d0e0f, 0x0001020304050607 }
|
||||||
// Note: simd.i8x16 is reverse order from x86._mm_set_epi8.
|
|
||||||
15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
@(private = "file", require_results, enable_target_feature = "sse2,ssse3")
|
@(private = "file", require_results, enable_target_feature = "sse2,ssse3")
|
||||||
byteswap :: #force_inline proc "contextless" (x: x86.__m128i) -> x86.__m128i {
|
byteswap :: #force_inline proc "contextless" (x: x86.__m128i) -> x86.__m128i {
|
||||||
return x86._mm_shuffle_epi8(x, byteswap_index)
|
return x86._mm_shuffle_epi8(x, _BYTESWAP_INDEX)
|
||||||
}
|
}
|
||||||
|
|
||||||
// From a 128-bit value kw, compute kx as the XOR of the two 64-bit
|
// From a 128-bit value kw, compute kx as the XOR of the two 64-bit
|
||||||
@@ -244,8 +240,8 @@ ghash :: proc "contextless" (dst, key, data: []byte) #no_bounds_check {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process 1 block at a time
|
// Process 1 block at a time
|
||||||
src: []byte
|
|
||||||
for l > 0 {
|
for l > 0 {
|
||||||
|
src: []byte = ---
|
||||||
if l >= _aes.GHASH_BLOCK_SIZE {
|
if l >= _aes.GHASH_BLOCK_SIZE {
|
||||||
src = buf
|
src = buf
|
||||||
buf = buf[_aes.GHASH_BLOCK_SIZE:]
|
buf = buf[_aes.GHASH_BLOCK_SIZE:]
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
//+build amd64
|
#+build amd64
|
||||||
package aes_hw_intel
|
package aes_hw_intel
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package _chacha20
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "core:encoding/endian"
|
||||||
|
import "core:math/bits"
|
||||||
|
import "core:mem"
|
||||||
|
|
||||||
|
// KEY_SIZE is the (X)ChaCha20 key size in bytes.
|
||||||
|
KEY_SIZE :: 32
|
||||||
|
// IV_SIZE is the ChaCha20 IV size in bytes.
|
||||||
|
IV_SIZE :: 12
|
||||||
|
// XIV_SIZE is the XChaCha20 IV size in bytes.
|
||||||
|
XIV_SIZE :: 24
|
||||||
|
|
||||||
|
// MAX_CTR_IETF is the maximum counter value for the IETF flavor ChaCha20.
|
||||||
|
MAX_CTR_IETF :: 0xffffffff
|
||||||
|
// BLOCK_SIZE is the (X)ChaCha20 block size in bytes.
|
||||||
|
BLOCK_SIZE :: 64
|
||||||
|
// STATE_SIZE_U32 is the (X)ChaCha20 state size in u32s.
|
||||||
|
STATE_SIZE_U32 :: 16
|
||||||
|
// Rounds is the (X)ChaCha20 round count.
|
||||||
|
ROUNDS :: 20
|
||||||
|
|
||||||
|
// SIGMA_0 is sigma[0:4].
|
||||||
|
SIGMA_0: u32 : 0x61707865
|
||||||
|
// SIGMA_1 is sigma[4:8].
|
||||||
|
SIGMA_1: u32 : 0x3320646e
|
||||||
|
// SIGMA_2 is sigma[8:12].
|
||||||
|
SIGMA_2: u32 : 0x79622d32
|
||||||
|
// SIGMA_3 is sigma[12:16].
|
||||||
|
SIGMA_3: u32 : 0x6b206574
|
||||||
|
|
||||||
|
// Context is a ChaCha20 or XChaCha20 instance.
|
||||||
|
Context :: struct {
|
||||||
|
_s: [STATE_SIZE_U32]u32,
|
||||||
|
_buffer: [BLOCK_SIZE]byte,
|
||||||
|
_off: int,
|
||||||
|
_is_ietf_flavor: bool,
|
||||||
|
_is_initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// init inititializes a Context for ChaCha20 with the provided key and
|
||||||
|
// iv.
|
||||||
|
//
|
||||||
|
// WARNING: This ONLY handles ChaCha20. XChaCha20 sub-key and IV
|
||||||
|
// derivation is expected to be handled by the caller, so that the
|
||||||
|
// HChaCha call can be suitably accelerated.
|
||||||
|
init :: proc "contextless" (ctx: ^Context, key, iv: []byte, is_xchacha: bool) {
|
||||||
|
if len(key) != KEY_SIZE || len(iv) != IV_SIZE {
|
||||||
|
intrinsics.trap()
|
||||||
|
}
|
||||||
|
|
||||||
|
k, n := key, iv
|
||||||
|
|
||||||
|
ctx._s[0] = SIGMA_0
|
||||||
|
ctx._s[1] = SIGMA_1
|
||||||
|
ctx._s[2] = SIGMA_2
|
||||||
|
ctx._s[3] = SIGMA_3
|
||||||
|
ctx._s[4] = endian.unchecked_get_u32le(k[0:4])
|
||||||
|
ctx._s[5] = endian.unchecked_get_u32le(k[4:8])
|
||||||
|
ctx._s[6] = endian.unchecked_get_u32le(k[8:12])
|
||||||
|
ctx._s[7] = endian.unchecked_get_u32le(k[12:16])
|
||||||
|
ctx._s[8] = endian.unchecked_get_u32le(k[16:20])
|
||||||
|
ctx._s[9] = endian.unchecked_get_u32le(k[20:24])
|
||||||
|
ctx._s[10] = endian.unchecked_get_u32le(k[24:28])
|
||||||
|
ctx._s[11] = endian.unchecked_get_u32le(k[28:32])
|
||||||
|
ctx._s[12] = 0
|
||||||
|
ctx._s[13] = endian.unchecked_get_u32le(n[0:4])
|
||||||
|
ctx._s[14] = endian.unchecked_get_u32le(n[4:8])
|
||||||
|
ctx._s[15] = endian.unchecked_get_u32le(n[8:12])
|
||||||
|
|
||||||
|
ctx._off = BLOCK_SIZE
|
||||||
|
ctx._is_ietf_flavor = !is_xchacha
|
||||||
|
ctx._is_initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// seek seeks the (X)ChaCha20 stream counter to the specified block.
|
||||||
|
seek :: proc(ctx: ^Context, block_nr: u64) {
|
||||||
|
assert(ctx._is_initialized)
|
||||||
|
|
||||||
|
if ctx._is_ietf_flavor {
|
||||||
|
if block_nr > MAX_CTR_IETF {
|
||||||
|
panic("crypto/chacha20: attempted to seek past maximum counter")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx._s[13] = u32(block_nr >> 32)
|
||||||
|
}
|
||||||
|
ctx._s[12] = u32(block_nr)
|
||||||
|
ctx._off = BLOCK_SIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
|
// be used again.
|
||||||
|
reset :: proc(ctx: ^Context) {
|
||||||
|
mem.zero_explicit(&ctx._s, size_of(ctx._s))
|
||||||
|
mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
||||||
|
|
||||||
|
ctx._is_initialized = false
|
||||||
|
}
|
||||||
|
|
||||||
|
check_counter_limit :: proc(ctx: ^Context, nr_blocks: int) {
|
||||||
|
// Enforce the maximum consumed keystream per IV.
|
||||||
|
//
|
||||||
|
// While all modern "standard" definitions of ChaCha20 use
|
||||||
|
// the IETF 32-bit counter, for XChaCha20 most common
|
||||||
|
// implementations allow for a 64-bit counter.
|
||||||
|
//
|
||||||
|
// Honestly, the answer here is "use a MRAE primitive", but
|
||||||
|
// go with "common" practice in the case of XChaCha20.
|
||||||
|
|
||||||
|
ERR_CTR_EXHAUSTED :: "crypto/chacha20: maximum (X)ChaCha20 keystream per IV reached"
|
||||||
|
|
||||||
|
if ctx._is_ietf_flavor {
|
||||||
|
if u64(ctx._s[12]) + u64(nr_blocks) > MAX_CTR_IETF {
|
||||||
|
panic(ERR_CTR_EXHAUSTED)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12])
|
||||||
|
if _, carry := bits.add_u64(ctr, u64(nr_blocks), 0); carry != 0 {
|
||||||
|
panic(ERR_CTR_EXHAUSTED)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,360 @@
|
|||||||
|
package chacha20_ref
|
||||||
|
|
||||||
|
import "core:crypto/_chacha20"
|
||||||
|
import "core:encoding/endian"
|
||||||
|
import "core:math/bits"
|
||||||
|
|
||||||
|
stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int) {
|
||||||
|
// Enforce the maximum consumed keystream per IV.
|
||||||
|
_chacha20.check_counter_limit(ctx, nr_blocks)
|
||||||
|
|
||||||
|
dst, src := dst, src
|
||||||
|
x := &ctx._s
|
||||||
|
for n := 0; n < nr_blocks; n = n + 1 {
|
||||||
|
x0, x1, x2, x3 :=
|
||||||
|
_chacha20.SIGMA_0, _chacha20.SIGMA_1, _chacha20.SIGMA_2, _chacha20.SIGMA_3
|
||||||
|
x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 :=
|
||||||
|
x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]
|
||||||
|
|
||||||
|
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||||
|
// Even when forcing inlining manually inlining all of
|
||||||
|
// these is decently faster.
|
||||||
|
|
||||||
|
// quarterround(x, 0, 4, 8, 12)
|
||||||
|
x0 += x4
|
||||||
|
x12 ~= x0
|
||||||
|
x12 = bits.rotate_left32(x12, 16)
|
||||||
|
x8 += x12
|
||||||
|
x4 ~= x8
|
||||||
|
x4 = bits.rotate_left32(x4, 12)
|
||||||
|
x0 += x4
|
||||||
|
x12 ~= x0
|
||||||
|
x12 = bits.rotate_left32(x12, 8)
|
||||||
|
x8 += x12
|
||||||
|
x4 ~= x8
|
||||||
|
x4 = bits.rotate_left32(x4, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 1, 5, 9, 13)
|
||||||
|
x1 += x5
|
||||||
|
x13 ~= x1
|
||||||
|
x13 = bits.rotate_left32(x13, 16)
|
||||||
|
x9 += x13
|
||||||
|
x5 ~= x9
|
||||||
|
x5 = bits.rotate_left32(x5, 12)
|
||||||
|
x1 += x5
|
||||||
|
x13 ~= x1
|
||||||
|
x13 = bits.rotate_left32(x13, 8)
|
||||||
|
x9 += x13
|
||||||
|
x5 ~= x9
|
||||||
|
x5 = bits.rotate_left32(x5, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 2, 6, 10, 14)
|
||||||
|
x2 += x6
|
||||||
|
x14 ~= x2
|
||||||
|
x14 = bits.rotate_left32(x14, 16)
|
||||||
|
x10 += x14
|
||||||
|
x6 ~= x10
|
||||||
|
x6 = bits.rotate_left32(x6, 12)
|
||||||
|
x2 += x6
|
||||||
|
x14 ~= x2
|
||||||
|
x14 = bits.rotate_left32(x14, 8)
|
||||||
|
x10 += x14
|
||||||
|
x6 ~= x10
|
||||||
|
x6 = bits.rotate_left32(x6, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 3, 7, 11, 15)
|
||||||
|
x3 += x7
|
||||||
|
x15 ~= x3
|
||||||
|
x15 = bits.rotate_left32(x15, 16)
|
||||||
|
x11 += x15
|
||||||
|
x7 ~= x11
|
||||||
|
x7 = bits.rotate_left32(x7, 12)
|
||||||
|
x3 += x7
|
||||||
|
x15 ~= x3
|
||||||
|
x15 = bits.rotate_left32(x15, 8)
|
||||||
|
x11 += x15
|
||||||
|
x7 ~= x11
|
||||||
|
x7 = bits.rotate_left32(x7, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 0, 5, 10, 15)
|
||||||
|
x0 += x5
|
||||||
|
x15 ~= x0
|
||||||
|
x15 = bits.rotate_left32(x15, 16)
|
||||||
|
x10 += x15
|
||||||
|
x5 ~= x10
|
||||||
|
x5 = bits.rotate_left32(x5, 12)
|
||||||
|
x0 += x5
|
||||||
|
x15 ~= x0
|
||||||
|
x15 = bits.rotate_left32(x15, 8)
|
||||||
|
x10 += x15
|
||||||
|
x5 ~= x10
|
||||||
|
x5 = bits.rotate_left32(x5, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 1, 6, 11, 12)
|
||||||
|
x1 += x6
|
||||||
|
x12 ~= x1
|
||||||
|
x12 = bits.rotate_left32(x12, 16)
|
||||||
|
x11 += x12
|
||||||
|
x6 ~= x11
|
||||||
|
x6 = bits.rotate_left32(x6, 12)
|
||||||
|
x1 += x6
|
||||||
|
x12 ~= x1
|
||||||
|
x12 = bits.rotate_left32(x12, 8)
|
||||||
|
x11 += x12
|
||||||
|
x6 ~= x11
|
||||||
|
x6 = bits.rotate_left32(x6, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 2, 7, 8, 13)
|
||||||
|
x2 += x7
|
||||||
|
x13 ~= x2
|
||||||
|
x13 = bits.rotate_left32(x13, 16)
|
||||||
|
x8 += x13
|
||||||
|
x7 ~= x8
|
||||||
|
x7 = bits.rotate_left32(x7, 12)
|
||||||
|
x2 += x7
|
||||||
|
x13 ~= x2
|
||||||
|
x13 = bits.rotate_left32(x13, 8)
|
||||||
|
x8 += x13
|
||||||
|
x7 ~= x8
|
||||||
|
x7 = bits.rotate_left32(x7, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 3, 4, 9, 14)
|
||||||
|
x3 += x4
|
||||||
|
x14 ~= x3
|
||||||
|
x14 = bits.rotate_left32(x14, 16)
|
||||||
|
x9 += x14
|
||||||
|
x4 ~= x9
|
||||||
|
x4 = bits.rotate_left32(x4, 12)
|
||||||
|
x3 += x4
|
||||||
|
x14 ~= x3
|
||||||
|
x14 = bits.rotate_left32(x14, 8)
|
||||||
|
x9 += x14
|
||||||
|
x4 ~= x9
|
||||||
|
x4 = bits.rotate_left32(x4, 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
x0 += _chacha20.SIGMA_0
|
||||||
|
x1 += _chacha20.SIGMA_1
|
||||||
|
x2 += _chacha20.SIGMA_2
|
||||||
|
x3 += _chacha20.SIGMA_3
|
||||||
|
x4 += x[4]
|
||||||
|
x5 += x[5]
|
||||||
|
x6 += x[6]
|
||||||
|
x7 += x[7]
|
||||||
|
x8 += x[8]
|
||||||
|
x9 += x[9]
|
||||||
|
x10 += x[10]
|
||||||
|
x11 += x[11]
|
||||||
|
x12 += x[12]
|
||||||
|
x13 += x[13]
|
||||||
|
x14 += x[14]
|
||||||
|
x15 += x[15]
|
||||||
|
|
||||||
|
// - The caller(s) ensure that src/dst are valid.
|
||||||
|
// - The compiler knows if the target is picky about alignment.
|
||||||
|
|
||||||
|
#no_bounds_check {
|
||||||
|
if src != nil {
|
||||||
|
endian.unchecked_put_u32le(dst[0:4], endian.unchecked_get_u32le(src[0:4]) ~ x0)
|
||||||
|
endian.unchecked_put_u32le(dst[4:8], endian.unchecked_get_u32le(src[4:8]) ~ x1)
|
||||||
|
endian.unchecked_put_u32le(dst[8:12], endian.unchecked_get_u32le(src[8:12]) ~ x2)
|
||||||
|
endian.unchecked_put_u32le(dst[12:16], endian.unchecked_get_u32le(src[12:16]) ~ x3)
|
||||||
|
endian.unchecked_put_u32le(dst[16:20], endian.unchecked_get_u32le(src[16:20]) ~ x4)
|
||||||
|
endian.unchecked_put_u32le(dst[20:24], endian.unchecked_get_u32le(src[20:24]) ~ x5)
|
||||||
|
endian.unchecked_put_u32le(dst[24:28], endian.unchecked_get_u32le(src[24:28]) ~ x6)
|
||||||
|
endian.unchecked_put_u32le(dst[28:32], endian.unchecked_get_u32le(src[28:32]) ~ x7)
|
||||||
|
endian.unchecked_put_u32le(dst[32:36], endian.unchecked_get_u32le(src[32:36]) ~ x8)
|
||||||
|
endian.unchecked_put_u32le(dst[36:40], endian.unchecked_get_u32le(src[36:40]) ~ x9)
|
||||||
|
endian.unchecked_put_u32le(
|
||||||
|
dst[40:44],
|
||||||
|
endian.unchecked_get_u32le(src[40:44]) ~ x10,
|
||||||
|
)
|
||||||
|
endian.unchecked_put_u32le(
|
||||||
|
dst[44:48],
|
||||||
|
endian.unchecked_get_u32le(src[44:48]) ~ x11,
|
||||||
|
)
|
||||||
|
endian.unchecked_put_u32le(
|
||||||
|
dst[48:52],
|
||||||
|
endian.unchecked_get_u32le(src[48:52]) ~ x12,
|
||||||
|
)
|
||||||
|
endian.unchecked_put_u32le(
|
||||||
|
dst[52:56],
|
||||||
|
endian.unchecked_get_u32le(src[52:56]) ~ x13,
|
||||||
|
)
|
||||||
|
endian.unchecked_put_u32le(
|
||||||
|
dst[56:60],
|
||||||
|
endian.unchecked_get_u32le(src[56:60]) ~ x14,
|
||||||
|
)
|
||||||
|
endian.unchecked_put_u32le(
|
||||||
|
dst[60:64],
|
||||||
|
endian.unchecked_get_u32le(src[60:64]) ~ x15,
|
||||||
|
)
|
||||||
|
src = src[_chacha20.BLOCK_SIZE:]
|
||||||
|
} else {
|
||||||
|
endian.unchecked_put_u32le(dst[0:4], x0)
|
||||||
|
endian.unchecked_put_u32le(dst[4:8], x1)
|
||||||
|
endian.unchecked_put_u32le(dst[8:12], x2)
|
||||||
|
endian.unchecked_put_u32le(dst[12:16], x3)
|
||||||
|
endian.unchecked_put_u32le(dst[16:20], x4)
|
||||||
|
endian.unchecked_put_u32le(dst[20:24], x5)
|
||||||
|
endian.unchecked_put_u32le(dst[24:28], x6)
|
||||||
|
endian.unchecked_put_u32le(dst[28:32], x7)
|
||||||
|
endian.unchecked_put_u32le(dst[32:36], x8)
|
||||||
|
endian.unchecked_put_u32le(dst[36:40], x9)
|
||||||
|
endian.unchecked_put_u32le(dst[40:44], x10)
|
||||||
|
endian.unchecked_put_u32le(dst[44:48], x11)
|
||||||
|
endian.unchecked_put_u32le(dst[48:52], x12)
|
||||||
|
endian.unchecked_put_u32le(dst[52:56], x13)
|
||||||
|
endian.unchecked_put_u32le(dst[56:60], x14)
|
||||||
|
endian.unchecked_put_u32le(dst[60:64], x15)
|
||||||
|
}
|
||||||
|
dst = dst[_chacha20.BLOCK_SIZE:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the counter. Overflow checking is done upon
|
||||||
|
// entry into the routine, so a 64-bit increment safely
|
||||||
|
// covers both cases.
|
||||||
|
new_ctr := ((u64(ctx._s[13]) << 32) | u64(ctx._s[12])) + 1
|
||||||
|
x[12] = u32(new_ctr)
|
||||||
|
x[13] = u32(new_ctr >> 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hchacha20 :: proc "contextless" (dst, key, iv: []byte) {
|
||||||
|
x0, x1, x2, x3 := _chacha20.SIGMA_0, _chacha20.SIGMA_1, _chacha20.SIGMA_2, _chacha20.SIGMA_3
|
||||||
|
x4 := endian.unchecked_get_u32le(key[0:4])
|
||||||
|
x5 := endian.unchecked_get_u32le(key[4:8])
|
||||||
|
x6 := endian.unchecked_get_u32le(key[8:12])
|
||||||
|
x7 := endian.unchecked_get_u32le(key[12:16])
|
||||||
|
x8 := endian.unchecked_get_u32le(key[16:20])
|
||||||
|
x9 := endian.unchecked_get_u32le(key[20:24])
|
||||||
|
x10 := endian.unchecked_get_u32le(key[24:28])
|
||||||
|
x11 := endian.unchecked_get_u32le(key[28:32])
|
||||||
|
x12 := endian.unchecked_get_u32le(iv[0:4])
|
||||||
|
x13 := endian.unchecked_get_u32le(iv[4:8])
|
||||||
|
x14 := endian.unchecked_get_u32le(iv[8:12])
|
||||||
|
x15 := endian.unchecked_get_u32le(iv[12:16])
|
||||||
|
|
||||||
|
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||||
|
// quarterround(x, 0, 4, 8, 12)
|
||||||
|
x0 += x4
|
||||||
|
x12 ~= x0
|
||||||
|
x12 = bits.rotate_left32(x12, 16)
|
||||||
|
x8 += x12
|
||||||
|
x4 ~= x8
|
||||||
|
x4 = bits.rotate_left32(x4, 12)
|
||||||
|
x0 += x4
|
||||||
|
x12 ~= x0
|
||||||
|
x12 = bits.rotate_left32(x12, 8)
|
||||||
|
x8 += x12
|
||||||
|
x4 ~= x8
|
||||||
|
x4 = bits.rotate_left32(x4, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 1, 5, 9, 13)
|
||||||
|
x1 += x5
|
||||||
|
x13 ~= x1
|
||||||
|
x13 = bits.rotate_left32(x13, 16)
|
||||||
|
x9 += x13
|
||||||
|
x5 ~= x9
|
||||||
|
x5 = bits.rotate_left32(x5, 12)
|
||||||
|
x1 += x5
|
||||||
|
x13 ~= x1
|
||||||
|
x13 = bits.rotate_left32(x13, 8)
|
||||||
|
x9 += x13
|
||||||
|
x5 ~= x9
|
||||||
|
x5 = bits.rotate_left32(x5, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 2, 6, 10, 14)
|
||||||
|
x2 += x6
|
||||||
|
x14 ~= x2
|
||||||
|
x14 = bits.rotate_left32(x14, 16)
|
||||||
|
x10 += x14
|
||||||
|
x6 ~= x10
|
||||||
|
x6 = bits.rotate_left32(x6, 12)
|
||||||
|
x2 += x6
|
||||||
|
x14 ~= x2
|
||||||
|
x14 = bits.rotate_left32(x14, 8)
|
||||||
|
x10 += x14
|
||||||
|
x6 ~= x10
|
||||||
|
x6 = bits.rotate_left32(x6, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 3, 7, 11, 15)
|
||||||
|
x3 += x7
|
||||||
|
x15 ~= x3
|
||||||
|
x15 = bits.rotate_left32(x15, 16)
|
||||||
|
x11 += x15
|
||||||
|
x7 ~= x11
|
||||||
|
x7 = bits.rotate_left32(x7, 12)
|
||||||
|
x3 += x7
|
||||||
|
x15 ~= x3
|
||||||
|
x15 = bits.rotate_left32(x15, 8)
|
||||||
|
x11 += x15
|
||||||
|
x7 ~= x11
|
||||||
|
x7 = bits.rotate_left32(x7, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 0, 5, 10, 15)
|
||||||
|
x0 += x5
|
||||||
|
x15 ~= x0
|
||||||
|
x15 = bits.rotate_left32(x15, 16)
|
||||||
|
x10 += x15
|
||||||
|
x5 ~= x10
|
||||||
|
x5 = bits.rotate_left32(x5, 12)
|
||||||
|
x0 += x5
|
||||||
|
x15 ~= x0
|
||||||
|
x15 = bits.rotate_left32(x15, 8)
|
||||||
|
x10 += x15
|
||||||
|
x5 ~= x10
|
||||||
|
x5 = bits.rotate_left32(x5, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 1, 6, 11, 12)
|
||||||
|
x1 += x6
|
||||||
|
x12 ~= x1
|
||||||
|
x12 = bits.rotate_left32(x12, 16)
|
||||||
|
x11 += x12
|
||||||
|
x6 ~= x11
|
||||||
|
x6 = bits.rotate_left32(x6, 12)
|
||||||
|
x1 += x6
|
||||||
|
x12 ~= x1
|
||||||
|
x12 = bits.rotate_left32(x12, 8)
|
||||||
|
x11 += x12
|
||||||
|
x6 ~= x11
|
||||||
|
x6 = bits.rotate_left32(x6, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 2, 7, 8, 13)
|
||||||
|
x2 += x7
|
||||||
|
x13 ~= x2
|
||||||
|
x13 = bits.rotate_left32(x13, 16)
|
||||||
|
x8 += x13
|
||||||
|
x7 ~= x8
|
||||||
|
x7 = bits.rotate_left32(x7, 12)
|
||||||
|
x2 += x7
|
||||||
|
x13 ~= x2
|
||||||
|
x13 = bits.rotate_left32(x13, 8)
|
||||||
|
x8 += x13
|
||||||
|
x7 ~= x8
|
||||||
|
x7 = bits.rotate_left32(x7, 7)
|
||||||
|
|
||||||
|
// quarterround(x, 3, 4, 9, 14)
|
||||||
|
x3 += x4
|
||||||
|
x14 ~= x3
|
||||||
|
x14 = bits.rotate_left32(x14, 16)
|
||||||
|
x9 += x14
|
||||||
|
x4 ~= x9
|
||||||
|
x4 = bits.rotate_left32(x4, 12)
|
||||||
|
x3 += x4
|
||||||
|
x14 ~= x3
|
||||||
|
x14 = bits.rotate_left32(x14, 8)
|
||||||
|
x9 += x14
|
||||||
|
x4 ~= x9
|
||||||
|
x4 = bits.rotate_left32(x4, 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
endian.unchecked_put_u32le(dst[0:4], x0)
|
||||||
|
endian.unchecked_put_u32le(dst[4:8], x1)
|
||||||
|
endian.unchecked_put_u32le(dst[8:12], x2)
|
||||||
|
endian.unchecked_put_u32le(dst[12:16], x3)
|
||||||
|
endian.unchecked_put_u32le(dst[16:20], x12)
|
||||||
|
endian.unchecked_put_u32le(dst[20:24], x13)
|
||||||
|
endian.unchecked_put_u32le(dst[24:28], x14)
|
||||||
|
endian.unchecked_put_u32le(dst[28:32], x15)
|
||||||
|
}
|
||||||
@@ -0,0 +1,481 @@
|
|||||||
|
package chacha20_simd128
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "core:crypto/_chacha20"
|
||||||
|
import "core:simd"
|
||||||
|
@(require) import "core:sys/info"
|
||||||
|
|
||||||
|
// Portable 128-bit `core:simd` implementation.
|
||||||
|
//
|
||||||
|
// This is loosely based on Ted Krovetz's public domain C intrinsic
|
||||||
|
// implementation.
|
||||||
|
//
|
||||||
|
// This is written to perform adequately on any target that has "enough"
|
||||||
|
// 128-bit vector registers, the current thought is that 4 blocks at at
|
||||||
|
// time is reasonable for amd64, though Ted's code is more conservative.
|
||||||
|
//
|
||||||
|
// See:
|
||||||
|
// supercop-20230530/crypto_stream/chacha20/krovetz/vec128
|
||||||
|
|
||||||
|
// Ensure the compiler emits SIMD instructions. This is a minimum, and
|
||||||
|
// setting the microarchitecture at compile time will allow for better
|
||||||
|
// code gen when applicable (eg: AVX). This is somewhat redundant with
|
||||||
|
// the default microarchitecture configurations.
|
||||||
|
when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 {
|
||||||
|
@(private = "file")
|
||||||
|
TARGET_SIMD_FEATURES :: "neon"
|
||||||
|
} else when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
|
||||||
|
// Note: LLVM appears to be smart enough to use PSHUFB despite not
|
||||||
|
// explicitly using simd.u8x16 shuffles.
|
||||||
|
@(private = "file")
|
||||||
|
TARGET_SIMD_FEATURES :: "sse2,ssse3"
|
||||||
|
} else {
|
||||||
|
@(private = "file")
|
||||||
|
TARGET_SIMD_FEATURES :: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_7L: simd.u32x4 : {7, 7, 7, 7}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_7R: simd.u32x4 : {25, 25, 25, 25}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_12L: simd.u32x4 : {12, 12, 12, 12}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_12R: simd.u32x4 : {20, 20, 20, 20}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_8L: simd.u32x4 : {8, 8, 8, 8}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_8R: simd.u32x4 : {24, 24, 24, 24}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_16: simd.u32x4 : {16, 16, 16, 16}
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Big {
|
||||||
|
@(private = "file")
|
||||||
|
_increment_counter :: #force_inline proc "contextless" (ctx: ^_chacha20.Context) -> simd.u32x4 {
|
||||||
|
// In the Big Endian case, the low and high portions in the vector
|
||||||
|
// are flipped, so the 64-bit addition can't be done with a simple
|
||||||
|
// vector add.
|
||||||
|
x := &ctx._s
|
||||||
|
|
||||||
|
new_ctr := ((u64(ctx._s[13]) << 32) | u64(ctx._s[12])) + 1
|
||||||
|
x[12] = u32(new_ctr)
|
||||||
|
x[13] = u32(new_ctr >> 32)
|
||||||
|
|
||||||
|
return intrinsics.unaligned_load(transmute(^simd.u32x4)&x[12])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the endian-ness of the components of a u32x4 vector, for
|
||||||
|
// the purposes of output.
|
||||||
|
@(private = "file")
|
||||||
|
_byteswap_u32x4 :: #force_inline proc "contextless" (v: simd.u32x4) -> simd.u32x4 {
|
||||||
|
return(
|
||||||
|
transmute(simd.u32x4)simd.shuffle(
|
||||||
|
transmute(simd.u8x16)v,
|
||||||
|
transmute(simd.u8x16)v,
|
||||||
|
3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@(private = "file")
|
||||||
|
_VEC_ONE: simd.u64x2 : {1, 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_dq_round_simd128 :: #force_inline proc "contextless" (
|
||||||
|
v0, v1, v2, v3: simd.u32x4,
|
||||||
|
) -> (
|
||||||
|
simd.u32x4,
|
||||||
|
simd.u32x4,
|
||||||
|
simd.u32x4,
|
||||||
|
simd.u32x4,
|
||||||
|
) {
|
||||||
|
v0, v1, v2, v3 := v0, v1, v2, v3
|
||||||
|
|
||||||
|
// a += b; d ^= a; d = ROTW16(d);
|
||||||
|
v0 = simd.add(v0, v1)
|
||||||
|
v3 = simd.bit_xor(v3, v0)
|
||||||
|
v3 = simd.bit_xor(simd.shl(v3, _ROT_16), simd.shr(v3, _ROT_16))
|
||||||
|
|
||||||
|
// c += d; b ^= c; b = ROTW12(b);
|
||||||
|
v2 = simd.add(v2, v3)
|
||||||
|
v1 = simd.bit_xor(v1, v2)
|
||||||
|
v1 = simd.bit_xor(simd.shl(v1, _ROT_12L), simd.shr(v1, _ROT_12R))
|
||||||
|
|
||||||
|
// a += b; d ^= a; d = ROTW8(d);
|
||||||
|
v0 = simd.add(v0, v1)
|
||||||
|
v3 = simd.bit_xor(v3, v0)
|
||||||
|
v3 = simd.bit_xor(simd.shl(v3, _ROT_8L), simd.shr(v3, _ROT_8R))
|
||||||
|
|
||||||
|
// c += d; b ^= c; b = ROTW7(b);
|
||||||
|
v2 = simd.add(v2, v3)
|
||||||
|
v1 = simd.bit_xor(v1, v2)
|
||||||
|
v1 = simd.bit_xor(simd.shl(v1, _ROT_7L), simd.shr(v1, _ROT_7R))
|
||||||
|
|
||||||
|
// b = ROTV1(b); c = ROTV2(c); d = ROTV3(d);
|
||||||
|
v1 = simd.shuffle(v1, v1, 1, 2, 3, 0)
|
||||||
|
v2 = simd.shuffle(v2, v2, 2, 3, 0, 1)
|
||||||
|
v3 = simd.shuffle(v3, v3, 3, 0, 1, 2)
|
||||||
|
|
||||||
|
// a += b; d ^= a; d = ROTW16(d);
|
||||||
|
v0 = simd.add(v0, v1)
|
||||||
|
v3 = simd.bit_xor(v3, v0)
|
||||||
|
v3 = simd.bit_xor(simd.shl(v3, _ROT_16), simd.shr(v3, _ROT_16))
|
||||||
|
|
||||||
|
// c += d; b ^= c; b = ROTW12(b);
|
||||||
|
v2 = simd.add(v2, v3)
|
||||||
|
v1 = simd.bit_xor(v1, v2)
|
||||||
|
v1 = simd.bit_xor(simd.shl(v1, _ROT_12L), simd.shr(v1, _ROT_12R))
|
||||||
|
|
||||||
|
// a += b; d ^= a; d = ROTW8(d);
|
||||||
|
v0 = simd.add(v0, v1)
|
||||||
|
v3 = simd.bit_xor(v3, v0)
|
||||||
|
v3 = simd.bit_xor(simd.shl(v3, _ROT_8L), simd.shr(v3, _ROT_8R))
|
||||||
|
|
||||||
|
// c += d; b ^= c; b = ROTW7(b);
|
||||||
|
v2 = simd.add(v2, v3)
|
||||||
|
v1 = simd.bit_xor(v1, v2)
|
||||||
|
v1 = simd.bit_xor(simd.shl(v1, _ROT_7L), simd.shr(v1, _ROT_7R))
|
||||||
|
|
||||||
|
// b = ROTV3(b); c = ROTV2(c); d = ROTV1(d);
|
||||||
|
v1 = simd.shuffle(v1, v1, 3, 0, 1, 2)
|
||||||
|
v2 = simd.shuffle(v2, v2, 2, 3, 0, 1)
|
||||||
|
v3 = simd.shuffle(v3, v3, 1, 2, 3, 0)
|
||||||
|
|
||||||
|
return v0, v1, v2, v3
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_add_state_simd128 :: #force_inline proc "contextless" (
|
||||||
|
v0, v1, v2, v3, s0, s1, s2, s3: simd.u32x4,
|
||||||
|
) -> (
|
||||||
|
simd.u32x4,
|
||||||
|
simd.u32x4,
|
||||||
|
simd.u32x4,
|
||||||
|
simd.u32x4,
|
||||||
|
) {
|
||||||
|
v0, v1, v2, v3 := v0, v1, v2, v3
|
||||||
|
|
||||||
|
v0 = simd.add(v0, s0)
|
||||||
|
v1 = simd.add(v1, s1)
|
||||||
|
v2 = simd.add(v2, s2)
|
||||||
|
v3 = simd.add(v3, s3)
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Big {
|
||||||
|
v0 = _byteswap_u32x4(v0)
|
||||||
|
v1 = _byteswap_u32x4(v1)
|
||||||
|
v2 = _byteswap_u32x4(v2)
|
||||||
|
v3 = _byteswap_u32x4(v3)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v0, v1, v2, v3
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_xor_simd128 :: #force_inline proc "contextless" (
|
||||||
|
src: [^]simd.u32x4,
|
||||||
|
v0, v1, v2, v3: simd.u32x4,
|
||||||
|
) -> (
|
||||||
|
simd.u32x4,
|
||||||
|
simd.u32x4,
|
||||||
|
simd.u32x4,
|
||||||
|
simd.u32x4,
|
||||||
|
) {
|
||||||
|
v0, v1, v2, v3 := v0, v1, v2, v3
|
||||||
|
|
||||||
|
v0 = simd.bit_xor(v0, intrinsics.unaligned_load((^simd.u32x4)(src[0:])))
|
||||||
|
v1 = simd.bit_xor(v1, intrinsics.unaligned_load((^simd.u32x4)(src[1:])))
|
||||||
|
v2 = simd.bit_xor(v2, intrinsics.unaligned_load((^simd.u32x4)(src[2:])))
|
||||||
|
v3 = simd.bit_xor(v3, intrinsics.unaligned_load((^simd.u32x4)(src[3:])))
|
||||||
|
|
||||||
|
return v0, v1, v2, v3
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_store_simd128 :: #force_inline proc "contextless" (
|
||||||
|
dst: [^]simd.u32x4,
|
||||||
|
v0, v1, v2, v3: simd.u32x4,
|
||||||
|
) {
|
||||||
|
intrinsics.unaligned_store((^simd.u32x4)(dst[0:]), v0)
|
||||||
|
intrinsics.unaligned_store((^simd.u32x4)(dst[1:]), v1)
|
||||||
|
intrinsics.unaligned_store((^simd.u32x4)(dst[2:]), v2)
|
||||||
|
intrinsics.unaligned_store((^simd.u32x4)(dst[3:]), v3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// is_performant returns true iff the target and current host both support
|
||||||
|
// "enough" 128-bit SIMD to make this implementation performant.
|
||||||
|
is_performant :: proc "contextless" () -> bool {
|
||||||
|
when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 || ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
|
||||||
|
when ODIN_ARCH == .arm64 || ODIN_ARCH == .arm32 {
|
||||||
|
req_features :: info.CPU_Features{.asimd}
|
||||||
|
} else when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
|
||||||
|
req_features :: info.CPU_Features{.sse2, .ssse3}
|
||||||
|
}
|
||||||
|
|
||||||
|
features, ok := info.cpu_features.?
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return features >= req_features
|
||||||
|
} else when ODIN_ARCH == .wasm64p32 || ODIN_ARCH == .wasm32 {
|
||||||
|
return intrinsics.has_target_feature("simd128")
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(enable_target_feature = TARGET_SIMD_FEATURES)
|
||||||
|
stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int) {
|
||||||
|
// Enforce the maximum consumed keystream per IV.
|
||||||
|
_chacha20.check_counter_limit(ctx, nr_blocks)
|
||||||
|
|
||||||
|
dst_v := ([^]simd.u32x4)(raw_data(dst))
|
||||||
|
src_v := ([^]simd.u32x4)(raw_data(src))
|
||||||
|
|
||||||
|
x := &ctx._s
|
||||||
|
n := nr_blocks
|
||||||
|
|
||||||
|
// The state vector is an array of uint32s in native byte-order.
|
||||||
|
x_v := ([^]simd.u32x4)(raw_data(x))
|
||||||
|
s0 := intrinsics.unaligned_load((^simd.u32x4)(x_v[0:]))
|
||||||
|
s1 := intrinsics.unaligned_load((^simd.u32x4)(x_v[1:]))
|
||||||
|
s2 := intrinsics.unaligned_load((^simd.u32x4)(x_v[2:]))
|
||||||
|
s3 := intrinsics.unaligned_load((^simd.u32x4)(x_v[3:]))
|
||||||
|
|
||||||
|
// 8 blocks at a time.
|
||||||
|
//
|
||||||
|
// Note: This is only worth it on Aarch64.
|
||||||
|
when ODIN_ARCH == .arm64 {
|
||||||
|
for ; n >= 8; n = n - 8 {
|
||||||
|
v0, v1, v2, v3 := s0, s1, s2, s3
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
s7 := transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s3, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s7 := _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
v4, v5, v6, v7 := s0, s1, s2, s7
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
s11 := transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s7, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s11 := _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
v8, v9, v10, v11 := s0, s1, s2, s11
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
s15 := transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s11, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s15 := _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
v12, v13, v14, v15 := s0, s1, s2, s15
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
s19 := transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s15, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s19 := _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
v16, v17, v18, v19 := s0, s1, s2, s19
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
s23 := transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s19, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s23 := _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
v20, v21, v22, v23 := s0, s1, s2, s23
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
s27 := transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s23, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s27 := _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
v24, v25, v26, v27 := s0, s1, s2, s27
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
s31 := transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s27, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s31 := _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
v28, v29, v30, v31 := s0, s1, s2, s31
|
||||||
|
|
||||||
|
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||||
|
v0, v1, v2, v3 = _dq_round_simd128(v0, v1, v2, v3)
|
||||||
|
v4, v5, v6, v7 = _dq_round_simd128(v4, v5, v6, v7)
|
||||||
|
v8, v9, v10, v11 = _dq_round_simd128(v8, v9, v10, v11)
|
||||||
|
v12, v13, v14, v15 = _dq_round_simd128(v12, v13, v14, v15)
|
||||||
|
v16, v17, v18, v19 = _dq_round_simd128(v16, v17, v18, v19)
|
||||||
|
v20, v21, v22, v23 = _dq_round_simd128(v20, v21, v22, v23)
|
||||||
|
v24, v25, v26, v27 = _dq_round_simd128(v24, v25, v26, v27)
|
||||||
|
v28, v29, v30, v31 = _dq_round_simd128(v28, v29, v30, v31)
|
||||||
|
}
|
||||||
|
|
||||||
|
v0, v1, v2, v3 = _add_state_simd128(v0, v1, v2, v3, s0, s1, s2, s3)
|
||||||
|
v4, v5, v6, v7 = _add_state_simd128(v4, v5, v6, v7, s0, s1, s2, s7)
|
||||||
|
v8, v9, v10, v11 = _add_state_simd128(v8, v9, v10, v11, s0, s1, s2, s11)
|
||||||
|
v12, v13, v14, v15 = _add_state_simd128(v12, v13, v14, v15, s0, s1, s2, s15)
|
||||||
|
v16, v17, v18, v19 = _add_state_simd128(v16, v17, v18, v19, s0, s1, s2, s19)
|
||||||
|
v20, v21, v22, v23 = _add_state_simd128(v20, v21, v22, v23, s0, s1, s2, s23)
|
||||||
|
v24, v25, v26, v27 = _add_state_simd128(v24, v25, v26, v27, s0, s1, s2, s27)
|
||||||
|
v28, v29, v30, v31 = _add_state_simd128(v28, v29, v30, v31, s0, s1, s2, s31)
|
||||||
|
|
||||||
|
#no_bounds_check {
|
||||||
|
if src != nil {
|
||||||
|
v0, v1, v2, v3 = _xor_simd128(src_v, v0, v1, v2, v3)
|
||||||
|
v4, v5, v6, v7 = _xor_simd128(src_v[4:], v4, v5, v6, v7)
|
||||||
|
v8, v9, v10, v11 = _xor_simd128(src_v[8:], v8, v9, v10, v11)
|
||||||
|
v12, v13, v14, v15 = _xor_simd128(src_v[12:], v12, v13, v14, v15)
|
||||||
|
v16, v17, v18, v19 = _xor_simd128(src_v[16:], v16, v17, v18, v19)
|
||||||
|
v20, v21, v22, v23 = _xor_simd128(src_v[20:], v20, v21, v22, v23)
|
||||||
|
v24, v25, v26, v27 = _xor_simd128(src_v[24:], v24, v25, v26, v27)
|
||||||
|
v28, v29, v30, v31 = _xor_simd128(src_v[28:], v28, v29, v30, v31)
|
||||||
|
src_v = src_v[32:]
|
||||||
|
}
|
||||||
|
|
||||||
|
_store_simd128(dst_v, v0, v1, v2, v3)
|
||||||
|
_store_simd128(dst_v[4:], v4, v5, v6, v7)
|
||||||
|
_store_simd128(dst_v[8:], v8, v9, v10, v11)
|
||||||
|
_store_simd128(dst_v[12:], v12, v13, v14, v15)
|
||||||
|
_store_simd128(dst_v[16:], v16, v17, v18, v19)
|
||||||
|
_store_simd128(dst_v[20:], v20, v21, v22, v23)
|
||||||
|
_store_simd128(dst_v[24:], v24, v25, v26, v27)
|
||||||
|
_store_simd128(dst_v[28:], v28, v29, v30, v31)
|
||||||
|
dst_v = dst_v[32:]
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
// s31 holds the most current counter, so `s3 = s31 + 1`.
|
||||||
|
s3 = transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s31, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s3 = _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4 blocks at a time.
|
||||||
|
//
|
||||||
|
// Note: The i386 target lacks the required number of registers
|
||||||
|
// for this to be performant, so it is skipped.
|
||||||
|
when ODIN_ARCH != .i386 {
|
||||||
|
for ; n >= 4; n = n - 4 {
|
||||||
|
v0, v1, v2, v3 := s0, s1, s2, s3
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
s7 := transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s3, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s7 := _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
v4, v5, v6, v7 := s0, s1, s2, s7
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
s11 := transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s7, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s11 := _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
v8, v9, v10, v11 := s0, s1, s2, s11
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
s15 := transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s11, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s15 := _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
v12, v13, v14, v15 := s0, s1, s2, s15
|
||||||
|
|
||||||
|
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||||
|
v0, v1, v2, v3 = _dq_round_simd128(v0, v1, v2, v3)
|
||||||
|
v4, v5, v6, v7 = _dq_round_simd128(v4, v5, v6, v7)
|
||||||
|
v8, v9, v10, v11 = _dq_round_simd128(v8, v9, v10, v11)
|
||||||
|
v12, v13, v14, v15 = _dq_round_simd128(v12, v13, v14, v15)
|
||||||
|
}
|
||||||
|
|
||||||
|
v0, v1, v2, v3 = _add_state_simd128(v0, v1, v2, v3, s0, s1, s2, s3)
|
||||||
|
v4, v5, v6, v7 = _add_state_simd128(v4, v5, v6, v7, s0, s1, s2, s7)
|
||||||
|
v8, v9, v10, v11 = _add_state_simd128(v8, v9, v10, v11, s0, s1, s2, s11)
|
||||||
|
v12, v13, v14, v15 = _add_state_simd128(v12, v13, v14, v15, s0, s1, s2, s15)
|
||||||
|
|
||||||
|
#no_bounds_check {
|
||||||
|
if src != nil {
|
||||||
|
v0, v1, v2, v3 = _xor_simd128(src_v, v0, v1, v2, v3)
|
||||||
|
v4, v5, v6, v7 = _xor_simd128(src_v[4:], v4, v5, v6, v7)
|
||||||
|
v8, v9, v10, v11 = _xor_simd128(src_v[8:], v8, v9, v10, v11)
|
||||||
|
v12, v13, v14, v15 = _xor_simd128(src_v[12:], v12, v13, v14, v15)
|
||||||
|
src_v = src_v[16:]
|
||||||
|
}
|
||||||
|
|
||||||
|
_store_simd128(dst_v, v0, v1, v2, v3)
|
||||||
|
_store_simd128(dst_v[4:], v4, v5, v6, v7)
|
||||||
|
_store_simd128(dst_v[8:], v8, v9, v10, v11)
|
||||||
|
_store_simd128(dst_v[12:], v12, v13, v14, v15)
|
||||||
|
dst_v = dst_v[16:]
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
// s15 holds the most current counter, so `s3 = s15 + 1`.
|
||||||
|
s3 = transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s15, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s3 = _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 block at a time.
|
||||||
|
for ; n > 0; n = n - 1 {
|
||||||
|
v0, v1, v2, v3 := s0, s1, s2, s3
|
||||||
|
|
||||||
|
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||||
|
v0, v1, v2, v3 = _dq_round_simd128(v0, v1, v2, v3)
|
||||||
|
}
|
||||||
|
v0, v1, v2, v3 = _add_state_simd128(v0, v1, v2, v3, s0, s1, s2, s3)
|
||||||
|
|
||||||
|
#no_bounds_check {
|
||||||
|
if src != nil {
|
||||||
|
v0, v1, v2, v3 = _xor_simd128(src_v, v0, v1, v2, v3)
|
||||||
|
src_v = src_v[4:]
|
||||||
|
}
|
||||||
|
|
||||||
|
_store_simd128(dst_v, v0, v1, v2, v3)
|
||||||
|
dst_v = dst_v[4:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the counter. Overflow checking is done upon
|
||||||
|
// entry into the routine, so a 64-bit increment safely
|
||||||
|
// covers both cases.
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
s3 = transmute(simd.u32x4)simd.add(transmute(simd.u64x2)s3, _VEC_ONE)
|
||||||
|
} else {
|
||||||
|
s3 = _increment_counter(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Little {
|
||||||
|
// Write back the counter to the state.
|
||||||
|
intrinsics.unaligned_store((^simd.u32x4)(x_v[3:]), s3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(enable_target_feature = TARGET_SIMD_FEATURES)
|
||||||
|
hchacha20 :: proc "contextless" (dst, key, iv: []byte) {
|
||||||
|
v0 := simd.u32x4{_chacha20.SIGMA_0, _chacha20.SIGMA_1, _chacha20.SIGMA_2, _chacha20.SIGMA_3}
|
||||||
|
v1 := intrinsics.unaligned_load((^simd.u32x4)(&key[0]))
|
||||||
|
v2 := intrinsics.unaligned_load((^simd.u32x4)(&key[16]))
|
||||||
|
v3 := intrinsics.unaligned_load((^simd.u32x4)(&iv[0]))
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Big {
|
||||||
|
v1 = _byteswap_u32x4(v1)
|
||||||
|
v2 = _byteswap_u32x4(v2)
|
||||||
|
v3 = _byteswap_u32x4(v3)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||||
|
v0, v1, v2, v3 = _dq_round_simd128(v0, v1, v2, v3)
|
||||||
|
}
|
||||||
|
|
||||||
|
when ODIN_ENDIAN == .Big {
|
||||||
|
v0 = _byteswap_u32x4(v0)
|
||||||
|
v3 = _byteswap_u32x4(v3)
|
||||||
|
}
|
||||||
|
|
||||||
|
dst_v := ([^]simd.u32x4)(raw_data(dst))
|
||||||
|
intrinsics.unaligned_store((^simd.u32x4)(dst_v[0:]), v0)
|
||||||
|
intrinsics.unaligned_store((^simd.u32x4)(dst_v[1:]), v3)
|
||||||
|
}
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
#+build amd64
|
||||||
|
package chacha20_simd256
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "core:crypto/_chacha20"
|
||||||
|
import chacha_simd128 "core:crypto/_chacha20/simd128"
|
||||||
|
import "core:simd"
|
||||||
|
import "core:sys/info"
|
||||||
|
|
||||||
|
// This is loosely based on Ted Krovetz's public domain C intrinsic
|
||||||
|
// implementations. While written using `core:simd`, this is currently
|
||||||
|
// amd64 specific because we do not have a way to detect ARM SVE.
|
||||||
|
//
|
||||||
|
// See:
|
||||||
|
// supercop-20230530/crypto_stream/chacha20/krovetz/vec128
|
||||||
|
// supercop-20230530/crypto_stream/chacha20/krovetz/avx2
|
||||||
|
|
||||||
|
#assert(ODIN_ENDIAN == .Little)
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_7L: simd.u32x8 : {7, 7, 7, 7, 7, 7, 7, 7}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_7R: simd.u32x8 : {25, 25, 25, 25, 25, 25, 25, 25}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_12L: simd.u32x8 : {12, 12, 12, 12, 12, 12, 12, 12}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_12R: simd.u32x8 : {20, 20, 20, 20, 20, 20, 20, 20}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_8L: simd.u32x8 : {8, 8, 8, 8, 8, 8, 8, 8}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_8R: simd.u32x8 : {24, 24, 24, 24, 24, 24, 24, 24}
|
||||||
|
@(private = "file")
|
||||||
|
_ROT_16: simd.u32x8 : {16, 16, 16, 16, 16, 16, 16, 16}
|
||||||
|
@(private = "file")
|
||||||
|
_VEC_ZERO_ONE: simd.u64x4 : {0, 0, 1, 0}
|
||||||
|
@(private = "file")
|
||||||
|
_VEC_TWO: simd.u64x4 : {2, 0, 2, 0}
|
||||||
|
|
||||||
|
// is_performant returns true iff the target and current host both support
|
||||||
|
// "enough" SIMD to make this implementation performant.
|
||||||
|
is_performant :: proc "contextless" () -> bool {
|
||||||
|
req_features :: info.CPU_Features{.avx, .avx2}
|
||||||
|
|
||||||
|
features, ok := info.cpu_features.?
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return features >= req_features
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_dq_round_simd256 :: #force_inline proc "contextless" (
|
||||||
|
v0, v1, v2, v3: simd.u32x8,
|
||||||
|
) -> (
|
||||||
|
simd.u32x8,
|
||||||
|
simd.u32x8,
|
||||||
|
simd.u32x8,
|
||||||
|
simd.u32x8,
|
||||||
|
) {
|
||||||
|
v0, v1, v2, v3 := v0, v1, v2, v3
|
||||||
|
|
||||||
|
// a += b; d ^= a; d = ROTW16(d);
|
||||||
|
v0 = simd.add(v0, v1)
|
||||||
|
v3 = simd.bit_xor(v3, v0)
|
||||||
|
v3 = simd.bit_xor(simd.shl(v3, _ROT_16), simd.shr(v3, _ROT_16))
|
||||||
|
|
||||||
|
// c += d; b ^= c; b = ROTW12(b);
|
||||||
|
v2 = simd.add(v2, v3)
|
||||||
|
v1 = simd.bit_xor(v1, v2)
|
||||||
|
v1 = simd.bit_xor(simd.shl(v1, _ROT_12L), simd.shr(v1, _ROT_12R))
|
||||||
|
|
||||||
|
// a += b; d ^= a; d = ROTW8(d);
|
||||||
|
v0 = simd.add(v0, v1)
|
||||||
|
v3 = simd.bit_xor(v3, v0)
|
||||||
|
v3 = simd.bit_xor(simd.shl(v3, _ROT_8L), simd.shr(v3, _ROT_8R))
|
||||||
|
|
||||||
|
// c += d; b ^= c; b = ROTW7(b);
|
||||||
|
v2 = simd.add(v2, v3)
|
||||||
|
v1 = simd.bit_xor(v1, v2)
|
||||||
|
v1 = simd.bit_xor(simd.shl(v1, _ROT_7L), simd.shr(v1, _ROT_7R))
|
||||||
|
|
||||||
|
// b = ROTV1(b); c = ROTV2(c); d = ROTV3(d);
|
||||||
|
v1 = simd.shuffle(v1, v1, 1, 2, 3, 0, 5, 6, 7, 4)
|
||||||
|
v2 = simd.shuffle(v2, v2, 2, 3, 0, 1, 6, 7, 4, 5)
|
||||||
|
v3 = simd.shuffle(v3, v3, 3, 0, 1, 2, 7, 4, 5, 6)
|
||||||
|
|
||||||
|
// a += b; d ^= a; d = ROTW16(d);
|
||||||
|
v0 = simd.add(v0, v1)
|
||||||
|
v3 = simd.bit_xor(v3, v0)
|
||||||
|
v3 = simd.bit_xor(simd.shl(v3, _ROT_16), simd.shr(v3, _ROT_16))
|
||||||
|
|
||||||
|
// c += d; b ^= c; b = ROTW12(b);
|
||||||
|
v2 = simd.add(v2, v3)
|
||||||
|
v1 = simd.bit_xor(v1, v2)
|
||||||
|
v1 = simd.bit_xor(simd.shl(v1, _ROT_12L), simd.shr(v1, _ROT_12R))
|
||||||
|
|
||||||
|
// a += b; d ^= a; d = ROTW8(d);
|
||||||
|
v0 = simd.add(v0, v1)
|
||||||
|
v3 = simd.bit_xor(v3, v0)
|
||||||
|
v3 = simd.bit_xor(simd.shl(v3, _ROT_8L), simd.shr(v3, _ROT_8R))
|
||||||
|
|
||||||
|
// c += d; b ^= c; b = ROTW7(b);
|
||||||
|
v2 = simd.add(v2, v3)
|
||||||
|
v1 = simd.bit_xor(v1, v2)
|
||||||
|
v1 = simd.bit_xor(simd.shl(v1, _ROT_7L), simd.shr(v1, _ROT_7R))
|
||||||
|
|
||||||
|
// b = ROTV3(b); c = ROTV2(c); d = ROTV1(d);
|
||||||
|
v1 = simd.shuffle(v1, v1, 3, 0, 1, 2, 7, 4, 5, 6)
|
||||||
|
v2 = simd.shuffle(v2, v2, 2, 3, 0, 1, 6, 7, 4, 5)
|
||||||
|
v3 = simd.shuffle(v3, v3, 1, 2, 3, 0, 5, 6, 7, 4)
|
||||||
|
|
||||||
|
return v0, v1, v2, v3
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_add_and_permute_state_simd256 :: #force_inline proc "contextless" (
|
||||||
|
v0, v1, v2, v3, s0, s1, s2, s3: simd.u32x8,
|
||||||
|
) -> (
|
||||||
|
simd.u32x8,
|
||||||
|
simd.u32x8,
|
||||||
|
simd.u32x8,
|
||||||
|
simd.u32x8,
|
||||||
|
) {
|
||||||
|
t0 := simd.add(v0, s0)
|
||||||
|
t1 := simd.add(v1, s1)
|
||||||
|
t2 := simd.add(v2, s2)
|
||||||
|
t3 := simd.add(v3, s3)
|
||||||
|
|
||||||
|
// Big Endian would byteswap here.
|
||||||
|
|
||||||
|
// Each of v0 .. v3 has 128-bits of keystream for 2 separate blocks.
|
||||||
|
// permute the state such that (r0, r1) contains block 0, and (r2, r3)
|
||||||
|
// contains block 1.
|
||||||
|
r0 := simd.shuffle(t0, t1, 0, 1, 2, 3, 8, 9, 10, 11)
|
||||||
|
r2 := simd.shuffle(t0, t1, 4, 5, 6, 7, 12, 13, 14, 15)
|
||||||
|
r1 := simd.shuffle(t2, t3, 0, 1, 2, 3, 8, 9, 10, 11)
|
||||||
|
r3 := simd.shuffle(t2, t3, 4, 5, 6, 7, 12, 13, 14, 15)
|
||||||
|
|
||||||
|
return r0, r1, r2, r3
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_xor_simd256 :: #force_inline proc "contextless" (
|
||||||
|
src: [^]simd.u32x8,
|
||||||
|
v0, v1, v2, v3: simd.u32x8,
|
||||||
|
) -> (
|
||||||
|
simd.u32x8,
|
||||||
|
simd.u32x8,
|
||||||
|
simd.u32x8,
|
||||||
|
simd.u32x8,
|
||||||
|
) {
|
||||||
|
v0, v1, v2, v3 := v0, v1, v2, v3
|
||||||
|
|
||||||
|
v0 = simd.bit_xor(v0, intrinsics.unaligned_load((^simd.u32x8)(src[0:])))
|
||||||
|
v1 = simd.bit_xor(v1, intrinsics.unaligned_load((^simd.u32x8)(src[1:])))
|
||||||
|
v2 = simd.bit_xor(v2, intrinsics.unaligned_load((^simd.u32x8)(src[2:])))
|
||||||
|
v3 = simd.bit_xor(v3, intrinsics.unaligned_load((^simd.u32x8)(src[3:])))
|
||||||
|
|
||||||
|
return v0, v1, v2, v3
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_xor_simd256_x1 :: #force_inline proc "contextless" (
|
||||||
|
src: [^]simd.u32x8,
|
||||||
|
v0, v1: simd.u32x8,
|
||||||
|
) -> (
|
||||||
|
simd.u32x8,
|
||||||
|
simd.u32x8,
|
||||||
|
) {
|
||||||
|
v0, v1 := v0, v1
|
||||||
|
|
||||||
|
v0 = simd.bit_xor(v0, intrinsics.unaligned_load((^simd.u32x8)(src[0:])))
|
||||||
|
v1 = simd.bit_xor(v1, intrinsics.unaligned_load((^simd.u32x8)(src[1:])))
|
||||||
|
|
||||||
|
return v0, v1
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_store_simd256 :: #force_inline proc "contextless" (
|
||||||
|
dst: [^]simd.u32x8,
|
||||||
|
v0, v1, v2, v3: simd.u32x8,
|
||||||
|
) {
|
||||||
|
intrinsics.unaligned_store((^simd.u32x8)(dst[0:]), v0)
|
||||||
|
intrinsics.unaligned_store((^simd.u32x8)(dst[1:]), v1)
|
||||||
|
intrinsics.unaligned_store((^simd.u32x8)(dst[2:]), v2)
|
||||||
|
intrinsics.unaligned_store((^simd.u32x8)(dst[3:]), v3)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private = "file")
|
||||||
|
_store_simd256_x1 :: #force_inline proc "contextless" (
|
||||||
|
dst: [^]simd.u32x8,
|
||||||
|
v0, v1: simd.u32x8,
|
||||||
|
) {
|
||||||
|
intrinsics.unaligned_store((^simd.u32x8)(dst[0:]), v0)
|
||||||
|
intrinsics.unaligned_store((^simd.u32x8)(dst[1:]), v1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(enable_target_feature = "sse2,ssse3,avx,avx2")
|
||||||
|
stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int) {
|
||||||
|
// Enforce the maximum consumed keystream per IV.
|
||||||
|
_chacha20.check_counter_limit(ctx, nr_blocks)
|
||||||
|
|
||||||
|
dst_v := ([^]simd.u32x8)(raw_data(dst))
|
||||||
|
src_v := ([^]simd.u32x8)(raw_data(src))
|
||||||
|
|
||||||
|
x := &ctx._s
|
||||||
|
n := nr_blocks
|
||||||
|
|
||||||
|
// The state vector is an array of uint32s in native byte-order.
|
||||||
|
// Setup s0 .. s3 such that each register stores 2 copies of the
|
||||||
|
// state.
|
||||||
|
x_v := ([^]simd.u32x4)(raw_data(x))
|
||||||
|
t0 := intrinsics.unaligned_load((^simd.u32x4)(x_v[0:]))
|
||||||
|
t1 := intrinsics.unaligned_load((^simd.u32x4)(x_v[1:]))
|
||||||
|
t2 := intrinsics.unaligned_load((^simd.u32x4)(x_v[2:]))
|
||||||
|
t3 := intrinsics.unaligned_load((^simd.u32x4)(x_v[3:]))
|
||||||
|
s0 := simd.swizzle(t0, 0, 1, 2, 3, 0, 1, 2, 3)
|
||||||
|
s1 := simd.swizzle(t1, 0, 1, 2, 3, 0, 1, 2, 3)
|
||||||
|
s2 := simd.swizzle(t2, 0, 1, 2, 3, 0, 1, 2, 3)
|
||||||
|
s3 := simd.swizzle(t3, 0, 1, 2, 3, 0, 1, 2, 3)
|
||||||
|
|
||||||
|
// Advance the counter in the 2nd copy of the state by one.
|
||||||
|
s3 = transmute(simd.u32x8)simd.add(transmute(simd.u64x4)s3, _VEC_ZERO_ONE)
|
||||||
|
|
||||||
|
// 8 blocks at a time.
|
||||||
|
for ; n >= 8; n = n - 8 {
|
||||||
|
v0, v1, v2, v3 := s0, s1, s2, s3
|
||||||
|
|
||||||
|
s7 := transmute(simd.u32x8)simd.add(transmute(simd.u64x4)s3, _VEC_TWO)
|
||||||
|
v4, v5, v6, v7 := s0, s1, s2, s7
|
||||||
|
|
||||||
|
s11 := transmute(simd.u32x8)simd.add(transmute(simd.u64x4)s7, _VEC_TWO)
|
||||||
|
v8, v9, v10, v11 := s0, s1, s2, s11
|
||||||
|
|
||||||
|
s15 := transmute(simd.u32x8)simd.add(transmute(simd.u64x4)s11, _VEC_TWO)
|
||||||
|
v12, v13, v14, v15 := s0, s1, s2, s15
|
||||||
|
|
||||||
|
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||||
|
v0, v1, v2, v3 = _dq_round_simd256(v0, v1, v2, v3)
|
||||||
|
v4, v5, v6, v7 = _dq_round_simd256(v4, v5, v6, v7)
|
||||||
|
v8, v9, v10, v11 = _dq_round_simd256(v8, v9, v10, v11)
|
||||||
|
v12, v13, v14, v15 = _dq_round_simd256(v12, v13, v14, v15)
|
||||||
|
}
|
||||||
|
|
||||||
|
v0, v1, v2, v3 = _add_and_permute_state_simd256(v0, v1, v2, v3, s0, s1, s2, s3)
|
||||||
|
v4, v5, v6, v7 = _add_and_permute_state_simd256(v4, v5, v6, v7, s0, s1, s2, s7)
|
||||||
|
v8, v9, v10, v11 = _add_and_permute_state_simd256(v8, v9, v10, v11, s0, s1, s2, s11)
|
||||||
|
v12, v13, v14, v15 = _add_and_permute_state_simd256(v12, v13, v14, v15, s0, s1, s2, s15)
|
||||||
|
|
||||||
|
#no_bounds_check {
|
||||||
|
if src != nil {
|
||||||
|
v0, v1, v2, v3 = _xor_simd256(src_v, v0, v1, v2, v3)
|
||||||
|
v4, v5, v6, v7 = _xor_simd256(src_v[4:], v4, v5, v6, v7)
|
||||||
|
v8, v9, v10, v11 = _xor_simd256(src_v[8:], v8, v9, v10, v11)
|
||||||
|
v12, v13, v14, v15 = _xor_simd256(src_v[12:], v12, v13, v14, v15)
|
||||||
|
src_v = src_v[16:]
|
||||||
|
}
|
||||||
|
|
||||||
|
_store_simd256(dst_v, v0, v1, v2, v3)
|
||||||
|
_store_simd256(dst_v[4:], v4, v5, v6, v7)
|
||||||
|
_store_simd256(dst_v[8:], v8, v9, v10, v11)
|
||||||
|
_store_simd256(dst_v[12:], v12, v13, v14, v15)
|
||||||
|
dst_v = dst_v[16:]
|
||||||
|
}
|
||||||
|
|
||||||
|
s3 = transmute(simd.u32x8)simd.add(transmute(simd.u64x4)s15, _VEC_TWO)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 2 (or 1) block at a time.
|
||||||
|
for ; n > 0; n = n - 2 {
|
||||||
|
v0, v1, v2, v3 := s0, s1, s2, s3
|
||||||
|
|
||||||
|
for i := _chacha20.ROUNDS; i > 0; i = i - 2 {
|
||||||
|
v0, v1, v2, v3 = _dq_round_simd256(v0, v1, v2, v3)
|
||||||
|
}
|
||||||
|
v0, v1, v2, v3 = _add_and_permute_state_simd256(v0, v1, v2, v3, s0, s1, s2, s3)
|
||||||
|
|
||||||
|
if n == 1 {
|
||||||
|
// Note: No need to advance src_v, dst_v, or increment the counter
|
||||||
|
// since this is guaranteed to be the final block.
|
||||||
|
#no_bounds_check {
|
||||||
|
if src != nil {
|
||||||
|
v0, v1 = _xor_simd256_x1(src_v, v0, v1)
|
||||||
|
}
|
||||||
|
|
||||||
|
_store_simd256_x1(dst_v, v0, v1)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
#no_bounds_check {
|
||||||
|
if src != nil {
|
||||||
|
v0, v1, v2, v3 = _xor_simd256(src_v, v0, v1, v2, v3)
|
||||||
|
src_v = src_v[4:]
|
||||||
|
}
|
||||||
|
|
||||||
|
_store_simd256(dst_v, v0, v1, v2, v3)
|
||||||
|
dst_v = dst_v[4:]
|
||||||
|
}
|
||||||
|
|
||||||
|
s3 = transmute(simd.u32x8)simd.add(transmute(simd.u64x4)s3, _VEC_TWO)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write back the counter. Doing it this way, saves having to
|
||||||
|
// pull out the correct counter value from s3.
|
||||||
|
new_ctr := ((u64(ctx._s[13]) << 32) | u64(ctx._s[12])) + u64(nr_blocks)
|
||||||
|
ctx._s[12] = u32(new_ctr)
|
||||||
|
ctx._s[13] = u32(new_ctr >> 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(enable_target_feature = "sse2,ssse3,avx")
|
||||||
|
hchacha20 :: proc "contextless" (dst, key, iv: []byte) {
|
||||||
|
// We can just enable AVX and call the simd128 code as going
|
||||||
|
// wider has 0 performance benefit, but VEX encoded instructions
|
||||||
|
// is nice.
|
||||||
|
#force_inline chacha_simd128.hchacha20(dst, key, iv)
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#+build !amd64
|
||||||
|
package chacha20_simd256
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "core:crypto/_chacha20"
|
||||||
|
|
||||||
|
is_performant :: proc "contextless" () -> bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
stream_blocks :: proc(ctx: ^_chacha20.Context, dst, src: []byte, nr_blocks: int) {
|
||||||
|
panic("crypto/chacha20: simd256 implementation unsupported")
|
||||||
|
}
|
||||||
|
|
||||||
|
hchacha20 :: proc "contextless" (dst, key, iv: []byte) {
|
||||||
|
intrinsics.trap()
|
||||||
|
}
|
||||||
@@ -81,16 +81,18 @@ bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
|
|||||||
// 2. while len(z) mod 8 ≠ 0:
|
// 2. while len(z) mod 8 ≠ 0:
|
||||||
// z = z || 0
|
// z = z || 0
|
||||||
|
|
||||||
// 3. while (len(z)/8) mod w ≠ 0:
|
// 3. while (len(z)/8) mod w != 0:
|
||||||
// z = z || 00000000
|
// z = z || 00000000
|
||||||
z_len := u128(z_hi) << 64 | u128(z_lo)
|
z_len := u128(z_hi) << 64 | u128(z_lo)
|
||||||
z_rem := int(z_len % u128(w))
|
z_rem := int(z_len % u128(w))
|
||||||
pad := _PAD[:w - z_rem]
|
if z_rem != 0 {
|
||||||
|
pad := _PAD[:w - z_rem]
|
||||||
|
|
||||||
// We just add the padding to the state, instead of returning z.
|
// We just add the padding to the state, instead of returning z.
|
||||||
//
|
//
|
||||||
// 4. return z.
|
// 4. return z.
|
||||||
update(ctx, pad)
|
update(ctx, pad)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
|
encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package aead
|
||||||
|
|
||||||
|
// seal_oneshot encrypts the plaintext and authenticates the aad and ciphertext,
|
||||||
|
// with the provided algorithm, key, and iv, stores the output in dst and tag.
|
||||||
|
//
|
||||||
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
|
seal_oneshot :: proc(algo: Algorithm, dst, tag, key, iv, aad, plaintext: []byte, impl: Implementation = nil) {
|
||||||
|
ctx: Context
|
||||||
|
init(&ctx, algo, key, impl)
|
||||||
|
defer reset(&ctx)
|
||||||
|
seal_ctx(&ctx, dst, tag, iv, aad, plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// open authenticates the aad and ciphertext, and decrypts the ciphertext,
|
||||||
|
// with the provided algorithm, key, iv, and tag, and stores the output in dst,
|
||||||
|
// returning true iff the authentication was successful. If authentication
|
||||||
|
// fails, the destination buffer will be zeroed.
|
||||||
|
//
|
||||||
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
|
@(require_results)
|
||||||
|
open_oneshot :: proc(algo: Algorithm, dst, key, iv, aad, ciphertext, tag: []byte, impl: Implementation = nil) -> bool {
|
||||||
|
ctx: Context
|
||||||
|
init(&ctx, algo, key, impl)
|
||||||
|
defer reset(&ctx)
|
||||||
|
return open_ctx(&ctx, dst, iv, aad, ciphertext, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
seal :: proc {
|
||||||
|
seal_ctx,
|
||||||
|
seal_oneshot,
|
||||||
|
}
|
||||||
|
|
||||||
|
open :: proc {
|
||||||
|
open_ctx,
|
||||||
|
open_oneshot,
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
package aead provides a generic interface to the supported Authenticated
|
||||||
|
Encryption with Associated Data algorithms.
|
||||||
|
|
||||||
|
Both a one-shot and context based interface are provided, with similar
|
||||||
|
usage. If multiple messages are to be sealed/opened via the same key,
|
||||||
|
the context based interface may be more efficient, depending on the
|
||||||
|
algorithm.
|
||||||
|
|
||||||
|
WARNING: Reusing the same key + iv to seal (encrypt) multiple messages
|
||||||
|
results in catastrophic loss of security for most algorithms.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
package aead_example
|
||||||
|
|
||||||
|
import "core:bytes"
|
||||||
|
import "core:crypto"
|
||||||
|
import "core:crypto/aead"
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
algo := aead.Algorithm.XCHACHA20POLY1305
|
||||||
|
|
||||||
|
// The example added associated data, and plaintext.
|
||||||
|
aad_str := "Get your ass in gear boys."
|
||||||
|
pt_str := "They're immanetizing the Eschaton."
|
||||||
|
|
||||||
|
aad := transmute([]byte)aad_str
|
||||||
|
plaintext := transmute([]byte)pt_str
|
||||||
|
pt_len := len(plaintext)
|
||||||
|
|
||||||
|
// Generate a random key for the purposes of illustration.
|
||||||
|
key := make([]byte, aead.KEY_SIZES[algo])
|
||||||
|
defer delete(key)
|
||||||
|
crypto.rand_bytes(key)
|
||||||
|
|
||||||
|
// `ciphertext || tag`, is a common way data is transmitted, so
|
||||||
|
// demonstrate that.
|
||||||
|
buf := make([]byte, pt_len + aead.TAG_SIZES[algo])
|
||||||
|
defer delete(buf)
|
||||||
|
ciphertext, tag := buf[:pt_len], buf[pt_len:]
|
||||||
|
|
||||||
|
// Seal the AAD + Plaintext.
|
||||||
|
iv := make([]byte, aead.IV_SIZES[algo])
|
||||||
|
defer delete(iv)
|
||||||
|
crypto.rand_bytes(iv) // Random IVs are safe with XChaCha20-Poly1305.
|
||||||
|
aead.seal(algo, ciphertext, tag, key, iv, aad, plaintext)
|
||||||
|
|
||||||
|
// Open the AAD + Ciphertext.
|
||||||
|
opened_pt := buf[:pt_len]
|
||||||
|
if ok := aead.open(algo, opened_pt, key, iv, aad, ciphertext, tag); !ok {
|
||||||
|
panic("aead example: failed to open")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(bytes.equal(opened_pt, plaintext))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
package aead
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
package aead
|
||||||
|
|
||||||
|
import "core:crypto/aes"
|
||||||
|
import "core:crypto/chacha20"
|
||||||
|
import "core:crypto/chacha20poly1305"
|
||||||
|
import "core:reflect"
|
||||||
|
|
||||||
|
// Implementation is an AEAD implementation. Most callers will not need
|
||||||
|
// to use this as the package will automatically select the most performant
|
||||||
|
// implementation available.
|
||||||
|
Implementation :: union {
|
||||||
|
aes.Implementation,
|
||||||
|
chacha20.Implementation,
|
||||||
|
}
|
||||||
|
|
||||||
|
// MAX_TAG_SIZE is the maximum size tag that can be returned by any of the
|
||||||
|
// Algorithms supported via this package.
|
||||||
|
MAX_TAG_SIZE :: 16
|
||||||
|
|
||||||
|
// Algorithm is the algorithm identifier associated with a given Context.
|
||||||
|
Algorithm :: enum {
|
||||||
|
Invalid,
|
||||||
|
AES_GCM_128,
|
||||||
|
AES_GCM_192,
|
||||||
|
AES_GCM_256,
|
||||||
|
CHACHA20POLY1305,
|
||||||
|
XCHACHA20POLY1305,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ALGORITM_NAMES is the Agorithm to algorithm name string.
|
||||||
|
ALGORITHM_NAMES := [Algorithm]string {
|
||||||
|
.Invalid = "Invalid",
|
||||||
|
.AES_GCM_128 = "AES-GCM-128",
|
||||||
|
.AES_GCM_192 = "AES-GCM-192",
|
||||||
|
.AES_GCM_256 = "AES-GCM-256",
|
||||||
|
.CHACHA20POLY1305 = "chacha20poly1305",
|
||||||
|
.XCHACHA20POLY1305 = "xchacha20poly1305",
|
||||||
|
}
|
||||||
|
|
||||||
|
// TAG_SIZES is the Algorithm to tag size in bytes.
|
||||||
|
TAG_SIZES := [Algorithm]int {
|
||||||
|
.Invalid = 0,
|
||||||
|
.AES_GCM_128 = aes.GCM_TAG_SIZE,
|
||||||
|
.AES_GCM_192 = aes.GCM_TAG_SIZE,
|
||||||
|
.AES_GCM_256 = aes.GCM_TAG_SIZE,
|
||||||
|
.CHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
|
||||||
|
.XCHACHA20POLY1305 = chacha20poly1305.TAG_SIZE,
|
||||||
|
}
|
||||||
|
|
||||||
|
// KEY_SIZES is the Algorithm to key size in bytes.
|
||||||
|
KEY_SIZES := [Algorithm]int {
|
||||||
|
.Invalid = 0,
|
||||||
|
.AES_GCM_128 = aes.KEY_SIZE_128,
|
||||||
|
.AES_GCM_192 = aes.KEY_SIZE_192,
|
||||||
|
.AES_GCM_256 = aes.KEY_SIZE_256,
|
||||||
|
.CHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
|
||||||
|
.XCHACHA20POLY1305 = chacha20poly1305.KEY_SIZE,
|
||||||
|
}
|
||||||
|
|
||||||
|
// IV_SIZES is the Algorithm to initialization vector size in bytes.
|
||||||
|
//
|
||||||
|
// Note: Some algorithms (such as AES-GCM) support variable IV sizes.
|
||||||
|
IV_SIZES := [Algorithm]int {
|
||||||
|
.Invalid = 0,
|
||||||
|
.AES_GCM_128 = aes.GCM_IV_SIZE,
|
||||||
|
.AES_GCM_192 = aes.GCM_IV_SIZE,
|
||||||
|
.AES_GCM_256 = aes.GCM_IV_SIZE,
|
||||||
|
.CHACHA20POLY1305 = chacha20poly1305.IV_SIZE,
|
||||||
|
.XCHACHA20POLY1305 = chacha20poly1305.XIV_SIZE,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context is a concrete instantiation of a specific AEAD algorithm.
|
||||||
|
Context :: struct {
|
||||||
|
_algo: Algorithm,
|
||||||
|
_impl: union {
|
||||||
|
aes.Context_GCM,
|
||||||
|
chacha20poly1305.Context,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
_IMPL_IDS := [Algorithm]typeid {
|
||||||
|
.Invalid = nil,
|
||||||
|
.AES_GCM_128 = typeid_of(aes.Context_GCM),
|
||||||
|
.AES_GCM_192 = typeid_of(aes.Context_GCM),
|
||||||
|
.AES_GCM_256 = typeid_of(aes.Context_GCM),
|
||||||
|
.CHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
|
||||||
|
.XCHACHA20POLY1305 = typeid_of(chacha20poly1305.Context),
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes a Context with a specific AEAD Algorithm.
|
||||||
|
init :: proc(ctx: ^Context, algorithm: Algorithm, key: []byte, impl: Implementation = nil) {
|
||||||
|
if ctx._impl != nil {
|
||||||
|
reset(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(key) != KEY_SIZES[algorithm] {
|
||||||
|
panic("crypto/aead: invalid key size")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directly specialize the union by setting the type ID (save a copy).
|
||||||
|
reflect.set_union_variant_typeid(
|
||||||
|
ctx._impl,
|
||||||
|
_IMPL_IDS[algorithm],
|
||||||
|
)
|
||||||
|
switch algorithm {
|
||||||
|
case .AES_GCM_128, .AES_GCM_192, .AES_GCM_256:
|
||||||
|
impl_ := impl != nil ? impl.(aes.Implementation) : aes.DEFAULT_IMPLEMENTATION
|
||||||
|
aes.init_gcm(&ctx._impl.(aes.Context_GCM), key, impl_)
|
||||||
|
case .CHACHA20POLY1305:
|
||||||
|
impl_ := impl != nil ? impl.(chacha20.Implementation) : chacha20.DEFAULT_IMPLEMENTATION
|
||||||
|
chacha20poly1305.init(&ctx._impl.(chacha20poly1305.Context), key, impl_)
|
||||||
|
case .XCHACHA20POLY1305:
|
||||||
|
impl_ := impl != nil ? impl.(chacha20.Implementation) : chacha20.DEFAULT_IMPLEMENTATION
|
||||||
|
chacha20poly1305.init_xchacha(&ctx._impl.(chacha20poly1305.Context), key, impl_)
|
||||||
|
case .Invalid:
|
||||||
|
panic("crypto/aead: uninitialized algorithm")
|
||||||
|
case:
|
||||||
|
panic("crypto/aead: invalid algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx._algo = algorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// seal_ctx encrypts the plaintext and authenticates the aad and ciphertext,
|
||||||
|
// with the provided Context and iv, stores the output in dst and tag.
|
||||||
|
//
|
||||||
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
|
seal_ctx :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
||||||
|
switch &impl in ctx._impl {
|
||||||
|
case aes.Context_GCM:
|
||||||
|
aes.seal_gcm(&impl, dst, tag, iv, aad, plaintext)
|
||||||
|
case chacha20poly1305.Context:
|
||||||
|
chacha20poly1305.seal(&impl, dst, tag, iv, aad, plaintext)
|
||||||
|
case:
|
||||||
|
panic("crypto/aead: uninitialized algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// open_ctx authenticates the aad and ciphertext, and decrypts the ciphertext,
|
||||||
|
// with the provided Context, iv, and tag, and stores the output in dst,
|
||||||
|
// returning true iff the authentication was successful. If authentication
|
||||||
|
// fails, the destination buffer will be zeroed.
|
||||||
|
//
|
||||||
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
|
@(require_results)
|
||||||
|
open_ctx :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
|
switch &impl in ctx._impl {
|
||||||
|
case aes.Context_GCM:
|
||||||
|
return aes.open_gcm(&impl, dst, iv, aad, ciphertext, tag)
|
||||||
|
case chacha20poly1305.Context:
|
||||||
|
return chacha20poly1305.open(&impl, dst, iv, aad, ciphertext, tag)
|
||||||
|
case:
|
||||||
|
panic("crypto/aead: uninitialized algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
|
// be used again.
|
||||||
|
reset :: proc(ctx: ^Context) {
|
||||||
|
switch &impl in ctx._impl {
|
||||||
|
case aes.Context_GCM:
|
||||||
|
aes.reset_gcm(&impl)
|
||||||
|
case chacha20poly1305.Context:
|
||||||
|
chacha20poly1305.reset(&impl)
|
||||||
|
case:
|
||||||
|
// Calling reset repeatedly is fine.
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx._algo = .Invalid
|
||||||
|
ctx._impl = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// algorithm returns the Algorithm used by a Context instance.
|
||||||
|
algorithm :: proc(ctx: ^Context) -> Algorithm {
|
||||||
|
return ctx._algo
|
||||||
|
}
|
||||||
|
|
||||||
|
// iv_size returns the IV size of a Context instance in bytes.
|
||||||
|
iv_size :: proc(ctx: ^Context) -> int {
|
||||||
|
return IV_SIZES[ctx._algo]
|
||||||
|
}
|
||||||
|
|
||||||
|
// tag_size returns the tag size of a Context instance in bytes.
|
||||||
|
tag_size :: proc(ctx: ^Context) -> int {
|
||||||
|
return TAG_SIZES[ctx._algo]
|
||||||
|
}
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
package aes implements the AES block cipher and some common modes.
|
package aes implements the AES block cipher and some common modes.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197-upd1.pdf
|
- [[ https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197-upd1.pdf ]]
|
||||||
- https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf
|
- [[ https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf ]]
|
||||||
- https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
- [[ https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf ]]
|
||||||
*/
|
*/
|
||||||
package aes
|
package aes
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Context_CTR :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init_ctr initializes a Context_CTR with the provided key and IV.
|
// init_ctr initializes a Context_CTR with the provided key and IV.
|
||||||
init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := Implementation.Hardware) {
|
init_ctr :: proc(ctx: ^Context_CTR, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
||||||
if len(iv) != CTR_IV_SIZE {
|
if len(iv) != CTR_IV_SIZE {
|
||||||
panic("crypto/aes: invalid CTR IV size")
|
panic("crypto/aes: invalid CTR IV size")
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ xor_bytes_ctr :: proc(ctx: ^Context_CTR, dst, src: []byte) {
|
|||||||
panic("crypto/aes: dst and src alias inexactly")
|
panic("crypto/aes: dst and src alias inexactly")
|
||||||
}
|
}
|
||||||
|
|
||||||
for remaining := len(src); remaining > 0; {
|
#no_bounds_check for remaining := len(src); remaining > 0; {
|
||||||
// Process multiple blocks at once
|
// Process multiple blocks at once
|
||||||
if ctx._off == BLOCK_SIZE {
|
if ctx._off == BLOCK_SIZE {
|
||||||
if nr_blocks := remaining / BLOCK_SIZE; nr_blocks > 0 {
|
if nr_blocks := remaining / BLOCK_SIZE; nr_blocks > 0 {
|
||||||
@@ -85,7 +85,7 @@ keystream_bytes_ctr :: proc(ctx: ^Context_CTR, dst: []byte) {
|
|||||||
assert(ctx._is_initialized)
|
assert(ctx._is_initialized)
|
||||||
|
|
||||||
dst := dst
|
dst := dst
|
||||||
for remaining := len(dst); remaining > 0; {
|
#no_bounds_check for remaining := len(dst); remaining > 0; {
|
||||||
// Process multiple blocks at once
|
// Process multiple blocks at once
|
||||||
if ctx._off == BLOCK_SIZE {
|
if ctx._off == BLOCK_SIZE {
|
||||||
if nr_blocks := remaining / BLOCK_SIZE; nr_blocks > 0 {
|
if nr_blocks := remaining / BLOCK_SIZE; nr_blocks > 0 {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build amd64
|
#+build amd64
|
||||||
package aes
|
package aes
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Context_ECB :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init_ecb initializes a Context_ECB with the provided key.
|
// init_ecb initializes a Context_ECB with the provided key.
|
||||||
init_ecb :: proc(ctx: ^Context_ECB, key: []byte, impl := Implementation.Hardware) {
|
init_ecb :: proc(ctx: ^Context_ECB, key: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
||||||
init_impl(&ctx._impl, key, impl)
|
init_impl(&ctx._impl, key, impl)
|
||||||
ctx._is_initialized = true
|
ctx._is_initialized = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build amd64
|
#+build amd64
|
||||||
package aes
|
package aes
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import "core:crypto/_aes/ct64"
|
|||||||
import "core:encoding/endian"
|
import "core:encoding/endian"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
// GCM_NONCE_SIZE is the default size of the GCM nonce in bytes.
|
// GCM_IV_SIZE is the default size of the GCM IV in bytes.
|
||||||
GCM_NONCE_SIZE :: 12
|
GCM_IV_SIZE :: 12
|
||||||
// GCM_NONCE_SIZE_MAX is the maximum size of the GCM nonce in bytes.
|
// GCM_IV_SIZE_MAX is the maximum size of the GCM IV in bytes.
|
||||||
GCM_NONCE_SIZE_MAX :: 0x2000000000000000 // floor((2^64 - 1) / 8) bits
|
GCM_IV_SIZE_MAX :: 0x2000000000000000 // floor((2^64 - 1) / 8) bits
|
||||||
// GCM_TAG_SIZE is the size of a GCM tag in bytes.
|
// GCM_TAG_SIZE is the size of a GCM tag in bytes.
|
||||||
GCM_TAG_SIZE :: _aes.GHASH_TAG_SIZE
|
GCM_TAG_SIZE :: _aes.GHASH_TAG_SIZE
|
||||||
|
|
||||||
@@ -26,19 +26,19 @@ Context_GCM :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// init_gcm initializes a Context_GCM with the provided key.
|
// init_gcm initializes a Context_GCM with the provided key.
|
||||||
init_gcm :: proc(ctx: ^Context_GCM, key: []byte, impl := Implementation.Hardware) {
|
init_gcm :: proc(ctx: ^Context_GCM, key: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
||||||
init_impl(&ctx._impl, key, impl)
|
init_impl(&ctx._impl, key, impl)
|
||||||
ctx._is_initialized = true
|
ctx._is_initialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// seal_gcm encrypts the plaintext and authenticates the aad and ciphertext,
|
// seal_gcm encrypts the plaintext and authenticates the aad and ciphertext,
|
||||||
// with the provided Context_GCM and nonce, stores the output in dst and tag.
|
// with the provided Context_GCM and iv, stores the output in dst and tag.
|
||||||
//
|
//
|
||||||
// dst and plaintext MUST alias exactly or not at all.
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, nonce, aad, plaintext: []byte) {
|
seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, iv, aad, plaintext: []byte) {
|
||||||
assert(ctx._is_initialized)
|
assert(ctx._is_initialized)
|
||||||
|
|
||||||
gcm_validate_common_slice_sizes(tag, nonce, aad, plaintext)
|
gcm_validate_common_slice_sizes(tag, iv, aad, plaintext)
|
||||||
if len(dst) != len(plaintext) {
|
if len(dst) != len(plaintext) {
|
||||||
panic("crypto/aes: invalid destination ciphertext size")
|
panic("crypto/aes: invalid destination ciphertext size")
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, nonce, aad, plaintext: []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
||||||
gcm_seal_hw(&impl, dst, tag, nonce, aad, plaintext)
|
gcm_seal_hw(&impl, dst, tag, iv, aad, plaintext)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, nonce, aad, plaintext: []byte) {
|
|||||||
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
|
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
s: [_aes.GHASH_TAG_SIZE]byte
|
s: [_aes.GHASH_TAG_SIZE]byte
|
||||||
init_ghash_ct64(ctx, &h, &j0, &j0_enc, nonce)
|
init_ghash_ct64(ctx, &h, &j0, &j0_enc, iv)
|
||||||
|
|
||||||
// Note: Our GHASH implementation handles appending padding.
|
// Note: Our GHASH implementation handles appending padding.
|
||||||
ct64.ghash(s[:], h[:], aad)
|
ct64.ghash(s[:], h[:], aad)
|
||||||
@@ -69,15 +69,16 @@ seal_gcm :: proc(ctx: ^Context_GCM, dst, tag, nonce, aad, plaintext: []byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// open_gcm authenticates the aad and ciphertext, and decrypts the ciphertext,
|
// open_gcm authenticates the aad and ciphertext, and decrypts the ciphertext,
|
||||||
// with the provided Context_GCM, nonce, and tag, and stores the output in dst,
|
// with the provided Context_GCM, iv, and tag, and stores the output in dst,
|
||||||
// returning true iff the authentication was successful. If authentication
|
// returning true iff the authentication was successful. If authentication
|
||||||
// fails, the destination buffer will be zeroed.
|
// fails, the destination buffer will be zeroed.
|
||||||
//
|
//
|
||||||
// dst and plaintext MUST alias exactly or not at all.
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
open_gcm :: proc(ctx: ^Context_GCM, dst, nonce, aad, ciphertext, tag: []byte) -> bool {
|
@(require_results)
|
||||||
|
open_gcm :: proc(ctx: ^Context_GCM, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
assert(ctx._is_initialized)
|
assert(ctx._is_initialized)
|
||||||
|
|
||||||
gcm_validate_common_slice_sizes(tag, nonce, aad, ciphertext)
|
gcm_validate_common_slice_sizes(tag, iv, aad, ciphertext)
|
||||||
if len(dst) != len(ciphertext) {
|
if len(dst) != len(ciphertext) {
|
||||||
panic("crypto/aes: invalid destination plaintext size")
|
panic("crypto/aes: invalid destination plaintext size")
|
||||||
}
|
}
|
||||||
@@ -86,14 +87,14 @@ open_gcm :: proc(ctx: ^Context_GCM, dst, nonce, aad, ciphertext, tag: []byte) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
if impl, is_hw := ctx._impl.(Context_Impl_Hardware); is_hw {
|
||||||
return gcm_open_hw(&impl, dst, nonce, aad, ciphertext, tag)
|
return gcm_open_hw(&impl, dst, iv, aad, ciphertext, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
h: [_aes.GHASH_KEY_SIZE]byte
|
h: [_aes.GHASH_KEY_SIZE]byte
|
||||||
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
|
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
s: [_aes.GHASH_TAG_SIZE]byte
|
s: [_aes.GHASH_TAG_SIZE]byte
|
||||||
init_ghash_ct64(ctx, &h, &j0, &j0_enc, nonce)
|
init_ghash_ct64(ctx, &h, &j0, &j0_enc, iv)
|
||||||
|
|
||||||
ct64.ghash(s[:], h[:], aad)
|
ct64.ghash(s[:], h[:], aad)
|
||||||
gctr_ct64(ctx, dst, &s, ciphertext, &h, &j0, false)
|
gctr_ct64(ctx, dst, &s, ciphertext, &h, &j0, false)
|
||||||
@@ -112,7 +113,7 @@ open_gcm :: proc(ctx: ^Context_GCM, dst, nonce, aad, ciphertext, tag: []byte) ->
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset_ctr sanitizes the Context_GCM. The Context_GCM must be
|
// reset_gcm sanitizes the Context_GCM. The Context_GCM must be
|
||||||
// re-initialized to be used again.
|
// re-initialized to be used again.
|
||||||
reset_gcm :: proc "contextless" (ctx: ^Context_GCM) {
|
reset_gcm :: proc "contextless" (ctx: ^Context_GCM) {
|
||||||
reset_impl(&ctx._impl)
|
reset_impl(&ctx._impl)
|
||||||
@@ -120,14 +121,14 @@ reset_gcm :: proc "contextless" (ctx: ^Context_GCM) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
gcm_validate_common_slice_sizes :: proc(tag, nonce, aad, text: []byte) {
|
gcm_validate_common_slice_sizes :: proc(tag, iv, aad, text: []byte) {
|
||||||
if len(tag) != GCM_TAG_SIZE {
|
if len(tag) != GCM_TAG_SIZE {
|
||||||
panic("crypto/aes: invalid GCM tag size")
|
panic("crypto/aes: invalid GCM tag size")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The specification supports nonces in the range [1, 2^64) bits.
|
// The specification supports IVs in the range [1, 2^64) bits.
|
||||||
if l := len(nonce); l == 0 || u64(l) >= GCM_NONCE_SIZE_MAX {
|
if l := len(iv); l == 0 || u64(l) >= GCM_IV_SIZE_MAX {
|
||||||
panic("crypto/aes: invalid GCM nonce size")
|
panic("crypto/aes: invalid GCM IV size")
|
||||||
}
|
}
|
||||||
|
|
||||||
if aad_len := u64(len(aad)); aad_len > GCM_A_MAX {
|
if aad_len := u64(len(aad)); aad_len > GCM_A_MAX {
|
||||||
@@ -144,7 +145,7 @@ init_ghash_ct64 :: proc(
|
|||||||
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
||||||
j0: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
j0: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
j0_enc: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
j0_enc: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
nonce: []byte,
|
iv: []byte,
|
||||||
) {
|
) {
|
||||||
impl := &ctx._impl.(ct64.Context)
|
impl := &ctx._impl.(ct64.Context)
|
||||||
|
|
||||||
@@ -152,14 +153,14 @@ init_ghash_ct64 :: proc(
|
|||||||
ct64.encrypt_block(impl, h[:], h[:])
|
ct64.encrypt_block(impl, h[:], h[:])
|
||||||
|
|
||||||
// Define a block, J0, as follows:
|
// Define a block, J0, as follows:
|
||||||
if l := len(nonce); l == GCM_NONCE_SIZE {
|
if l := len(iv); l == GCM_IV_SIZE {
|
||||||
// if len(IV) = 96, then let J0 = IV || 0^31 || 1
|
// if len(IV) = 96, then let J0 = IV || 0^31 || 1
|
||||||
copy(j0[:], nonce)
|
copy(j0[:], iv)
|
||||||
j0[_aes.GHASH_BLOCK_SIZE - 1] = 1
|
j0[_aes.GHASH_BLOCK_SIZE - 1] = 1
|
||||||
} else {
|
} else {
|
||||||
// If len(IV) != 96, then let s = 128 ceil(len(IV)/128) - len(IV),
|
// If len(IV) != 96, then let s = 128 ceil(len(IV)/128) - len(IV),
|
||||||
// and let J0 = GHASHH(IV || 0^(s+64) || ceil(len(IV))^64).
|
// and let J0 = GHASHH(IV || 0^(s+64) || ceil(len(IV))^64).
|
||||||
ct64.ghash(j0[:], h[:], nonce)
|
ct64.ghash(j0[:], h[:], iv)
|
||||||
|
|
||||||
tmp: [_aes.GHASH_BLOCK_SIZE]byte
|
tmp: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
endian.unchecked_put_u64be(tmp[8:], u64(l) * 8)
|
endian.unchecked_put_u64be(tmp[8:], u64(l) * 8)
|
||||||
@@ -197,7 +198,7 @@ gctr_ct64 :: proc(
|
|||||||
s: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
s: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
src: []byte,
|
src: []byte,
|
||||||
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
||||||
nonce: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
iv: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
is_seal: bool,
|
is_seal: bool,
|
||||||
) #no_bounds_check {
|
) #no_bounds_check {
|
||||||
ct64_inc_ctr32 := #force_inline proc "contextless" (dst: []byte, ctr: u32) -> u32 {
|
ct64_inc_ctr32 := #force_inline proc "contextless" (dst: []byte, ctr: u32) -> u32 {
|
||||||
@@ -208,14 +209,14 @@ gctr_ct64 :: proc(
|
|||||||
// Setup the counter blocks.
|
// Setup the counter blocks.
|
||||||
tmp, tmp2: [ct64.STRIDE][BLOCK_SIZE]byte = ---, ---
|
tmp, tmp2: [ct64.STRIDE][BLOCK_SIZE]byte = ---, ---
|
||||||
ctrs, blks: [ct64.STRIDE][]byte = ---, ---
|
ctrs, blks: [ct64.STRIDE][]byte = ---, ---
|
||||||
ctr := endian.unchecked_get_u32be(nonce[GCM_NONCE_SIZE:]) + 1
|
ctr := endian.unchecked_get_u32be(iv[GCM_IV_SIZE:]) + 1
|
||||||
for i in 0 ..< ct64.STRIDE {
|
for i in 0 ..< ct64.STRIDE {
|
||||||
// Setup scratch space for the keystream.
|
// Setup scratch space for the keystream.
|
||||||
blks[i] = tmp2[i][:]
|
blks[i] = tmp2[i][:]
|
||||||
|
|
||||||
// Pre-copy the IV to all the counter blocks.
|
// Pre-copy the IV to all the counter blocks.
|
||||||
ctrs[i] = tmp[i][:]
|
ctrs[i] = tmp[i][:]
|
||||||
copy(ctrs[i], nonce[:GCM_NONCE_SIZE])
|
copy(ctrs[i], iv[:GCM_IV_SIZE])
|
||||||
}
|
}
|
||||||
|
|
||||||
impl := &ctx._impl.(ct64.Context)
|
impl := &ctx._impl.(ct64.Context)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build amd64
|
#+build amd64
|
||||||
package aes
|
package aes
|
||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
@@ -10,12 +10,12 @@ import "core:mem"
|
|||||||
import "core:simd/x86"
|
import "core:simd/x86"
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
gcm_seal_hw :: proc(ctx: ^Context_Impl_Hardware, dst, tag, nonce, aad, plaintext: []byte) {
|
gcm_seal_hw :: proc(ctx: ^Context_Impl_Hardware, dst, tag, iv, aad, plaintext: []byte) {
|
||||||
h: [_aes.GHASH_KEY_SIZE]byte
|
h: [_aes.GHASH_KEY_SIZE]byte
|
||||||
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
|
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
s: [_aes.GHASH_TAG_SIZE]byte
|
s: [_aes.GHASH_TAG_SIZE]byte
|
||||||
init_ghash_hw(ctx, &h, &j0, &j0_enc, nonce)
|
init_ghash_hw(ctx, &h, &j0, &j0_enc, iv)
|
||||||
|
|
||||||
// Note: Our GHASH implementation handles appending padding.
|
// Note: Our GHASH implementation handles appending padding.
|
||||||
hw_intel.ghash(s[:], h[:], aad)
|
hw_intel.ghash(s[:], h[:], aad)
|
||||||
@@ -29,12 +29,12 @@ gcm_seal_hw :: proc(ctx: ^Context_Impl_Hardware, dst, tag, nonce, aad, plaintext
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
gcm_open_hw :: proc(ctx: ^Context_Impl_Hardware, dst, nonce, aad, ciphertext, tag: []byte) -> bool {
|
gcm_open_hw :: proc(ctx: ^Context_Impl_Hardware, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
h: [_aes.GHASH_KEY_SIZE]byte
|
h: [_aes.GHASH_KEY_SIZE]byte
|
||||||
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
j0: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
|
j0_enc: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
s: [_aes.GHASH_TAG_SIZE]byte
|
s: [_aes.GHASH_TAG_SIZE]byte
|
||||||
init_ghash_hw(ctx, &h, &j0, &j0_enc, nonce)
|
init_ghash_hw(ctx, &h, &j0, &j0_enc, iv)
|
||||||
|
|
||||||
hw_intel.ghash(s[:], h[:], aad)
|
hw_intel.ghash(s[:], h[:], aad)
|
||||||
gctr_hw(ctx, dst, &s, ciphertext, &h, &j0, false)
|
gctr_hw(ctx, dst, &s, ciphertext, &h, &j0, false)
|
||||||
@@ -59,20 +59,20 @@ init_ghash_hw :: proc(
|
|||||||
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
||||||
j0: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
j0: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
j0_enc: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
j0_enc: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
nonce: []byte,
|
iv: []byte,
|
||||||
) {
|
) {
|
||||||
// 1. Let H = CIPH(k, 0^128)
|
// 1. Let H = CIPH(k, 0^128)
|
||||||
encrypt_block_hw(ctx, h[:], h[:])
|
encrypt_block_hw(ctx, h[:], h[:])
|
||||||
|
|
||||||
// Define a block, J0, as follows:
|
// Define a block, J0, as follows:
|
||||||
if l := len(nonce); l == GCM_NONCE_SIZE {
|
if l := len(iv); l == GCM_IV_SIZE {
|
||||||
// if len(IV) = 96, then let J0 = IV || 0^31 || 1
|
// if len(IV) = 96, then let J0 = IV || 0^31 || 1
|
||||||
copy(j0[:], nonce)
|
copy(j0[:], iv)
|
||||||
j0[_aes.GHASH_BLOCK_SIZE - 1] = 1
|
j0[_aes.GHASH_BLOCK_SIZE - 1] = 1
|
||||||
} else {
|
} else {
|
||||||
// If len(IV) != 96, then let s = 128 ceil(len(IV)/128) - len(IV),
|
// If len(IV) != 96, then let s = 128 ceil(len(IV)/128) - len(IV),
|
||||||
// and let J0 = GHASHH(IV || 0^(s+64) || ceil(len(IV))^64).
|
// and let J0 = GHASHH(IV || 0^(s+64) || ceil(len(IV))^64).
|
||||||
hw_intel.ghash(j0[:], h[:], nonce)
|
hw_intel.ghash(j0[:], h[:], iv)
|
||||||
|
|
||||||
tmp: [_aes.GHASH_BLOCK_SIZE]byte
|
tmp: [_aes.GHASH_BLOCK_SIZE]byte
|
||||||
endian.unchecked_put_u64be(tmp[8:], u64(l) * 8)
|
endian.unchecked_put_u64be(tmp[8:], u64(l) * 8)
|
||||||
@@ -109,7 +109,7 @@ gctr_hw :: proc(
|
|||||||
s: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
s: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
src: []byte,
|
src: []byte,
|
||||||
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
h: ^[_aes.GHASH_KEY_SIZE]byte,
|
||||||
nonce: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
iv: ^[_aes.GHASH_BLOCK_SIZE]byte,
|
||||||
is_seal: bool,
|
is_seal: bool,
|
||||||
) #no_bounds_check {
|
) #no_bounds_check {
|
||||||
sks: [15]x86.__m128i = ---
|
sks: [15]x86.__m128i = ---
|
||||||
@@ -118,8 +118,8 @@ gctr_hw :: proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup the counter block
|
// Setup the counter block
|
||||||
ctr_blk := intrinsics.unaligned_load((^x86.__m128i)(nonce))
|
ctr_blk := intrinsics.unaligned_load((^x86.__m128i)(iv))
|
||||||
ctr := endian.unchecked_get_u32be(nonce[GCM_NONCE_SIZE:]) + 1
|
ctr := endian.unchecked_get_u32be(iv[GCM_IV_SIZE:]) + 1
|
||||||
|
|
||||||
src, dst := src, dst
|
src, dst := src, dst
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ Context_Impl :: union {
|
|||||||
Context_Impl_Hardware,
|
Context_Impl_Hardware,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEFAULT_IMPLEMENTATION is the implementation that will be used by
|
||||||
|
// default if possible.
|
||||||
|
DEFAULT_IMPLEMENTATION :: Implementation.Hardware
|
||||||
|
|
||||||
// Implementation is an AES implementation. Most callers will not need
|
// Implementation is an AES implementation. Most callers will not need
|
||||||
// to use this as the package will automatically select the most performant
|
// to use this as the package will automatically select the most performant
|
||||||
// implementation available (See `is_hardware_accelerated()`).
|
// implementation available (See `is_hardware_accelerated()`).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build !amd64
|
#+build !amd64
|
||||||
package aes
|
package aes
|
||||||
|
|
||||||
@(private = "file")
|
@(private = "file")
|
||||||
@@ -34,11 +34,11 @@ ctr_blocks_hw :: proc(ctx: ^Context_CTR, dst, src: []byte, nr_blocks: int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
gcm_seal_hw :: proc(ctx: ^Context_Impl_Hardware, dst, tag, nonce, aad, plaintext: []byte) {
|
gcm_seal_hw :: proc(ctx: ^Context_Impl_Hardware, dst, tag, iv, aad, plaintext: []byte) {
|
||||||
panic(ERR_HW_NOT_SUPPORTED)
|
panic(ERR_HW_NOT_SUPPORTED)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
gcm_open_hw :: proc(ctx: ^Context_Impl_Hardware, dst, nonce, aad, ciphertext, tag: []byte) -> bool {
|
gcm_open_hw :: proc(ctx: ^Context_Impl_Hardware, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
panic(ERR_HW_NOT_SUPPORTED)
|
panic(ERR_HW_NOT_SUPPORTED)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//+build amd64
|
#+build amd64
|
||||||
package aes
|
package aes
|
||||||
|
|
||||||
import "core:crypto/_aes/hw_intel"
|
import "core:crypto/_aes/hw_intel"
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
package blake2b implements the BLAKE2b hash algorithm.
|
package blake2b implements the BLAKE2b hash algorithm.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
- https://datatracker.ietf.org/doc/html/rfc7693
|
- [[ https://datatracker.ietf.org/doc/html/rfc7693 ]]
|
||||||
- https://www.blake2.net
|
- [[ https://www.blake2.net ]]
|
||||||
*/
|
*/
|
||||||
package blake2b
|
package blake2b
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
package blake2s implements the BLAKE2s hash algorithm.
|
package blake2s implements the BLAKE2s hash algorithm.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
- https://datatracker.ietf.org/doc/html/rfc7693
|
- [[ https://datatracker.ietf.org/doc/html/rfc7693 ]]
|
||||||
- https://www.blake2.net/
|
- [[ https://www.blake2.net/ ]]
|
||||||
*/
|
*/
|
||||||
package blake2s
|
package blake2s
|
||||||
|
|
||||||
|
|||||||
@@ -2,125 +2,72 @@
|
|||||||
package chacha20 implements the ChaCha20 and XChaCha20 stream ciphers.
|
package chacha20 implements the ChaCha20 and XChaCha20 stream ciphers.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
- https://datatracker.ietf.org/doc/html/rfc8439
|
- [[ https://datatracker.ietf.org/doc/html/rfc8439 ]]
|
||||||
- https://datatracker.ietf.org/doc/draft-irtf-cfrg-xchacha/03/
|
- [[ https://datatracker.ietf.org/doc/draft-irtf-cfrg-xchacha/03/ ]]
|
||||||
*/
|
*/
|
||||||
package chacha20
|
package chacha20
|
||||||
|
|
||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
import "core:encoding/endian"
|
import "core:crypto/_chacha20"
|
||||||
import "core:math/bits"
|
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
|
|
||||||
// KEY_SIZE is the (X)ChaCha20 key size in bytes.
|
// KEY_SIZE is the (X)ChaCha20 key size in bytes.
|
||||||
KEY_SIZE :: 32
|
KEY_SIZE :: _chacha20.KEY_SIZE
|
||||||
// NONCE_SIZE is the ChaCha20 nonce size in bytes.
|
// IV_SIZE is the ChaCha20 IV size in bytes.
|
||||||
NONCE_SIZE :: 12
|
IV_SIZE :: _chacha20.IV_SIZE
|
||||||
// XNONCE_SIZE is the XChaCha20 nonce size in bytes.
|
// XIV_SIZE is the XChaCha20 IV size in bytes.
|
||||||
XNONCE_SIZE :: 24
|
XIV_SIZE :: _chacha20.XIV_SIZE
|
||||||
|
|
||||||
@(private)
|
|
||||||
_MAX_CTR_IETF :: 0xffffffff
|
|
||||||
|
|
||||||
@(private)
|
|
||||||
_BLOCK_SIZE :: 64
|
|
||||||
@(private)
|
|
||||||
_STATE_SIZE_U32 :: 16
|
|
||||||
@(private)
|
|
||||||
_ROUNDS :: 20
|
|
||||||
|
|
||||||
@(private)
|
|
||||||
_SIGMA_0: u32 : 0x61707865
|
|
||||||
@(private)
|
|
||||||
_SIGMA_1: u32 : 0x3320646e
|
|
||||||
@(private)
|
|
||||||
_SIGMA_2: u32 : 0x79622d32
|
|
||||||
@(private)
|
|
||||||
_SIGMA_3: u32 : 0x6b206574
|
|
||||||
|
|
||||||
// Context is a ChaCha20 or XChaCha20 instance.
|
// Context is a ChaCha20 or XChaCha20 instance.
|
||||||
Context :: struct {
|
Context :: struct {
|
||||||
_s: [_STATE_SIZE_U32]u32,
|
_state: _chacha20.Context,
|
||||||
_buffer: [_BLOCK_SIZE]byte,
|
_impl: Implementation,
|
||||||
_off: int,
|
|
||||||
_is_ietf_flavor: bool,
|
|
||||||
_is_initialized: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// init inititializes a Context for ChaCha20 or XChaCha20 with the provided
|
// init inititializes a Context for ChaCha20 or XChaCha20 with the provided
|
||||||
// key and nonce.
|
// key and iv.
|
||||||
init :: proc(ctx: ^Context, key, nonce: []byte) {
|
init :: proc(ctx: ^Context, key, iv: []byte, impl := DEFAULT_IMPLEMENTATION) {
|
||||||
if len(key) != KEY_SIZE {
|
if len(key) != KEY_SIZE {
|
||||||
panic("crypto/chacha20: invalid ChaCha20 key size")
|
panic("crypto/chacha20: invalid (X)ChaCha20 key size")
|
||||||
}
|
}
|
||||||
if n_len := len(nonce); n_len != NONCE_SIZE && n_len != XNONCE_SIZE {
|
if l := len(iv); l != IV_SIZE && l != XIV_SIZE {
|
||||||
panic("crypto/chacha20: invalid (X)ChaCha20 nonce size")
|
panic("crypto/chacha20: invalid (X)ChaCha20 IV size")
|
||||||
}
|
}
|
||||||
|
|
||||||
k, n := key, nonce
|
k, n := key, iv
|
||||||
|
|
||||||
// Derive the XChaCha20 subkey and sub-nonce via HChaCha20.
|
init_impl(ctx, impl)
|
||||||
is_xchacha := len(nonce) == XNONCE_SIZE
|
|
||||||
|
is_xchacha := len(iv) == XIV_SIZE
|
||||||
if is_xchacha {
|
if is_xchacha {
|
||||||
sub_key := ctx._buffer[:KEY_SIZE]
|
sub_iv: [IV_SIZE]byte
|
||||||
_hchacha20(sub_key, k, n)
|
sub_key := ctx._state._buffer[:KEY_SIZE]
|
||||||
|
hchacha20(sub_key, k, n, ctx._impl)
|
||||||
k = sub_key
|
k = sub_key
|
||||||
n = n[16:24]
|
copy(sub_iv[4:], n[16:])
|
||||||
|
n = sub_iv[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx._s[0] = _SIGMA_0
|
_chacha20.init(&ctx._state, k, n, is_xchacha)
|
||||||
ctx._s[1] = _SIGMA_1
|
|
||||||
ctx._s[2] = _SIGMA_2
|
|
||||||
ctx._s[3] = _SIGMA_3
|
|
||||||
ctx._s[4] = endian.unchecked_get_u32le(k[0:4])
|
|
||||||
ctx._s[5] = endian.unchecked_get_u32le(k[4:8])
|
|
||||||
ctx._s[6] = endian.unchecked_get_u32le(k[8:12])
|
|
||||||
ctx._s[7] = endian.unchecked_get_u32le(k[12:16])
|
|
||||||
ctx._s[8] = endian.unchecked_get_u32le(k[16:20])
|
|
||||||
ctx._s[9] = endian.unchecked_get_u32le(k[20:24])
|
|
||||||
ctx._s[10] = endian.unchecked_get_u32le(k[24:28])
|
|
||||||
ctx._s[11] = endian.unchecked_get_u32le(k[28:32])
|
|
||||||
ctx._s[12] = 0
|
|
||||||
if !is_xchacha {
|
|
||||||
ctx._s[13] = endian.unchecked_get_u32le(n[0:4])
|
|
||||||
ctx._s[14] = endian.unchecked_get_u32le(n[4:8])
|
|
||||||
ctx._s[15] = endian.unchecked_get_u32le(n[8:12])
|
|
||||||
} else {
|
|
||||||
ctx._s[13] = 0
|
|
||||||
ctx._s[14] = endian.unchecked_get_u32le(n[0:4])
|
|
||||||
ctx._s[15] = endian.unchecked_get_u32le(n[4:8])
|
|
||||||
|
|
||||||
|
if is_xchacha {
|
||||||
// The sub-key is stored in the keystream buffer. While
|
// The sub-key is stored in the keystream buffer. While
|
||||||
// this will be overwritten in most circumstances, explicitly
|
// this will be overwritten in most circumstances, explicitly
|
||||||
// clear it out early.
|
// clear it out early.
|
||||||
mem.zero_explicit(&ctx._buffer, KEY_SIZE)
|
mem.zero_explicit(&ctx._state._buffer, KEY_SIZE)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx._off = _BLOCK_SIZE
|
|
||||||
ctx._is_ietf_flavor = !is_xchacha
|
|
||||||
ctx._is_initialized = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// seek seeks the (X)ChaCha20 stream counter to the specified block.
|
// seek seeks the (X)ChaCha20 stream counter to the specified block.
|
||||||
seek :: proc(ctx: ^Context, block_nr: u64) {
|
seek :: proc(ctx: ^Context, block_nr: u64) {
|
||||||
assert(ctx._is_initialized)
|
_chacha20.seek(&ctx._state, block_nr)
|
||||||
|
|
||||||
if ctx._is_ietf_flavor {
|
|
||||||
if block_nr > _MAX_CTR_IETF {
|
|
||||||
panic("crypto/chacha20: attempted to seek past maximum counter")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx._s[13] = u32(block_nr >> 32)
|
|
||||||
}
|
|
||||||
ctx._s[12] = u32(block_nr)
|
|
||||||
ctx._off = _BLOCK_SIZE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// xor_bytes XORs each byte in src with bytes taken from the (X)ChaCha20
|
// xor_bytes XORs each byte in src with bytes taken from the (X)ChaCha20
|
||||||
// keystream, and writes the resulting output to dst. Dst and src MUST
|
// keystream, and writes the resulting output to dst. Dst and src MUST
|
||||||
// alias exactly or not at all.
|
// alias exactly or not at all.
|
||||||
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
||||||
assert(ctx._is_initialized)
|
assert(ctx._state._is_initialized)
|
||||||
|
|
||||||
src, dst := src, dst
|
src, dst := src, dst
|
||||||
if dst_len := len(dst); dst_len < len(src) {
|
if dst_len := len(dst); dst_len < len(src) {
|
||||||
@@ -131,12 +78,13 @@ xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
|||||||
panic("crypto/chacha20: dst and src alias inexactly")
|
panic("crypto/chacha20: dst and src alias inexactly")
|
||||||
}
|
}
|
||||||
|
|
||||||
for remaining := len(src); remaining > 0; {
|
st := &ctx._state
|
||||||
|
#no_bounds_check for remaining := len(src); remaining > 0; {
|
||||||
// Process multiple blocks at once
|
// Process multiple blocks at once
|
||||||
if ctx._off == _BLOCK_SIZE {
|
if st._off == _chacha20.BLOCK_SIZE {
|
||||||
if nr_blocks := remaining / _BLOCK_SIZE; nr_blocks > 0 {
|
if nr_blocks := remaining / _chacha20.BLOCK_SIZE; nr_blocks > 0 {
|
||||||
direct_bytes := nr_blocks * _BLOCK_SIZE
|
direct_bytes := nr_blocks * _chacha20.BLOCK_SIZE
|
||||||
_do_blocks(ctx, dst, src, nr_blocks)
|
stream_blocks(ctx, dst, src, nr_blocks)
|
||||||
remaining -= direct_bytes
|
remaining -= direct_bytes
|
||||||
if remaining == 0 {
|
if remaining == 0 {
|
||||||
return
|
return
|
||||||
@@ -147,17 +95,17 @@ xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
|||||||
|
|
||||||
// If there is a partial block, generate and buffer 1 block
|
// If there is a partial block, generate and buffer 1 block
|
||||||
// worth of keystream.
|
// worth of keystream.
|
||||||
_do_blocks(ctx, ctx._buffer[:], nil, 1)
|
stream_blocks(ctx, st._buffer[:], nil, 1)
|
||||||
ctx._off = 0
|
st._off = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process partial blocks from the buffered keystream.
|
// Process partial blocks from the buffered keystream.
|
||||||
to_xor := min(_BLOCK_SIZE - ctx._off, remaining)
|
to_xor := min(_chacha20.BLOCK_SIZE - st._off, remaining)
|
||||||
buffered_keystream := ctx._buffer[ctx._off:]
|
buffered_keystream := st._buffer[st._off:]
|
||||||
for i := 0; i < to_xor; i = i + 1 {
|
for i := 0; i < to_xor; i = i + 1 {
|
||||||
dst[i] = buffered_keystream[i] ~ src[i]
|
dst[i] = buffered_keystream[i] ~ src[i]
|
||||||
}
|
}
|
||||||
ctx._off += to_xor
|
st._off += to_xor
|
||||||
dst = dst[to_xor:]
|
dst = dst[to_xor:]
|
||||||
src = src[to_xor:]
|
src = src[to_xor:]
|
||||||
remaining -= to_xor
|
remaining -= to_xor
|
||||||
@@ -166,15 +114,15 @@ xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
|||||||
|
|
||||||
// keystream_bytes fills dst with the raw (X)ChaCha20 keystream output.
|
// keystream_bytes fills dst with the raw (X)ChaCha20 keystream output.
|
||||||
keystream_bytes :: proc(ctx: ^Context, dst: []byte) {
|
keystream_bytes :: proc(ctx: ^Context, dst: []byte) {
|
||||||
assert(ctx._is_initialized)
|
assert(ctx._state._is_initialized)
|
||||||
|
|
||||||
dst := dst
|
dst, st := dst, &ctx._state
|
||||||
for remaining := len(dst); remaining > 0; {
|
#no_bounds_check for remaining := len(dst); remaining > 0; {
|
||||||
// Process multiple blocks at once
|
// Process multiple blocks at once
|
||||||
if ctx._off == _BLOCK_SIZE {
|
if st._off == _chacha20.BLOCK_SIZE {
|
||||||
if nr_blocks := remaining / _BLOCK_SIZE; nr_blocks > 0 {
|
if nr_blocks := remaining / _chacha20.BLOCK_SIZE; nr_blocks > 0 {
|
||||||
direct_bytes := nr_blocks * _BLOCK_SIZE
|
direct_bytes := nr_blocks * _chacha20.BLOCK_SIZE
|
||||||
_do_blocks(ctx, dst, nil, nr_blocks)
|
stream_blocks(ctx, dst, nil, nr_blocks)
|
||||||
remaining -= direct_bytes
|
remaining -= direct_bytes
|
||||||
if remaining == 0 {
|
if remaining == 0 {
|
||||||
return
|
return
|
||||||
@@ -184,15 +132,15 @@ keystream_bytes :: proc(ctx: ^Context, dst: []byte) {
|
|||||||
|
|
||||||
// If there is a partial block, generate and buffer 1 block
|
// If there is a partial block, generate and buffer 1 block
|
||||||
// worth of keystream.
|
// worth of keystream.
|
||||||
_do_blocks(ctx, ctx._buffer[:], nil, 1)
|
stream_blocks(ctx, st._buffer[:], nil, 1)
|
||||||
ctx._off = 0
|
st._off = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process partial blocks from the buffered keystream.
|
// Process partial blocks from the buffered keystream.
|
||||||
to_copy := min(_BLOCK_SIZE - ctx._off, remaining)
|
to_copy := min(_chacha20.BLOCK_SIZE - st._off, remaining)
|
||||||
buffered_keystream := ctx._buffer[ctx._off:]
|
buffered_keystream := st._buffer[st._off:]
|
||||||
copy(dst[:to_copy], buffered_keystream[:to_copy])
|
copy(dst[:to_copy], buffered_keystream[:to_copy])
|
||||||
ctx._off += to_copy
|
st._off += to_copy
|
||||||
dst = dst[to_copy:]
|
dst = dst[to_copy:]
|
||||||
remaining -= to_copy
|
remaining -= to_copy
|
||||||
}
|
}
|
||||||
@@ -201,366 +149,5 @@ keystream_bytes :: proc(ctx: ^Context, dst: []byte) {
|
|||||||
// reset sanitizes the Context. The Context must be re-initialized to
|
// reset sanitizes the Context. The Context must be re-initialized to
|
||||||
// be used again.
|
// be used again.
|
||||||
reset :: proc(ctx: ^Context) {
|
reset :: proc(ctx: ^Context) {
|
||||||
mem.zero_explicit(&ctx._s, size_of(ctx._s))
|
_chacha20.reset(&ctx._state)
|
||||||
mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
|
||||||
|
|
||||||
ctx._is_initialized = false
|
|
||||||
}
|
|
||||||
|
|
||||||
@(private)
|
|
||||||
_do_blocks :: proc(ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
|
||||||
// Enforce the maximum consumed keystream per nonce.
|
|
||||||
//
|
|
||||||
// While all modern "standard" definitions of ChaCha20 use
|
|
||||||
// the IETF 32-bit counter, for XChaCha20 most common
|
|
||||||
// implementations allow for a 64-bit counter.
|
|
||||||
//
|
|
||||||
// Honestly, the answer here is "use a MRAE primitive", but
|
|
||||||
// go with common practice in the case of XChaCha20.
|
|
||||||
if ctx._is_ietf_flavor {
|
|
||||||
if u64(ctx._s[12]) + u64(nr_blocks) > 0xffffffff {
|
|
||||||
panic("crypto/chacha20: maximum ChaCha20 keystream per nonce reached")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12])
|
|
||||||
if _, carry := bits.add_u64(ctr, u64(nr_blocks), 0); carry != 0 {
|
|
||||||
panic("crypto/chacha20: maximum XChaCha20 keystream per nonce reached")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dst, src := dst, src
|
|
||||||
x := &ctx._s
|
|
||||||
for n := 0; n < nr_blocks; n = n + 1 {
|
|
||||||
x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
|
|
||||||
x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 := x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]
|
|
||||||
|
|
||||||
for i := _ROUNDS; i > 0; i = i - 2 {
|
|
||||||
// Even when forcing inlining manually inlining all of
|
|
||||||
// these is decently faster.
|
|
||||||
|
|
||||||
// quarterround(x, 0, 4, 8, 12)
|
|
||||||
x0 += x4
|
|
||||||
x12 ~= x0
|
|
||||||
x12 = bits.rotate_left32(x12, 16)
|
|
||||||
x8 += x12
|
|
||||||
x4 ~= x8
|
|
||||||
x4 = bits.rotate_left32(x4, 12)
|
|
||||||
x0 += x4
|
|
||||||
x12 ~= x0
|
|
||||||
x12 = bits.rotate_left32(x12, 8)
|
|
||||||
x8 += x12
|
|
||||||
x4 ~= x8
|
|
||||||
x4 = bits.rotate_left32(x4, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 1, 5, 9, 13)
|
|
||||||
x1 += x5
|
|
||||||
x13 ~= x1
|
|
||||||
x13 = bits.rotate_left32(x13, 16)
|
|
||||||
x9 += x13
|
|
||||||
x5 ~= x9
|
|
||||||
x5 = bits.rotate_left32(x5, 12)
|
|
||||||
x1 += x5
|
|
||||||
x13 ~= x1
|
|
||||||
x13 = bits.rotate_left32(x13, 8)
|
|
||||||
x9 += x13
|
|
||||||
x5 ~= x9
|
|
||||||
x5 = bits.rotate_left32(x5, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 2, 6, 10, 14)
|
|
||||||
x2 += x6
|
|
||||||
x14 ~= x2
|
|
||||||
x14 = bits.rotate_left32(x14, 16)
|
|
||||||
x10 += x14
|
|
||||||
x6 ~= x10
|
|
||||||
x6 = bits.rotate_left32(x6, 12)
|
|
||||||
x2 += x6
|
|
||||||
x14 ~= x2
|
|
||||||
x14 = bits.rotate_left32(x14, 8)
|
|
||||||
x10 += x14
|
|
||||||
x6 ~= x10
|
|
||||||
x6 = bits.rotate_left32(x6, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 3, 7, 11, 15)
|
|
||||||
x3 += x7
|
|
||||||
x15 ~= x3
|
|
||||||
x15 = bits.rotate_left32(x15, 16)
|
|
||||||
x11 += x15
|
|
||||||
x7 ~= x11
|
|
||||||
x7 = bits.rotate_left32(x7, 12)
|
|
||||||
x3 += x7
|
|
||||||
x15 ~= x3
|
|
||||||
x15 = bits.rotate_left32(x15, 8)
|
|
||||||
x11 += x15
|
|
||||||
x7 ~= x11
|
|
||||||
x7 = bits.rotate_left32(x7, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 0, 5, 10, 15)
|
|
||||||
x0 += x5
|
|
||||||
x15 ~= x0
|
|
||||||
x15 = bits.rotate_left32(x15, 16)
|
|
||||||
x10 += x15
|
|
||||||
x5 ~= x10
|
|
||||||
x5 = bits.rotate_left32(x5, 12)
|
|
||||||
x0 += x5
|
|
||||||
x15 ~= x0
|
|
||||||
x15 = bits.rotate_left32(x15, 8)
|
|
||||||
x10 += x15
|
|
||||||
x5 ~= x10
|
|
||||||
x5 = bits.rotate_left32(x5, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 1, 6, 11, 12)
|
|
||||||
x1 += x6
|
|
||||||
x12 ~= x1
|
|
||||||
x12 = bits.rotate_left32(x12, 16)
|
|
||||||
x11 += x12
|
|
||||||
x6 ~= x11
|
|
||||||
x6 = bits.rotate_left32(x6, 12)
|
|
||||||
x1 += x6
|
|
||||||
x12 ~= x1
|
|
||||||
x12 = bits.rotate_left32(x12, 8)
|
|
||||||
x11 += x12
|
|
||||||
x6 ~= x11
|
|
||||||
x6 = bits.rotate_left32(x6, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 2, 7, 8, 13)
|
|
||||||
x2 += x7
|
|
||||||
x13 ~= x2
|
|
||||||
x13 = bits.rotate_left32(x13, 16)
|
|
||||||
x8 += x13
|
|
||||||
x7 ~= x8
|
|
||||||
x7 = bits.rotate_left32(x7, 12)
|
|
||||||
x2 += x7
|
|
||||||
x13 ~= x2
|
|
||||||
x13 = bits.rotate_left32(x13, 8)
|
|
||||||
x8 += x13
|
|
||||||
x7 ~= x8
|
|
||||||
x7 = bits.rotate_left32(x7, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 3, 4, 9, 14)
|
|
||||||
x3 += x4
|
|
||||||
x14 ~= x3
|
|
||||||
x14 = bits.rotate_left32(x14, 16)
|
|
||||||
x9 += x14
|
|
||||||
x4 ~= x9
|
|
||||||
x4 = bits.rotate_left32(x4, 12)
|
|
||||||
x3 += x4
|
|
||||||
x14 ~= x3
|
|
||||||
x14 = bits.rotate_left32(x14, 8)
|
|
||||||
x9 += x14
|
|
||||||
x4 ~= x9
|
|
||||||
x4 = bits.rotate_left32(x4, 7)
|
|
||||||
}
|
|
||||||
|
|
||||||
x0 += _SIGMA_0
|
|
||||||
x1 += _SIGMA_1
|
|
||||||
x2 += _SIGMA_2
|
|
||||||
x3 += _SIGMA_3
|
|
||||||
x4 += x[4]
|
|
||||||
x5 += x[5]
|
|
||||||
x6 += x[6]
|
|
||||||
x7 += x[7]
|
|
||||||
x8 += x[8]
|
|
||||||
x9 += x[9]
|
|
||||||
x10 += x[10]
|
|
||||||
x11 += x[11]
|
|
||||||
x12 += x[12]
|
|
||||||
x13 += x[13]
|
|
||||||
x14 += x[14]
|
|
||||||
x15 += x[15]
|
|
||||||
|
|
||||||
// While the "correct" answer to getting more performance out of
|
|
||||||
// this is "use vector operations", support for that is currently
|
|
||||||
// a work in progress/to be designed.
|
|
||||||
//
|
|
||||||
// In the meantime:
|
|
||||||
// - The caller(s) ensure that src/dst are valid.
|
|
||||||
// - The compiler knows if the target is picky about alignment.
|
|
||||||
|
|
||||||
#no_bounds_check {
|
|
||||||
if src != nil {
|
|
||||||
endian.unchecked_put_u32le(dst[0:4], endian.unchecked_get_u32le(src[0:4]) ~ x0)
|
|
||||||
endian.unchecked_put_u32le(dst[4:8], endian.unchecked_get_u32le(src[4:8]) ~ x1)
|
|
||||||
endian.unchecked_put_u32le(dst[8:12], endian.unchecked_get_u32le(src[8:12]) ~ x2)
|
|
||||||
endian.unchecked_put_u32le(dst[12:16], endian.unchecked_get_u32le(src[12:16]) ~ x3)
|
|
||||||
endian.unchecked_put_u32le(dst[16:20], endian.unchecked_get_u32le(src[16:20]) ~ x4)
|
|
||||||
endian.unchecked_put_u32le(dst[20:24], endian.unchecked_get_u32le(src[20:24]) ~ x5)
|
|
||||||
endian.unchecked_put_u32le(dst[24:28], endian.unchecked_get_u32le(src[24:28]) ~ x6)
|
|
||||||
endian.unchecked_put_u32le(dst[28:32], endian.unchecked_get_u32le(src[28:32]) ~ x7)
|
|
||||||
endian.unchecked_put_u32le(dst[32:36], endian.unchecked_get_u32le(src[32:36]) ~ x8)
|
|
||||||
endian.unchecked_put_u32le(dst[36:40], endian.unchecked_get_u32le(src[36:40]) ~ x9)
|
|
||||||
endian.unchecked_put_u32le(dst[40:44], endian.unchecked_get_u32le(src[40:44]) ~ x10)
|
|
||||||
endian.unchecked_put_u32le(dst[44:48], endian.unchecked_get_u32le(src[44:48]) ~ x11)
|
|
||||||
endian.unchecked_put_u32le(dst[48:52], endian.unchecked_get_u32le(src[48:52]) ~ x12)
|
|
||||||
endian.unchecked_put_u32le(dst[52:56], endian.unchecked_get_u32le(src[52:56]) ~ x13)
|
|
||||||
endian.unchecked_put_u32le(dst[56:60], endian.unchecked_get_u32le(src[56:60]) ~ x14)
|
|
||||||
endian.unchecked_put_u32le(dst[60:64], endian.unchecked_get_u32le(src[60:64]) ~ x15)
|
|
||||||
src = src[_BLOCK_SIZE:]
|
|
||||||
} else {
|
|
||||||
endian.unchecked_put_u32le(dst[0:4], x0)
|
|
||||||
endian.unchecked_put_u32le(dst[4:8], x1)
|
|
||||||
endian.unchecked_put_u32le(dst[8:12], x2)
|
|
||||||
endian.unchecked_put_u32le(dst[12:16], x3)
|
|
||||||
endian.unchecked_put_u32le(dst[16:20], x4)
|
|
||||||
endian.unchecked_put_u32le(dst[20:24], x5)
|
|
||||||
endian.unchecked_put_u32le(dst[24:28], x6)
|
|
||||||
endian.unchecked_put_u32le(dst[28:32], x7)
|
|
||||||
endian.unchecked_put_u32le(dst[32:36], x8)
|
|
||||||
endian.unchecked_put_u32le(dst[36:40], x9)
|
|
||||||
endian.unchecked_put_u32le(dst[40:44], x10)
|
|
||||||
endian.unchecked_put_u32le(dst[44:48], x11)
|
|
||||||
endian.unchecked_put_u32le(dst[48:52], x12)
|
|
||||||
endian.unchecked_put_u32le(dst[52:56], x13)
|
|
||||||
endian.unchecked_put_u32le(dst[56:60], x14)
|
|
||||||
endian.unchecked_put_u32le(dst[60:64], x15)
|
|
||||||
}
|
|
||||||
dst = dst[_BLOCK_SIZE:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment the counter. Overflow checking is done upon
|
|
||||||
// entry into the routine, so a 64-bit increment safely
|
|
||||||
// covers both cases.
|
|
||||||
new_ctr := ((u64(ctx._s[13]) << 32) | u64(ctx._s[12])) + 1
|
|
||||||
x[12] = u32(new_ctr)
|
|
||||||
x[13] = u32(new_ctr >> 32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@(private)
|
|
||||||
_hchacha20 :: proc "contextless" (dst, key, nonce: []byte) {
|
|
||||||
x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
|
|
||||||
x4 := endian.unchecked_get_u32le(key[0:4])
|
|
||||||
x5 := endian.unchecked_get_u32le(key[4:8])
|
|
||||||
x6 := endian.unchecked_get_u32le(key[8:12])
|
|
||||||
x7 := endian.unchecked_get_u32le(key[12:16])
|
|
||||||
x8 := endian.unchecked_get_u32le(key[16:20])
|
|
||||||
x9 := endian.unchecked_get_u32le(key[20:24])
|
|
||||||
x10 := endian.unchecked_get_u32le(key[24:28])
|
|
||||||
x11 := endian.unchecked_get_u32le(key[28:32])
|
|
||||||
x12 := endian.unchecked_get_u32le(nonce[0:4])
|
|
||||||
x13 := endian.unchecked_get_u32le(nonce[4:8])
|
|
||||||
x14 := endian.unchecked_get_u32le(nonce[8:12])
|
|
||||||
x15 := endian.unchecked_get_u32le(nonce[12:16])
|
|
||||||
|
|
||||||
for i := _ROUNDS; i > 0; i = i - 2 {
|
|
||||||
// quarterround(x, 0, 4, 8, 12)
|
|
||||||
x0 += x4
|
|
||||||
x12 ~= x0
|
|
||||||
x12 = bits.rotate_left32(x12, 16)
|
|
||||||
x8 += x12
|
|
||||||
x4 ~= x8
|
|
||||||
x4 = bits.rotate_left32(x4, 12)
|
|
||||||
x0 += x4
|
|
||||||
x12 ~= x0
|
|
||||||
x12 = bits.rotate_left32(x12, 8)
|
|
||||||
x8 += x12
|
|
||||||
x4 ~= x8
|
|
||||||
x4 = bits.rotate_left32(x4, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 1, 5, 9, 13)
|
|
||||||
x1 += x5
|
|
||||||
x13 ~= x1
|
|
||||||
x13 = bits.rotate_left32(x13, 16)
|
|
||||||
x9 += x13
|
|
||||||
x5 ~= x9
|
|
||||||
x5 = bits.rotate_left32(x5, 12)
|
|
||||||
x1 += x5
|
|
||||||
x13 ~= x1
|
|
||||||
x13 = bits.rotate_left32(x13, 8)
|
|
||||||
x9 += x13
|
|
||||||
x5 ~= x9
|
|
||||||
x5 = bits.rotate_left32(x5, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 2, 6, 10, 14)
|
|
||||||
x2 += x6
|
|
||||||
x14 ~= x2
|
|
||||||
x14 = bits.rotate_left32(x14, 16)
|
|
||||||
x10 += x14
|
|
||||||
x6 ~= x10
|
|
||||||
x6 = bits.rotate_left32(x6, 12)
|
|
||||||
x2 += x6
|
|
||||||
x14 ~= x2
|
|
||||||
x14 = bits.rotate_left32(x14, 8)
|
|
||||||
x10 += x14
|
|
||||||
x6 ~= x10
|
|
||||||
x6 = bits.rotate_left32(x6, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 3, 7, 11, 15)
|
|
||||||
x3 += x7
|
|
||||||
x15 ~= x3
|
|
||||||
x15 = bits.rotate_left32(x15, 16)
|
|
||||||
x11 += x15
|
|
||||||
x7 ~= x11
|
|
||||||
x7 = bits.rotate_left32(x7, 12)
|
|
||||||
x3 += x7
|
|
||||||
x15 ~= x3
|
|
||||||
x15 = bits.rotate_left32(x15, 8)
|
|
||||||
x11 += x15
|
|
||||||
x7 ~= x11
|
|
||||||
x7 = bits.rotate_left32(x7, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 0, 5, 10, 15)
|
|
||||||
x0 += x5
|
|
||||||
x15 ~= x0
|
|
||||||
x15 = bits.rotate_left32(x15, 16)
|
|
||||||
x10 += x15
|
|
||||||
x5 ~= x10
|
|
||||||
x5 = bits.rotate_left32(x5, 12)
|
|
||||||
x0 += x5
|
|
||||||
x15 ~= x0
|
|
||||||
x15 = bits.rotate_left32(x15, 8)
|
|
||||||
x10 += x15
|
|
||||||
x5 ~= x10
|
|
||||||
x5 = bits.rotate_left32(x5, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 1, 6, 11, 12)
|
|
||||||
x1 += x6
|
|
||||||
x12 ~= x1
|
|
||||||
x12 = bits.rotate_left32(x12, 16)
|
|
||||||
x11 += x12
|
|
||||||
x6 ~= x11
|
|
||||||
x6 = bits.rotate_left32(x6, 12)
|
|
||||||
x1 += x6
|
|
||||||
x12 ~= x1
|
|
||||||
x12 = bits.rotate_left32(x12, 8)
|
|
||||||
x11 += x12
|
|
||||||
x6 ~= x11
|
|
||||||
x6 = bits.rotate_left32(x6, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 2, 7, 8, 13)
|
|
||||||
x2 += x7
|
|
||||||
x13 ~= x2
|
|
||||||
x13 = bits.rotate_left32(x13, 16)
|
|
||||||
x8 += x13
|
|
||||||
x7 ~= x8
|
|
||||||
x7 = bits.rotate_left32(x7, 12)
|
|
||||||
x2 += x7
|
|
||||||
x13 ~= x2
|
|
||||||
x13 = bits.rotate_left32(x13, 8)
|
|
||||||
x8 += x13
|
|
||||||
x7 ~= x8
|
|
||||||
x7 = bits.rotate_left32(x7, 7)
|
|
||||||
|
|
||||||
// quarterround(x, 3, 4, 9, 14)
|
|
||||||
x3 += x4
|
|
||||||
x14 ~= x3
|
|
||||||
x14 = bits.rotate_left32(x14, 16)
|
|
||||||
x9 += x14
|
|
||||||
x4 ~= x9
|
|
||||||
x4 = bits.rotate_left32(x4, 12)
|
|
||||||
x3 += x4
|
|
||||||
x14 ~= x3
|
|
||||||
x14 = bits.rotate_left32(x14, 8)
|
|
||||||
x9 += x14
|
|
||||||
x4 ~= x9
|
|
||||||
x4 = bits.rotate_left32(x4, 7)
|
|
||||||
}
|
|
||||||
|
|
||||||
endian.unchecked_put_u32le(dst[0:4], x0)
|
|
||||||
endian.unchecked_put_u32le(dst[4:8], x1)
|
|
||||||
endian.unchecked_put_u32le(dst[8:12], x2)
|
|
||||||
endian.unchecked_put_u32le(dst[12:16], x3)
|
|
||||||
endian.unchecked_put_u32le(dst[16:20], x12)
|
|
||||||
endian.unchecked_put_u32le(dst[20:24], x13)
|
|
||||||
endian.unchecked_put_u32le(dst[24:28], x14)
|
|
||||||
endian.unchecked_put_u32le(dst[28:32], x15)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package chacha20
|
||||||
|
|
||||||
|
import "base:intrinsics"
|
||||||
|
import "core:crypto/_chacha20/ref"
|
||||||
|
import "core:crypto/_chacha20/simd128"
|
||||||
|
import "core:crypto/_chacha20/simd256"
|
||||||
|
|
||||||
|
// DEFAULT_IMPLEMENTATION is the implementation that will be used by
|
||||||
|
// default if possible.
|
||||||
|
DEFAULT_IMPLEMENTATION :: Implementation.Simd256
|
||||||
|
|
||||||
|
// Implementation is a ChaCha20 implementation. Most callers will not need
|
||||||
|
// to use this as the package will automatically select the most performant
|
||||||
|
// implementation available.
|
||||||
|
Implementation :: enum {
|
||||||
|
Portable,
|
||||||
|
Simd128,
|
||||||
|
Simd256,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
init_impl :: proc(ctx: ^Context, impl: Implementation) {
|
||||||
|
impl := impl
|
||||||
|
if impl == .Simd256 && !simd256.is_performant() {
|
||||||
|
impl = .Simd128
|
||||||
|
}
|
||||||
|
if impl == .Simd128 && !simd128.is_performant() {
|
||||||
|
impl = .Portable
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx._impl = impl
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
stream_blocks :: proc(ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
||||||
|
switch ctx._impl {
|
||||||
|
case .Simd256:
|
||||||
|
simd256.stream_blocks(&ctx._state, dst, src, nr_blocks)
|
||||||
|
case .Simd128:
|
||||||
|
simd128.stream_blocks(&ctx._state, dst, src, nr_blocks)
|
||||||
|
case .Portable:
|
||||||
|
ref.stream_blocks(&ctx._state, dst, src, nr_blocks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@(private)
|
||||||
|
hchacha20 :: proc "contextless" (dst, key, iv: []byte, impl: Implementation) {
|
||||||
|
switch impl {
|
||||||
|
case .Simd256:
|
||||||
|
simd256.hchacha20(dst, key, iv)
|
||||||
|
case .Simd128:
|
||||||
|
simd128.hchacha20(dst, key, iv)
|
||||||
|
case .Portable:
|
||||||
|
ref.hchacha20(dst, key, iv)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
package chacha20poly1305 implements the AEAD_CHACHA20_POLY1305 Authenticated
|
package chacha20poly1305 implements the AEAD_CHACHA20_POLY1305 and
|
||||||
Encryption with Additional Data algorithm.
|
AEAD_XChaCha20_Poly1305 Authenticated Encryption with Additional Data
|
||||||
|
algorithms.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
- https://www.rfc-editor.org/rfc/rfc8439
|
- [[ https://www.rfc-editor.org/rfc/rfc8439 ]]
|
||||||
|
- [[ https://datatracker.ietf.org/doc/html/draft-arciszewski-xchacha-03 ]]
|
||||||
*/
|
*/
|
||||||
package chacha20poly1305
|
package chacha20poly1305
|
||||||
|
|
||||||
@@ -15,8 +17,10 @@ import "core:mem"
|
|||||||
|
|
||||||
// KEY_SIZE is the chacha20poly1305 key size in bytes.
|
// KEY_SIZE is the chacha20poly1305 key size in bytes.
|
||||||
KEY_SIZE :: chacha20.KEY_SIZE
|
KEY_SIZE :: chacha20.KEY_SIZE
|
||||||
// NONCE_SIZE is the chacha20poly1305 nonce size in bytes.
|
// IV_SIZE is the chacha20poly1305 IV size in bytes.
|
||||||
NONCE_SIZE :: chacha20.NONCE_SIZE
|
IV_SIZE :: chacha20.IV_SIZE
|
||||||
|
// XIV_SIZE is the xchacha20poly1305 IV size in bytes.
|
||||||
|
XIV_SIZE :: chacha20.XIV_SIZE
|
||||||
// TAG_SIZE is the chacha20poly1305 tag size in bytes.
|
// TAG_SIZE is the chacha20poly1305 tag size in bytes.
|
||||||
TAG_SIZE :: poly1305.TAG_SIZE
|
TAG_SIZE :: poly1305.TAG_SIZE
|
||||||
|
|
||||||
@@ -24,15 +28,13 @@ TAG_SIZE :: poly1305.TAG_SIZE
|
|||||||
_P_MAX :: 64 * 0xffffffff // 64 * (2^32-1)
|
_P_MAX :: 64 * 0xffffffff // 64 * (2^32-1)
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
_validate_common_slice_sizes :: proc (tag, key, nonce, aad, text: []byte) {
|
_validate_common_slice_sizes :: proc (tag, iv, aad, text: []byte, is_xchacha: bool) {
|
||||||
if len(tag) != TAG_SIZE {
|
if len(tag) != TAG_SIZE {
|
||||||
panic("crypto/chacha20poly1305: invalid destination tag size")
|
panic("crypto/chacha20poly1305: invalid destination tag size")
|
||||||
}
|
}
|
||||||
if len(key) != KEY_SIZE {
|
expected_iv_len := is_xchacha ? XIV_SIZE : IV_SIZE
|
||||||
panic("crypto/chacha20poly1305: invalid key size")
|
if len(iv) != expected_iv_len {
|
||||||
}
|
panic("crypto/chacha20poly1305: invalid IV size")
|
||||||
if len(nonce) != NONCE_SIZE {
|
|
||||||
panic("crypto/chacha20poly1305: invalid nonce size")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#assert(size_of(int) == 8 || size_of(int) <= 4)
|
#assert(size_of(int) == 8 || size_of(int) <= 4)
|
||||||
@@ -59,18 +61,52 @@ _update_mac_pad16 :: #force_inline proc (ctx: ^poly1305.Context, x_len: int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt encrypts the plaintext and authenticates the aad and ciphertext,
|
// Context is a keyed (X)Chacha20Poly1305 instance.
|
||||||
// with the provided key and nonce, stores the output in ciphertext and tag.
|
Context :: struct {
|
||||||
encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
|
_key: [KEY_SIZE]byte,
|
||||||
_validate_common_slice_sizes(tag, key, nonce, aad, plaintext)
|
_impl: chacha20.Implementation,
|
||||||
|
_is_xchacha: bool,
|
||||||
|
_is_initialized: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes a Context with the provided key, for AEAD_CHACHA20_POLY1305.
|
||||||
|
init :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEMENTATION) {
|
||||||
|
if len(key) != KEY_SIZE {
|
||||||
|
panic("crypto/chacha20poly1305: invalid key size")
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(ctx._key[:], key)
|
||||||
|
ctx._impl = impl
|
||||||
|
ctx._is_xchacha = false
|
||||||
|
ctx._is_initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// init_xchacha initializes a Context with the provided key, for
|
||||||
|
// AEAD_XChaCha20_Poly1305.
|
||||||
|
//
|
||||||
|
// Note: While there are multiple definitions of XChaCha20-Poly1305
|
||||||
|
// this sticks to the IETF draft and uses a 32-bit counter.
|
||||||
|
init_xchacha :: proc(ctx: ^Context, key: []byte, impl := chacha20.DEFAULT_IMPLEMENTATION) {
|
||||||
|
init(ctx, key, impl)
|
||||||
|
ctx._is_xchacha = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// seal encrypts the plaintext and authenticates the aad and ciphertext,
|
||||||
|
// with the provided Context and iv, stores the output in dst and tag.
|
||||||
|
//
|
||||||
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
|
seal :: proc(ctx: ^Context, dst, tag, iv, aad, plaintext: []byte) {
|
||||||
|
ciphertext := dst
|
||||||
|
_validate_common_slice_sizes(tag, iv, aad, plaintext, ctx._is_xchacha)
|
||||||
if len(ciphertext) != len(plaintext) {
|
if len(ciphertext) != len(plaintext) {
|
||||||
panic("crypto/chacha20poly1305: invalid destination ciphertext size")
|
panic("crypto/chacha20poly1305: invalid destination ciphertext size")
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_ctx: chacha20.Context = ---
|
stream_ctx: chacha20.Context = ---
|
||||||
chacha20.init(&stream_ctx, key, nonce)
|
chacha20.init(&stream_ctx, ctx._key[:],iv, ctx._impl)
|
||||||
|
stream_ctx._state._is_ietf_flavor = true
|
||||||
|
|
||||||
// otk = poly1305_key_gen(key, nonce)
|
// otk = poly1305_key_gen(key, iv)
|
||||||
otk: [poly1305.KEY_SIZE]byte = ---
|
otk: [poly1305.KEY_SIZE]byte = ---
|
||||||
chacha20.keystream_bytes(&stream_ctx, otk[:])
|
chacha20.keystream_bytes(&stream_ctx, otk[:])
|
||||||
mac_ctx: poly1305.Context = ---
|
mac_ctx: poly1305.Context = ---
|
||||||
@@ -87,7 +123,7 @@ encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
|
|||||||
poly1305.update(&mac_ctx, aad)
|
poly1305.update(&mac_ctx, aad)
|
||||||
_update_mac_pad16(&mac_ctx, aad_len)
|
_update_mac_pad16(&mac_ctx, aad_len)
|
||||||
|
|
||||||
// ciphertext = chacha20_encrypt(key, 1, nonce, plaintext)
|
// ciphertext = chacha20_encrypt(key, 1, iv, plaintext)
|
||||||
chacha20.seek(&stream_ctx, 1)
|
chacha20.seek(&stream_ctx, 1)
|
||||||
chacha20.xor_bytes(&stream_ctx, ciphertext, plaintext)
|
chacha20.xor_bytes(&stream_ctx, ciphertext, plaintext)
|
||||||
chacha20.reset(&stream_ctx) // Don't need the stream context anymore.
|
chacha20.reset(&stream_ctx) // Don't need the stream context anymore.
|
||||||
@@ -107,13 +143,16 @@ encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
|
|||||||
poly1305.final(&mac_ctx, tag) // Implicitly sanitizes context.
|
poly1305.final(&mac_ctx, tag) // Implicitly sanitizes context.
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrypt authenticates the aad and ciphertext, and decrypts the ciphertext,
|
// open authenticates the aad and ciphertext, and decrypts the ciphertext,
|
||||||
// with the provided key, nonce, and tag, and stores the output in plaintext,
|
// with the provided Context, iv, and tag, and stores the output in dst,
|
||||||
// returning true iff the authentication was successful.
|
// returning true iff the authentication was successful. If authentication
|
||||||
|
// fails, the destination buffer will be zeroed.
|
||||||
//
|
//
|
||||||
// If authentication fails, the destination plaintext buffer will be zeroed.
|
// dst and plaintext MUST alias exactly or not at all.
|
||||||
decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool {
|
@(require_results)
|
||||||
_validate_common_slice_sizes(tag, key, nonce, aad, ciphertext)
|
open :: proc(ctx: ^Context, dst, iv, aad, ciphertext, tag: []byte) -> bool {
|
||||||
|
plaintext := dst
|
||||||
|
_validate_common_slice_sizes(tag, iv, aad, ciphertext, ctx._is_xchacha)
|
||||||
if len(ciphertext) != len(plaintext) {
|
if len(ciphertext) != len(plaintext) {
|
||||||
panic("crypto/chacha20poly1305: invalid destination plaintext size")
|
panic("crypto/chacha20poly1305: invalid destination plaintext size")
|
||||||
}
|
}
|
||||||
@@ -123,9 +162,10 @@ decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool {
|
|||||||
// points where needed.
|
// points where needed.
|
||||||
|
|
||||||
stream_ctx: chacha20.Context = ---
|
stream_ctx: chacha20.Context = ---
|
||||||
chacha20.init(&stream_ctx, key, nonce)
|
chacha20.init(&stream_ctx, ctx._key[:], iv, ctx._impl)
|
||||||
|
stream_ctx._state._is_ietf_flavor = true
|
||||||
|
|
||||||
// otk = poly1305_key_gen(key, nonce)
|
// otk = poly1305_key_gen(key, iv)
|
||||||
otk: [poly1305.KEY_SIZE]byte = ---
|
otk: [poly1305.KEY_SIZE]byte = ---
|
||||||
chacha20.keystream_bytes(&stream_ctx, otk[:])
|
chacha20.keystream_bytes(&stream_ctx, otk[:])
|
||||||
defer chacha20.reset(&stream_ctx)
|
defer chacha20.reset(&stream_ctx)
|
||||||
@@ -160,9 +200,17 @@ decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// plaintext = chacha20_decrypt(key, 1, nonce, ciphertext)
|
// plaintext = chacha20_decrypt(key, 1, iv, ciphertext)
|
||||||
chacha20.seek(&stream_ctx, 1)
|
chacha20.seek(&stream_ctx, 1)
|
||||||
chacha20.xor_bytes(&stream_ctx, plaintext, ciphertext)
|
chacha20.xor_bytes(&stream_ctx, plaintext, ciphertext)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset sanitizes the Context. The Context must be
|
||||||
|
// re-initialized to be used again.
|
||||||
|
reset :: proc "contextless" (ctx: ^Context) {
|
||||||
|
mem.zero_explicit(&ctx._key, len(ctx._key))
|
||||||
|
ctx._is_xchacha = false
|
||||||
|
ctx._is_initialized = false
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
package ed25519 implements the Ed25519 EdDSA signature algorithm.
|
package ed25519 implements the Ed25519 EdDSA signature algorithm.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
- https://datatracker.ietf.org/doc/html/rfc8032
|
- [[ https://datatracker.ietf.org/doc/html/rfc8032 ]]
|
||||||
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
|
- [[ https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf ]]
|
||||||
- https://eprint.iacr.org/2020/1244.pdf
|
- [[ https://eprint.iacr.org/2020/1244.pdf ]]
|
||||||
*/
|
*/
|
||||||
package ed25519
|
package ed25519
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ PUBLIC_KEY_SIZE :: 32
|
|||||||
SIGNATURE_SIZE :: 64
|
SIGNATURE_SIZE :: 64
|
||||||
|
|
||||||
@(private)
|
@(private)
|
||||||
NONCE_SIZE :: 32
|
HDIGEST2_SIZE :: 32
|
||||||
|
|
||||||
// Private_Key is an Ed25519 private key.
|
// Private_Key is an Ed25519 private key.
|
||||||
Private_Key :: struct {
|
Private_Key :: struct {
|
||||||
@@ -33,7 +33,7 @@ Private_Key :: struct {
|
|||||||
// See: https://github.com/MystenLabs/ed25519-unsafe-libs
|
// See: https://github.com/MystenLabs/ed25519-unsafe-libs
|
||||||
_b: [PRIVATE_KEY_SIZE]byte,
|
_b: [PRIVATE_KEY_SIZE]byte,
|
||||||
_s: grp.Scalar,
|
_s: grp.Scalar,
|
||||||
_nonce: [NONCE_SIZE]byte,
|
_hdigest2: [HDIGEST2_SIZE]byte,
|
||||||
_pub_key: Public_Key,
|
_pub_key: Public_Key,
|
||||||
_is_initialized: bool,
|
_is_initialized: bool,
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool {
|
|||||||
sha2.final(&ctx, h_bytes[:])
|
sha2.final(&ctx, h_bytes[:])
|
||||||
|
|
||||||
copy(priv_key._b[:], b)
|
copy(priv_key._b[:], b)
|
||||||
copy(priv_key._nonce[:], h_bytes[32:])
|
copy(priv_key._hdigest2[:], h_bytes[32:])
|
||||||
grp.sc_set_bytes_rfc8032(&priv_key._s, h_bytes[:32])
|
grp.sc_set_bytes_rfc8032(&priv_key._s, h_bytes[:32])
|
||||||
|
|
||||||
// Derive the corresponding public key.
|
// Derive the corresponding public key.
|
||||||
@@ -116,7 +116,7 @@ sign :: proc(priv_key: ^Private_Key, msg, sig: []byte) {
|
|||||||
ctx: sha2.Context_512 = ---
|
ctx: sha2.Context_512 = ---
|
||||||
digest_bytes: [sha2.DIGEST_SIZE_512]byte = ---
|
digest_bytes: [sha2.DIGEST_SIZE_512]byte = ---
|
||||||
sha2.init_512(&ctx)
|
sha2.init_512(&ctx)
|
||||||
sha2.update(&ctx, priv_key._nonce[:])
|
sha2.update(&ctx, priv_key._hdigest2[:])
|
||||||
sha2.update(&ctx, msg)
|
sha2.update(&ctx, msg)
|
||||||
sha2.final(&ctx, digest_bytes[:])
|
sha2.final(&ctx, digest_bytes[:])
|
||||||
|
|
||||||
|
|||||||
+28
-30
@@ -17,46 +17,44 @@ accomplish common tasks.
|
|||||||
A third optional boolean parameter controls if the file is streamed
|
A third optional boolean parameter controls if the file is streamed
|
||||||
(default), or or read at once.
|
(default), or or read at once.
|
||||||
|
|
||||||
```odin
|
Example:
|
||||||
package hash_example
|
package hash_example
|
||||||
|
|
||||||
import "core:crypto/hash"
|
import "core:crypto/hash"
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
input := "Feed the fire."
|
input := "Feed the fire."
|
||||||
|
|
||||||
// Compute the digest, using the high level API.
|
// Compute the digest, using the high level API.
|
||||||
returned_digest := hash.hash(hash.Algorithm.SHA512_256, input)
|
returned_digest := hash.hash(hash.Algorithm.SHA512_256, input)
|
||||||
defer delete(returned_digest)
|
defer delete(returned_digest)
|
||||||
|
|
||||||
// Variant that takes a destination buffer, instead of returning
|
// Variant that takes a destination buffer, instead of returning
|
||||||
// the digest.
|
// the digest.
|
||||||
digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.BLAKE2B]) // @note: Destination buffer has to be at least as big as the digest size of the hash.
|
digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.BLAKE2B]) // @note: Destination buffer has to be at least as big as the digest size of the hash.
|
||||||
defer delete(digest)
|
defer delete(digest)
|
||||||
hash.hash(hash.Algorithm.BLAKE2B, input, digest)
|
hash.hash(hash.Algorithm.BLAKE2B, input, digest)
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
A generic low level API is provided supporting the init/update/final interface
|
A generic low level API is provided supporting the init/update/final interface
|
||||||
that is typical with cryptographic hash function implementations.
|
that is typical with cryptographic hash function implementations.
|
||||||
|
|
||||||
```odin
|
Example:
|
||||||
package hash_example
|
package hash_example
|
||||||
|
|
||||||
import "core:crypto/hash"
|
import "core:crypto/hash"
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
input := "Let the cinders burn."
|
input := "Let the cinders burn."
|
||||||
|
|
||||||
// Compute the digest, using the low level API.
|
// Compute the digest, using the low level API.
|
||||||
ctx: hash.Context
|
ctx: hash.Context
|
||||||
digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.SHA3_512])
|
digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.SHA3_512])
|
||||||
defer delete(digest)
|
defer delete(digest)
|
||||||
|
|
||||||
hash.init(&ctx, hash.Algorithm.SHA3_512)
|
hash.init(&ctx, hash.Algorithm.SHA3_512)
|
||||||
hash.update(&ctx, transmute([]byte)input)
|
hash.update(&ctx, transmute([]byte)input)
|
||||||
hash.final(&ctx, digest)
|
hash.final(&ctx, digest)
|
||||||
}
|
}
|
||||||
```
|
|
||||||
*/
|
*/
|
||||||
package crypto_hash
|
package crypto_hash
|
||||||
|
|||||||
+15
-43
@@ -1,16 +1,15 @@
|
|||||||
package crypto_hash
|
package crypto_hash
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2021 zhibog
|
Copyright 2021 zhibog
|
||||||
Made available under the BSD-3 license.
|
Made available under the BSD-3 license.
|
||||||
|
|
||||||
List of contributors:
|
List of contributors:
|
||||||
zhibog, dotbmp: Initial implementation.
|
zhibog, dotbmp: Initial implementation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:mem"
|
import "core:mem"
|
||||||
import "core:os"
|
|
||||||
|
|
||||||
// hash_bytes will hash the given input and return the computed digest
|
// hash_bytes will hash the given input and return the computed digest
|
||||||
// in a newly allocated slice.
|
// in a newly allocated slice.
|
||||||
@@ -28,20 +27,26 @@ hash_bytes :: proc(algorithm: Algorithm, data: []byte, allocator := context.allo
|
|||||||
|
|
||||||
// hash_string_to_buffer will hash the given input and assign the
|
// hash_string_to_buffer will hash the given input and assign the
|
||||||
// computed digest to the third parameter. It requires that the
|
// computed digest to the third parameter. It requires that the
|
||||||
// destination buffer is at least as big as the digest size.
|
// destination buffer is at least as big as the digest size. The
|
||||||
hash_string_to_buffer :: proc(algorithm: Algorithm, data: string, hash: []byte) {
|
// provided destination buffer is returned to match the behavior of
|
||||||
hash_bytes_to_buffer(algorithm, transmute([]byte)(data), hash)
|
// `hash_string`.
|
||||||
|
hash_string_to_buffer :: proc(algorithm: Algorithm, data: string, hash: []byte) -> []byte {
|
||||||
|
return hash_bytes_to_buffer(algorithm, transmute([]byte)(data), hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash_bytes_to_buffer will hash the given input and write the
|
// hash_bytes_to_buffer will hash the given input and write the
|
||||||
// computed digest into the third parameter. It requires that the
|
// computed digest into the third parameter. It requires that the
|
||||||
// destination buffer is at least as big as the digest size.
|
// destination buffer is at least as big as the digest size. The
|
||||||
hash_bytes_to_buffer :: proc(algorithm: Algorithm, data, hash: []byte) {
|
// provided destination buffer is returned to match the behavior of
|
||||||
|
// `hash_bytes`.
|
||||||
|
hash_bytes_to_buffer :: proc(algorithm: Algorithm, data, hash: []byte) -> []byte {
|
||||||
ctx: Context
|
ctx: Context
|
||||||
|
|
||||||
init(&ctx, algorithm)
|
init(&ctx, algorithm)
|
||||||
update(&ctx, data)
|
update(&ctx, data)
|
||||||
final(&ctx, hash)
|
final(&ctx, hash)
|
||||||
|
|
||||||
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash_stream will incrementally fully consume a stream, and return the
|
// hash_stream will incrementally fully consume a stream, and return the
|
||||||
@@ -81,36 +86,3 @@ hash_stream :: proc(
|
|||||||
|
|
||||||
return dst, io.Error.None
|
return dst, io.Error.None
|
||||||
}
|
}
|
||||||
|
|
||||||
// hash_file will read the file provided by the given handle and return the
|
|
||||||
// computed digest in a newly allocated slice.
|
|
||||||
hash_file :: proc(
|
|
||||||
algorithm: Algorithm,
|
|
||||||
hd: os.Handle,
|
|
||||||
load_at_once := false,
|
|
||||||
allocator := context.allocator,
|
|
||||||
) -> (
|
|
||||||
[]byte,
|
|
||||||
io.Error,
|
|
||||||
) {
|
|
||||||
if !load_at_once {
|
|
||||||
return hash_stream(algorithm, os.stream_from_handle(hd), allocator)
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, ok := os.read_entire_file(hd, allocator)
|
|
||||||
if !ok {
|
|
||||||
return nil, io.Error.Unknown
|
|
||||||
}
|
|
||||||
defer delete(buf, allocator)
|
|
||||||
|
|
||||||
return hash_bytes(algorithm, buf, allocator), io.Error.None
|
|
||||||
}
|
|
||||||
|
|
||||||
hash :: proc {
|
|
||||||
hash_stream,
|
|
||||||
hash_file,
|
|
||||||
hash_bytes,
|
|
||||||
hash_string,
|
|
||||||
hash_bytes_to_buffer,
|
|
||||||
hash_string_to_buffer,
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#+build freestanding
|
||||||
|
package crypto_hash
|
||||||
|
|
||||||
|
hash :: proc {
|
||||||
|
hash_stream,
|
||||||
|
hash_bytes,
|
||||||
|
hash_string,
|
||||||
|
hash_bytes_to_buffer,
|
||||||
|
hash_string_to_buffer,
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#+build !freestanding
|
||||||
|
package crypto_hash
|
||||||
|
|
||||||
|
import "core:io"
|
||||||
|
import "core:os"
|
||||||
|
|
||||||
|
// hash_file will read the file provided by the given handle and return the
|
||||||
|
// computed digest in a newly allocated slice.
|
||||||
|
hash_file :: proc(
|
||||||
|
algorithm: Algorithm,
|
||||||
|
hd: os.Handle,
|
||||||
|
load_at_once := false,
|
||||||
|
allocator := context.allocator,
|
||||||
|
) -> (
|
||||||
|
[]byte,
|
||||||
|
io.Error,
|
||||||
|
) {
|
||||||
|
if !load_at_once {
|
||||||
|
return hash_stream(algorithm, os.stream_from_handle(hd), allocator)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, ok := os.read_entire_file(hd, allocator)
|
||||||
|
if !ok {
|
||||||
|
return nil, io.Error.Unknown
|
||||||
|
}
|
||||||
|
defer delete(buf, allocator)
|
||||||
|
|
||||||
|
return hash_bytes(algorithm, buf, allocator), io.Error.None
|
||||||
|
}
|
||||||
|
|
||||||
|
hash :: proc {
|
||||||
|
hash_stream,
|
||||||
|
hash_file,
|
||||||
|
hash_bytes,
|
||||||
|
hash_string,
|
||||||
|
hash_bytes_to_buffer,
|
||||||
|
hash_string_to_buffer,
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
package hkdf implements the HKDF HMAC-based Extract-and-Expand Key
|
package hkdf implements the HKDF HMAC-based Extract-and-Expand Key
|
||||||
Derivation Function.
|
Derivation Function.
|
||||||
|
|
||||||
See: https://www.rfc-editor.org/rfc/rfc5869
|
See: [[ https://www.rfc-editor.org/rfc/rfc5869 ]]
|
||||||
*/
|
*/
|
||||||
package hkdf
|
package hkdf
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
package hmac implements the HMAC MAC algorithm.
|
package hmac implements the HMAC MAC algorithm.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf
|
- [[ https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf ]]
|
||||||
*/
|
*/
|
||||||
package hmac
|
package hmac
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
package kmac implements the KMAC MAC algorithm.
|
package kmac implements the KMAC MAC algorithm.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
|
- [[ https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf ]]
|
||||||
*/
|
*/
|
||||||
package kmac
|
package kmac
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ WARNING: The MD5 algorithm is known to be insecure and should only be
|
|||||||
used for interoperating with legacy applications.
|
used for interoperating with legacy applications.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
- https://eprint.iacr.org/2005/075
|
- [[ https://eprint.iacr.org/2005/075 ]]
|
||||||
- https://datatracker.ietf.org/doc/html/rfc1321
|
- [[ https://datatracker.ietf.org/doc/html/rfc1321 ]]
|
||||||
*/
|
*/
|
||||||
package md5
|
package md5
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user