mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 11:11:37 -07:00
Compare commits
1243 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aab122ede8 | |||
| 503964c769 | |||
| b2e887be36 | |||
| f36fb6d1ef | |||
| 45d7a670ce | |||
| 86e82dc182 | |||
| b6d2ac11b8 | |||
| 6c38ae3658 | |||
| 532d477705 | |||
| 0abbf3ba0a | |||
| 330c161625 | |||
| 97db075e45 | |||
| 730f992bff | |||
| 95a38d5a96 | |||
| 13e459980b | |||
| 3b5e515a22 | |||
| 3ad95d6be3 | |||
| abd5fc606c | |||
| 6678242280 | |||
| fd487f66bc | |||
| d0dc7395e9 | |||
| 2ba2bc1fec | |||
| 97e2d8916a | |||
| 3dfd61dd4f | |||
| 0f39b9ef22 | |||
| af67cc7afe | |||
| 53558313d8 | |||
| 8a6a3e883c | |||
| af6d2480fa | |||
| c644f79573 | |||
| 219eb58c08 | |||
| 2207a01494 | |||
| 60478c0e07 | |||
| 9b496e82f3 | |||
| fa1d681e65 | |||
| 893c3bef9a | |||
| d96f8bb5c1 | |||
| 563c527419 | |||
| fec42a6d74 | |||
| 4defe88dec | |||
| 36f3001d59 | |||
| 1ce279e6a1 | |||
| c951cbdbbc | |||
| 31aba5a728 | |||
| 9a418fd27b | |||
| f9b9521bf0 | |||
| b155fdf8c9 | |||
| a43a5b053c | |||
| a14f0d8f58 | |||
| 25f781d64b | |||
| 9933ca8b56 | |||
| 9c958ee66d | |||
| a00d96c0de | |||
| ecac3aef32 | |||
| ec45504631 | |||
| 810cf22e5d | |||
| ef82f3e71e | |||
| 16dc79fc5c | |||
| 2aca370a0a | |||
| ce196529dc | |||
| 9d8bb7f4e4 | |||
| 667883b3d5 | |||
| 114ddc4a10 | |||
| 0df9c8bffc | |||
| 0a73ed0799 | |||
| a71cd07b36 | |||
| 2bb20a2c1c | |||
| 352d526b94 | |||
| b150f49c46 | |||
| d83532d29e | |||
| 83d3bc74b0 | |||
| fbf01543d1 | |||
| 8a98ee800a | |||
| f72e3f689b | |||
| cd6153a125 | |||
| fde4e8c905 | |||
| ca46484ae3 | |||
| 280adc8a85 | |||
| a45721e9ad | |||
| 8d399fa7c0 | |||
| 1422e5bc26 | |||
| 133b45d843 | |||
| 510574aa7f | |||
| 3fd3bf2d4d | |||
| fbff2b4fd6 | |||
| 7f0ca315b3 | |||
| 31407d9b1b | |||
| 83e2f5ff74 | |||
| 2375ac22a7 | |||
| b979fd4c43 | |||
| 0e5a482c42 | |||
| 1af84e082c | |||
| d248cddf90 | |||
| a7056f2b4f | |||
| 62cebe1bc9 | |||
| 99aff7e3fb | |||
| 5339e1e1b6 | |||
| 5fe0788cff | |||
| e42b16b106 | |||
| a1d9442380 | |||
| b754c1e072 | |||
| 3a0df80066 | |||
| a9bfb3ac2e | |||
| 692a47f080 | |||
| 21fcf7c874 | |||
| 05d07983c3 | |||
| fc30bde0f6 | |||
| a8d8696e2f | |||
| 66f9ef9a00 | |||
| fdd4ef3c59 | |||
| f9f6fbfe1f | |||
| 4fc96e1ca5 | |||
| cfc85fd737 | |||
| 6b7e9f0a2d | |||
| b3580fa63a | |||
| 406aa587e2 | |||
| b2f432c223 | |||
| 7c2352ea08 | |||
| 9647cb74ad | |||
| bb72ff9c35 | |||
| 397c2aa201 | |||
| b47d73c651 | |||
| 3ee9184537 | |||
| 2938def707 | |||
| 84686c70c5 | |||
| 3fa02427b3 | |||
| b862691d75 | |||
| f482cc8374 | |||
| 030b8d3f66 | |||
| 0d8dadb084 | |||
| d0674cb70f | |||
| 3975b5e736 | |||
| 2bdf5f58ef | |||
| 8e1a2094a7 | |||
| 9271372fef | |||
| 4edcaa6124 | |||
| d84d65ba45 | |||
| 28fb1ba83d | |||
| db95ed7cdd | |||
| e6a552e0ce | |||
| 915f63b3f9 | |||
| e1b545860f | |||
| 8899f42478 | |||
| 13b8a5b73d | |||
| b84a660806 | |||
| d325c8ad23 | |||
| b7fd51a251 | |||
| 7325120ca9 | |||
| 9a5a39c07d | |||
| 9b43aa3c94 | |||
| 63f30a8207 | |||
| 308e9112f2 | |||
| 58d0635f48 | |||
| 2ccb326a41 | |||
| cf9bdc134c | |||
| 29e4e85152 | |||
| a422aba578 | |||
| 7b387fd3aa | |||
| 12ec9bce7d | |||
| b378eb2df3 | |||
| 6422c090f2 | |||
| 3bc7c51325 | |||
| f57cc6beb1 | |||
| 2031d2769a | |||
| 83c9739a7d | |||
| d9ba698b7b | |||
| 19d566ebc5 | |||
| 92a5666c1c | |||
| 63d6b4752b | |||
| 6588fe35b3 | |||
| fa84272d5e | |||
| c90a6ab0d5 | |||
| 0989eac681 | |||
| b8c0a02164 | |||
| df526549e2 | |||
| 1009182f7b | |||
| 620dd2c812 | |||
| 533ba63c82 | |||
| 2165303f5e | |||
| c59f6d548b | |||
| 8eed65ad4c | |||
| bc0a2b8d39 | |||
| a405c72d4d | |||
| b26a685b76 | |||
| 3a8971c260 | |||
| ed742efc33 | |||
| 06ee9117d2 | |||
| e100d9264f | |||
| 53b02c5e6f | |||
| 50618759a6 | |||
| 600ca83386 | |||
| e5629dafd0 | |||
| a2167587ae | |||
| b2a35683a4 | |||
| dcc263c618 | |||
| 3b34cf6dbb | |||
| ae9f026f4b | |||
| 46093bad1e | |||
| c843002d07 | |||
| 6d4f30de1a | |||
| 68ff945419 | |||
| cab53e12b7 | |||
| d92767cb77 | |||
| 670fc70f1f | |||
| 223a336eb4 | |||
| 1ea1229516 | |||
| 6da6393735 | |||
| 87688936c6 | |||
| 590e52cc05 | |||
| 398af659e5 | |||
| 2e416c1a48 | |||
| 34247b2658 | |||
| 45d7dd8f27 | |||
| 09d7f1337b | |||
| 0f944bc0a1 | |||
| 07a9969a41 | |||
| 15c1e8274d | |||
| 517d7ae0b0 | |||
| efb0933965 | |||
| eb51cc6835 | |||
| 1d46adb598 | |||
| 61aa4558dc | |||
| 194d3fe6bd | |||
| 624b870f28 | |||
| eb61cf6043 | |||
| 7abb459861 | |||
| e9ac7d5fab | |||
| bb5dab342a | |||
| 7d6e9ef39c | |||
| d47ba09743 | |||
| 5c9da66595 | |||
| 9cbb9d8551 | |||
| 6b6f72e7bd | |||
| c8cdb22f0b | |||
| 6e2efce670 | |||
| 6d7afd3fa9 | |||
| 006ea11c56 | |||
| 010ffc486c | |||
| 29e5f94c2a | |||
| 6a0d2ffcac | |||
| fc587c507a | |||
| 65cb382135 | |||
| 8f0d74c08d | |||
| 800014e40c | |||
| fda283c55e | |||
| f39b34a8b7 | |||
| 1cc5e23801 | |||
| 8e0806be2d | |||
| c17adc98f5 | |||
| d381b77164 | |||
| e804fbd891 | |||
| ed5fd15f6e | |||
| 89ca15014c | |||
| a5efcfdd78 | |||
| 9c144dd24f | |||
| c0f9e8d6a3 | |||
| f312adb26a | |||
| 9c879e5e17 | |||
| b4fe9677a1 | |||
| 1951bc45a6 | |||
| ba77a9464c | |||
| b1dae2d59a | |||
| 1514d64964 | |||
| e4c502e79b | |||
| 553a244fec | |||
| 3bff922b6f | |||
| 56004c56fc | |||
| 863ee0b8d1 | |||
| 89ba8b4139 | |||
| d0b3b18e26 | |||
| cd7137af60 | |||
| 18fb665bf6 | |||
| a750fc0ba6 | |||
| a555862522 | |||
| 433109ff52 | |||
| ba428fecdb | |||
| 17cc7a2c5e | |||
| 9a2fc6cf4c | |||
| 8ff788f4ff | |||
| 80ecf5b68a | |||
| 5159f30c9c | |||
| d5daa9fda5 | |||
| 89315986d4 | |||
| 096b4f5454 | |||
| 65e68f11f8 | |||
| ec9ac59323 | |||
| 97be7feb99 | |||
| 00344e1323 | |||
| dd92d3054d | |||
| 07ef969546 | |||
| 72c15d7699 | |||
| 009b6f44e3 | |||
| ebd3065aa2 | |||
| 6cb74b63ec | |||
| effc71ca43 | |||
| 43d695a990 | |||
| 19eb2a8890 | |||
| 3875fb08e8 | |||
| 9c455b2213 | |||
| 04f0fbf23a | |||
| 51d6a254cf | |||
| 835effdef1 | |||
| c7bec2962e | |||
| ac634acd4b | |||
| 34c4389d75 | |||
| 8917a7ef88 | |||
| 45d5066029 | |||
| b47eeac414 | |||
| 5cc936245c | |||
| e68d3c8bbc | |||
| d7b1901b16 | |||
| 97d6bf6d8f | |||
| 271f84ab5b | |||
| 75b60fdb12 | |||
| 55141bdbb1 | |||
| 5f2496226f | |||
| dee66b8451 | |||
| 4aec2de7bd | |||
| ac10f504e4 | |||
| 26d107ce64 | |||
| d62c92f5a9 | |||
| 5c1646a6b3 | |||
| b539bb2693 | |||
| 7bc962b852 | |||
| f7ec628cb2 | |||
| 23f3898b4e | |||
| dd74a57c44 | |||
| d6353daf91 | |||
| c7c6852057 | |||
| b543be0d15 | |||
| 47837b206e | |||
| 9a41a450e7 | |||
| f20d0202fa | |||
| 8721d03cfe | |||
| d06575dd49 | |||
| 568b07473f | |||
| 04666746d7 | |||
| b2e7eb4db4 | |||
| f88af59372 | |||
| a974c08aff | |||
| fc9d3ec5a7 | |||
| db03c86544 | |||
| 51d12acab3 | |||
| 032e193d0d | |||
| 0f83ab466f | |||
| 53ce945034 | |||
| 0e168dd292 | |||
| 5ff6a25bdc | |||
| b6b8e640f5 | |||
| 573402c97e | |||
| e01701ce0e | |||
| f9b14500be | |||
| 4bb7cd5e4b | |||
| 6d5b1800fe | |||
| 0f5328973e | |||
| 65277ddd6b | |||
| 46718d6d85 | |||
| 2a0543d2f0 | |||
| 792f06a234 | |||
| 14f7619cdc | |||
| ffa3669d02 | |||
| b1903b915b | |||
| a7bab89c93 | |||
| a1ee9e7035 | |||
| c05a92ab3e | |||
| 5789df5c0c | |||
| 78c05a49a3 | |||
| 8d496dc3d0 | |||
| 703eab2f15 | |||
| 04bfc926ee | |||
| 791c9c16ba | |||
| 6d3a57b4ac | |||
| c7b8f1fb0a | |||
| 462f3f4abd | |||
| 5eef29290c | |||
| a944aa406d | |||
| 8c6c2543da | |||
| 7ae22b7ce5 | |||
| 3e295734cb | |||
| de41c2256d | |||
| b419615002 | |||
| c044e295ce | |||
| 87ab3f5dc8 | |||
| fa8dd5a13b | |||
| b818ebc02f | |||
| c04a53e453 | |||
| 15287a771f | |||
| 2db31cf0d5 | |||
| 550e798c1b | |||
| 290168f862 | |||
| 2f2a92866b | |||
| 116edb9052 | |||
| 9070e613a4 | |||
| 6243160ecd | |||
| 980ee3310f | |||
| c5d5d055ac | |||
| a783d4ce5b | |||
| a7b09a24b7 | |||
| 602e000379 | |||
| f93074a082 | |||
| 28f05e8aaa | |||
| 9eb1596939 | |||
| 8412352e5a | |||
| 4c35633e01 | |||
| b1371d5c7a | |||
| 3ada83a503 | |||
| 50ded324e0 | |||
| 674bd94f72 | |||
| ff24cfe314 | |||
| bc191d4f84 | |||
| 5c20676c76 | |||
| 11b7be1640 | |||
| 6ae8288142 | |||
| 7f1069cb0b | |||
| 41fbaaf1d3 | |||
| f14babe419 | |||
| 0bb2327d76 | |||
| f92042e7dd | |||
| 1861ecff86 | |||
| d4d9f55556 | |||
| 23cd64ec35 | |||
| 6734a7096a | |||
| 3263e54144 | |||
| 6805b85f89 | |||
| 88c97cbbd0 | |||
| 17b1c8d338 | |||
| 0a5c85f8e3 | |||
| 9c20df5b1b | |||
| 5d6b4eda1e | |||
| 290ada7f90 | |||
| bf37bee4f7 | |||
| 7df7fec6f7 | |||
| e423a6d692 | |||
| f4c74a9f32 | |||
| dce176fa39 | |||
| d666ff3744 | |||
| 1678391db3 | |||
| d1174f66bc | |||
| 84a7e03178 | |||
| 1bffc8baac | |||
| c558b694eb | |||
| a06bde729b | |||
| 5107bdc06b | |||
| 5137d12d36 | |||
| c6ee025063 | |||
| 826cf1508b | |||
| 51edf01162 | |||
| 87f6f3a1fe | |||
| fca691a066 | |||
| 494cac02d7 | |||
| 38c69b9691 | |||
| deb8922181 | |||
| cdda8f0eb9 | |||
| cba8cb2201 | |||
| ae67f37fc1 | |||
| 8d4bb35bcc | |||
| 180902468f | |||
| 7290c69257 | |||
| 3ebf5dcc0e | |||
| 9d4c2ba0d8 | |||
| 1d79521e81 | |||
| 31d7ef5696 | |||
| 8c621453ae | |||
| c712af3bc2 | |||
| f0a89f8d5d | |||
| 9b839621a9 | |||
| 66456714e1 | |||
| fc8e5b8a61 | |||
| 05cfc89283 | |||
| dc5cf23066 | |||
| 2e80879e88 | |||
| dfa0ccf976 | |||
| 6c16860be6 | |||
| d032cff23b | |||
| c3746d9f56 | |||
| 0fa6ba726f | |||
| b03f17dd8d | |||
| 24c8b15409 | |||
| 028a79e66c | |||
| 0a6673220b | |||
| 88add0b6b1 | |||
| 8a78b0d241 | |||
| 824c831190 | |||
| 874d6ccb60 | |||
| db3279e7da | |||
| 9251e06143 | |||
| a642ea0b28 | |||
| 00fc4c4e1b | |||
| 9ea11da00f | |||
| a8909f06ae | |||
| ee543a304a | |||
| 5c5b78cbbe | |||
| 54515af8cc | |||
| b894df2125 | |||
| 3f193d7446 | |||
| e127d21fed | |||
| 3060225f46 | |||
| c14b9d461a | |||
| 8060e3170e | |||
| 980947b355 | |||
| fd987b29ff | |||
| afcc2889ec | |||
| 5f001f6d51 | |||
| fc07211772 | |||
| c9e37a08be | |||
| dcbcf75269 | |||
| 59479b2ba6 | |||
| 007bd993a6 | |||
| 5a84a08225 | |||
| bb23648c71 | |||
| a4b8c1ea17 | |||
| 20e75cd463 | |||
| d74ddb2d91 | |||
| ec0831da70 | |||
| b12ba1508e | |||
| f3b0b82461 | |||
| fea38f6910 | |||
| 213b2fd0f8 | |||
| 42d595f6a1 | |||
| 656de10ba4 | |||
| f6f3a760bc | |||
| bafc791f1c | |||
| 1fc256dd90 | |||
| 41549b502b | |||
| f989f4df3e | |||
| 21d1c0e5a4 | |||
| d7b7804215 | |||
| 8472338bfa | |||
| 1de1d97429 | |||
| f21ead4f78 | |||
| a95cead8e7 | |||
| c276b1c0bc | |||
| b39ef29ec6 | |||
| b2b8b14955 | |||
| 7e0473dded | |||
| e6bd79c882 | |||
| 9e417592e3 | |||
| db87c34613 | |||
| 159257597a | |||
| bdd6a86d73 | |||
| 1ab3ec5731 | |||
| 004cd4933d | |||
| 14ee2181cb | |||
| b43c1f2b5b | |||
| b1e608bfba | |||
| 57c5455827 | |||
| cc185d98b4 | |||
| cd61251d39 | |||
| 43a199b57b | |||
| fa1875a8f1 | |||
| 44aae76294 | |||
| 009cebe8bf | |||
| 9399cb53b6 | |||
| bed81c8829 | |||
| 7a592cbb31 | |||
| c178f7199d | |||
| a4d3777ab2 | |||
| e931c82b9b | |||
| a7f0275093 | |||
| 9ab2fbea00 | |||
| 7fe86ed565 | |||
| 16584779fb | |||
| c5c2a4d09d | |||
| 912c326d8b | |||
| d496dbf3a0 | |||
| 88e6980b13 | |||
| 0d413b8136 | |||
| 32a4a5e601 | |||
| 91cf0826c1 | |||
| 3bc172c70b | |||
| cbfb32c34c | |||
| 5cd57a3a7f | |||
| 563ce2bd81 | |||
| 9469b90b01 | |||
| d2e1ec13f0 | |||
| 6df07a2c0a | |||
| c0d407a2b4 | |||
| f378367fe7 | |||
| c291fffce1 | |||
| 900fe95ba0 | |||
| 5dd2e38aff | |||
| 5ad8ebba9f | |||
| e7719eed65 | |||
| 5f49b8997a | |||
| d6734c85b8 | |||
| 4ca23499fa | |||
| e201a2fabb | |||
| 5c4485f657 | |||
| bae2a6fc1e | |||
| 8777fa1c04 | |||
| d771b3286d | |||
| 0e6dd56ac1 | |||
| 9127e584c5 | |||
| eab0e730a0 | |||
| 7128bc4b34 | |||
| 7b672ac72a | |||
| 8f4ab3c07d | |||
| dc5cfacc0a | |||
| 3aea2e1fff | |||
| 59933b244d | |||
| 42aca72d9f | |||
| 7034a31745 | |||
| 9d0786ded7 | |||
| 4c51706941 | |||
| a8c4f46747 | |||
| 8e367d221b | |||
| a08250ac5b | |||
| 131c71ea76 | |||
| 9db04fe446 | |||
| abaa906f34 | |||
| fa093d9b09 | |||
| 4035a226da | |||
| df5ee2dd06 | |||
| 9b4cd0743c | |||
| 3f090ed523 | |||
| 79173ef119 | |||
| 44758f2a60 | |||
| 1f0b24b735 | |||
| 327853ab92 | |||
| a6878fcd91 | |||
| 61202b5abd | |||
| 56516ee8b2 | |||
| 1cc639bc93 | |||
| 2357293e05 | |||
| 7a8b1669b0 | |||
| bc160d2eb7 | |||
| b02b85d242 | |||
| 1d151c4c92 | |||
| 899fab64d9 | |||
| 00ab3beed9 | |||
| ca10fc2d47 | |||
| 1ed6a484ac | |||
| b0675358c3 | |||
| 456dd22dc4 | |||
| 02b8fefa30 | |||
| 95808fd2e7 | |||
| e63d71c23b | |||
| 23a49ce65b | |||
| ca6300c860 | |||
| 1ebb7f8e9d | |||
| 4685cf1085 | |||
| 80a0b161b0 | |||
| 27feb5998c | |||
| e88db2818b | |||
| 19535d8721 | |||
| 8ea8fbeccb | |||
| 4e300ff90a | |||
| 7bf25a4cf2 | |||
| 5f0f9f477e | |||
| dd7c65a89c | |||
| 8620281191 | |||
| c311a60aaf | |||
| 2993490c75 | |||
| 60b324d4ed | |||
| 89a2fdd106 | |||
| 8530829ca4 | |||
| 0e5d7801dd | |||
| d5db49a3b0 | |||
| 606f11ebe8 | |||
| 07a4f4d017 | |||
| d463adfe4f | |||
| 16bd19ed43 | |||
| eb811e8341 | |||
| 8a63b6dff7 | |||
| 829654e3a2 | |||
| cec08114fd | |||
| bf90b61908 | |||
| ae0a5b1a87 | |||
| 55ff9b857e | |||
| e7122a0950 | |||
| 539cec7496 | |||
| 000709b594 | |||
| c51f94b600 | |||
| b703d5ca58 | |||
| aff345f57f | |||
| a4664f82cc | |||
| 9fb090d1b8 | |||
| 4a2ad3b13a | |||
| 9d5afee24b | |||
| 6736205723 | |||
| f0a7f1812f | |||
| 9c9ae5987a | |||
| f588593ff1 | |||
| a626adac8e | |||
| 338793b68e | |||
| 59575d9b22 | |||
| b58a0b60d0 | |||
| 3a4c82abf8 | |||
| 301b9eef31 | |||
| 36ed8fe55d | |||
| 946cf52df1 | |||
| a78f062499 | |||
| 3c47503780 | |||
| 6da82e038d | |||
| 38af752cd1 | |||
| 0428f508d8 | |||
| 3c24584290 | |||
| 535b8a9483 | |||
| 038086d1d9 | |||
| 9a16bc5fc5 | |||
| eee8e0faa2 | |||
| 3e7e779abf | |||
| 6a07effdd2 | |||
| 395e0fb225 | |||
| 9be9f0bb2c | |||
| 9e7cc8cf93 | |||
| 2743bf7e44 | |||
| d95c28f41b | |||
| e57224d6e4 | |||
| f67691c457 | |||
| d04c82e547 | |||
| 09fa1c29cd | |||
| ddcaa0de53 | |||
| 621b3c7829 | |||
| 68df35b378 | |||
| c1d853a24e | |||
| 30636f5114 | |||
| 7b9ea9eca0 | |||
| 89404147f6 | |||
| 29d4bdc80b | |||
| 594078cc1d | |||
| ab68e4c6c4 | |||
| 4956f9dad3 | |||
| fd090c6672 | |||
| b46b3010ec | |||
| 31914e9cb9 | |||
| da6edb3764 | |||
| 9a9625f885 | |||
| 15b40a4f29 | |||
| 5406acc8fa | |||
| d5b0ec712b | |||
| 766d6aa946 | |||
| 895ebb95d5 | |||
| af636eedde | |||
| 57b7822e12 | |||
| 03736d8bcb | |||
| 3a5d80b291 | |||
| 967ccfc7cc | |||
| 712ae1c5ac | |||
| f93f2dfd5c | |||
| 9ac619f4a2 | |||
| 9cfd4a953e | |||
| 14e2cc17d6 | |||
| 5f41741e8c | |||
| 8a067bc1fc | |||
| cfdf2bfb77 | |||
| 2f8316840e | |||
| a660098106 | |||
| 90d1f9ab27 | |||
| 2097b09abb | |||
| f63f28302e | |||
| 98b539ac5c | |||
| 9d067ae562 | |||
| 239d4e1076 | |||
| 99825a28d7 | |||
| 144504a752 | |||
| 5a542c7ad5 | |||
| 73f57c5933 | |||
| 2071d7ba84 | |||
| e8102a40d0 | |||
| 737677f1b6 | |||
| fc047a8043 | |||
| ae52e245ea | |||
| b8bfc715bf | |||
| b0aa08f85b | |||
| d6ee5e89fa | |||
| a263da0bbe | |||
| 9cb02aa51d | |||
| 799f4379d8 | |||
| bd51b21386 | |||
| 276284cbec | |||
| a8021f03a6 | |||
| c35d5d4c42 | |||
| 248a0bfa5f | |||
| 34791707fd | |||
| 37ae9eb609 | |||
| b6838731f5 | |||
| 991c1d4446 | |||
| d0bb1fb844 | |||
| ea43c030aa | |||
| d54f34a162 | |||
| a86cfa6e97 | |||
| 1b83f4a18b | |||
| 90ac400ec5 | |||
| 75c659fa41 | |||
| 8cfff254c9 | |||
| 8c2eb5df78 | |||
| a2f0ac0fd2 | |||
| 2987fc65a1 | |||
| c5bab58180 | |||
| 5ad88ea169 | |||
| 72d6b9b683 | |||
| 2f2c086382 | |||
| d9fafa7000 | |||
| 12e53f2336 | |||
| a2e729c303 | |||
| 7b89174a26 | |||
| 1d7f7a55d2 | |||
| 766485ccab | |||
| 6642aa94d5 | |||
| 1c9ec27d36 | |||
| 05e27fa92d | |||
| 7954a7a6f3 | |||
| 1d621295b1 | |||
| 02c2aff41b | |||
| 5476d43441 | |||
| b25e85a8bb | |||
| 190103883c | |||
| d460dd2bdc | |||
| 7ee7f4b064 | |||
| f2d3376c0b | |||
| 0fcd2f1d88 | |||
| 76f52dd6c9 | |||
| ae0be9c785 | |||
| 3f6f00d8e5 | |||
| c58eeca1b2 | |||
| 5032839abc | |||
| 2b1d85968d | |||
| 5896469f3b | |||
| 70c150fc83 | |||
| 2411febf83 | |||
| 5e7b031a1d | |||
| ef05e0858d | |||
| 880a18f124 | |||
| 5d94887e76 | |||
| 577049c69e | |||
| 46f46e645c | |||
| b13aa5db37 | |||
| 2990747cf8 | |||
| 62c30795e6 | |||
| bb94f4d129 | |||
| 7b53dbeb8a | |||
| 120ef168bf | |||
| 72dfb73c9d | |||
| 67dcd916e8 | |||
| 7f6f971284 | |||
| 67d5b97ff9 | |||
| efb2b05040 | |||
| 656e62d724 | |||
| ce8801c37f | |||
| c6c710465a | |||
| f3dc1f6e3b | |||
| 873b7f8588 | |||
| 7d3dfb1046 | |||
| f4782157d3 | |||
| 8de7d2f18f | |||
| aff8f06e3c | |||
| 0bf1b6c2f1 | |||
| 2017ebc1b5 | |||
| da56a75ad6 | |||
| 1e1228fb37 | |||
| 8d7c37e384 | |||
| cd65a15d81 | |||
| 9235e82451 | |||
| ecee0e2db2 | |||
| 0580eebd2a | |||
| 909a5016ee | |||
| c2c89e54a5 | |||
| 6389d9c11f | |||
| 2784e8ea51 | |||
| 85b71708dd | |||
| 649b5fa528 | |||
| d6a89d667d | |||
| b408ec6bac | |||
| 8545f316ff | |||
| 3bf7b416e7 | |||
| 0b83e3dae5 | |||
| d7d23e65ea | |||
| 2820bbc269 | |||
| 59aa05170d | |||
| 70c5153471 | |||
| 5961d4b316 | |||
| b59c80d6fd | |||
| 8c10f4cdde | |||
| 0cc72b536f | |||
| 8a7c2ea9d0 | |||
| cb1c10ce83 | |||
| 37c2e9bec3 | |||
| 4626cd03da | |||
| 3850be2e11 | |||
| 4c9aa30a1e | |||
| da977cf1e6 | |||
| ee97c5958f | |||
| 778bbee17c | |||
| 4efef08c94 | |||
| 87c835268a | |||
| 83ed0b37cd | |||
| dc49cf766f | |||
| 933754193a | |||
| 76eef47491 | |||
| 0e21f45076 | |||
| 9bfe3a94f4 | |||
| a2009220a2 | |||
| b410383aaf | |||
| b47736260a | |||
| 5154bb551a | |||
| 89084befb0 | |||
| f3caa4aee3 | |||
| 3ec253f385 | |||
| 759e342872 | |||
| 120b1101fb | |||
| 252fd0e928 | |||
| 1fa2af213d | |||
| de84db85cb | |||
| 33d85adf34 | |||
| 383d485e2a | |||
| 68d2b7bb89 | |||
| d667809e0a | |||
| 64ed4389ff | |||
| 4701b31b55 | |||
| e52cc73d50 | |||
| ac0ed13b35 | |||
| 269957b9fb | |||
| 464a675adc | |||
| 55f3e99f63 | |||
| 49fb0acfc9 | |||
| 09db245e4c | |||
| bc7972fbaf | |||
| 6f80d2dc36 | |||
| 46250168d6 | |||
| 509712f771 | |||
| 4b38dbe133 | |||
| ad0ffa4833 | |||
| b89fc9191c | |||
| 829e4cc67e | |||
| beb4699b46 | |||
| 031b0cc534 | |||
| ee504aa596 | |||
| baa5ea9258 | |||
| 9a490e4e0d | |||
| 252de70b0f | |||
| 4ae021cd4c | |||
| af962526df | |||
| 6024af172c | |||
| 8bd5a9bb9e | |||
| a9166f52f8 | |||
| 9bdd4c73a5 | |||
| 2362be11bf | |||
| 5b8a76c583 | |||
| 2a0e4f7a8c | |||
| ece82eecc3 | |||
| 67d02043fe | |||
| 29c80c238d | |||
| a4606e4da8 | |||
| d4df3f6383 | |||
| 58ff3dd1ed | |||
| cf8c9a6be4 | |||
| ea709451e8 | |||
| d47a403d29 | |||
| 1606f756b3 | |||
| 0b2f357bbe | |||
| 9a1c4dc56d | |||
| 7eabeda870 | |||
| 2e9298891e | |||
| f8b85339af | |||
| 12b8f91249 | |||
| 8e395cc6e9 | |||
| feba52002e | |||
| ca2b2c498e | |||
| 6e9e469abd | |||
| 0490ba46f4 | |||
| e0652ee2f4 | |||
| bc99bacb21 | |||
| 173527d631 | |||
| f8cb2bcad2 | |||
| b011487778 | |||
| c8cc130744 | |||
| fe0244606b | |||
| 036fa6482c | |||
| 04ca22b9ea | |||
| bf9ae77fbd | |||
| 4adfc120ba | |||
| d8bb93accc | |||
| e410908ce8 | |||
| d278c852cc | |||
| 364b64718f | |||
| 61335089c6 | |||
| 040b90ce76 | |||
| 843b2350eb | |||
| 526d338300 | |||
| a6aca5d6d1 | |||
| 05c67c3243 | |||
| 31b1aef44e | |||
| a8c09d77ff | |||
| 5665ae02bc | |||
| 92d3a681cd | |||
| cb66ed52ce | |||
| 2e1b2dc3ba | |||
| 65333181fc | |||
| 30ad923558 | |||
| a31ab31e96 | |||
| ac760a0bdd | |||
| fbf0180411 | |||
| 198ac00994 | |||
| 603764dbca | |||
| cb04333480 | |||
| 0e169fd1c2 | |||
| 68cf51c8f8 | |||
| 65afe6f70d | |||
| 291a064725 | |||
| bfbeb23f54 | |||
| 58e4a011c7 | |||
| 3de15987bd | |||
| c5c46c5073 | |||
| abe896a7be | |||
| f00df0afe9 | |||
| 35f21abc7b | |||
| b1977dfa4b | |||
| e23eba0914 | |||
| 4b245e2d83 | |||
| f169d8c396 | |||
| 39f8437f4d | |||
| 4aa8834d39 | |||
| f79efd43e4 | |||
| 058132e7e6 | |||
| 9b7c5f2b1b | |||
| 06e61c7315 | |||
| 1b23dbb228 | |||
| cc2ba146a6 | |||
| 3bbdc93882 | |||
| 0091193c04 | |||
| 1b5bcc1a99 | |||
| 5a661dc67b | |||
| fb48131f3c | |||
| 9300c99d65 | |||
| 9366bf44a6 | |||
| e8e3501443 | |||
| 9f96382558 | |||
| 82088e4a75 | |||
| b12bfe407d | |||
| 1e726bb3e0 | |||
| 1db5e1250f | |||
| e0ecae66fd | |||
| 0df7fe4247 | |||
| 84a8e17482 | |||
| cabaac5a68 | |||
| 2bb5c4cafc | |||
| bfff322eb9 | |||
| dd60802db4 | |||
| 6db42cfaec | |||
| 21f5b41150 | |||
| 1f007a46ee | |||
| 4fa4feb669 | |||
| 5fd3fc4c7c | |||
| ee2d3e00fd | |||
| 6f65ed6cc8 | |||
| 70525a12ca | |||
| 914950592c | |||
| 3c021f9c52 | |||
| 9ea88f1353 | |||
| 4d89249caf | |||
| ff0e976ff3 | |||
| 0df1645422 | |||
| bb6d73953c | |||
| c9c14bab8a | |||
| 7c6117bb8f | |||
| ae40946198 | |||
| 8063569cdd | |||
| dab72d5615 | |||
| 89493b70a9 | |||
| c12eb3ec93 | |||
| 7343ed0cac | |||
| 3102abf1aa | |||
| 03282c1234 | |||
| 490c8daedd | |||
| e78ee90ac2 | |||
| f809788f75 | |||
| 0888c69b57 | |||
| ab39644156 | |||
| 4c1a9d2b3f | |||
| f6308ab5b9 | |||
| 3baf8d92c3 | |||
| 34065865a0 | |||
| 3d90e580c5 | |||
| f635d3d8af | |||
| 29f1b79d70 | |||
| 955be66f1a | |||
| a0e6ae6f33 | |||
| 08d032859f | |||
| 4af77aeff6 | |||
| 6749639eb1 | |||
| 8a56bb3b5f | |||
| 37e79f9cca | |||
| a4c64002c5 | |||
| 63b6e8216c | |||
| 3e7b5670fb | |||
| e2e18324ed | |||
| 924039c01b | |||
| 2af2a035dc | |||
| f97ccca514 | |||
| 25e9255157 | |||
| 2e64866838 | |||
| b21b5ef222 | |||
| d9fab5e824 | |||
| 0424404140 | |||
| db89c2ccd0 | |||
| 0c97f6aa4e | |||
| af78ad2a87 | |||
| 9cc5cd9d40 | |||
| 59950bcad6 | |||
| 4587a55486 | |||
| 7640fb0483 | |||
| b8f9deb3d8 | |||
| 92aad90c6b | |||
| 506adfb105 | |||
| e819eebc63 | |||
| 841e73fcd5 | |||
| aa821991b8 | |||
| b71afdc3ee | |||
| bc139ba6c6 | |||
| 8af6da5de1 | |||
| 0b86038482 | |||
| 8d943f5902 | |||
| 32b27c690d | |||
| 3494a6dcd8 | |||
| 235fec23af | |||
| 97b066f112 | |||
| a99c0b3e4a | |||
| 2a6fb3a387 | |||
| 65204f13a8 | |||
| 8438d66e6b | |||
| 44c8da7bf2 | |||
| 41fdcfeecf | |||
| 70ba4b5321 | |||
| 71da3ef925 | |||
| 582bd760b7 | |||
| e86bb3a795 | |||
| e3a836f93c | |||
| 31b42a53fc | |||
| b71d3c739a | |||
| a162b51588 | |||
| 7c1119f217 | |||
| b0397581db | |||
| c7dc1220b3 | |||
| d16acdc89c | |||
| 1279ebe948 | |||
| aa5a95a4d1 | |||
| fa1cb28c8f | |||
| 3902273d68 | |||
| 811132ccbd | |||
| 391b3090c9 | |||
| d50380709d | |||
| 14a46c6d5e | |||
| b4e3da84c5 | |||
| 9d627e453a | |||
| d6e0e5d3f6 | |||
| 12b370ddc1 | |||
| 50f86dc14f | |||
| 9078ddaf5a | |||
| 8028033513 | |||
| 6b9202dfbf | |||
| 04c928fb9e | |||
| 354d00963c | |||
| 9e5e49a65d | |||
| 8a849bd1bd | |||
| 1b3fb11a31 | |||
| b30ceab864 | |||
| 3e1791aa5c | |||
| 2cb5cc774d | |||
| 5f7843a13d | |||
| 1b770fc3b2 | |||
| 79d3c3be66 | |||
| 55d42492ac | |||
| 9737c2ad0b | |||
| b9a813a69d | |||
| e0ac454ed0 | |||
| 1db95aa09d | |||
| c0bbe1e23d | |||
| 9e35361eb8 | |||
| cce42f4a6b | |||
| bd19081543 | |||
| dd9b0ae4e5 | |||
| 3b5d28f0ee | |||
| 0ca39c70a5 | |||
| 270348b112 | |||
| e67473d89a | |||
| 086478e8f2 | |||
| 5a8da5dcdb | |||
| f6f4734fee | |||
| f903951016 | |||
| e19460cbd7 | |||
| 6de2b7700f | |||
| 4e145cf69c | |||
| 341087a82b | |||
| 851fd1f8d1 | |||
| ce43b04688 | |||
| ff1ab74b3c | |||
| aed67ba665 | |||
| 70c1f9d0e1 | |||
| 761a079789 | |||
| 4116d66c59 | |||
| fc6edf65d7 | |||
| 548ab2e1b6 | |||
| 13a3c0e57c | |||
| 9834ceed42 | |||
| 4266a7c166 | |||
| 639cc9faa8 | |||
| e2cecafa66 | |||
| 4bcb68a973 | |||
| 7131772754 | |||
| c2e5602ee5 | |||
| 8714fd77a0 | |||
| c20839c461 | |||
| 51229a29f8 | |||
| 59675949da | |||
| 6564ce0fb0 | |||
| c36ac4bdfd | |||
| 764ce2a4b0 | |||
| d400a5a108 | |||
| 744eb7c6d8 | |||
| 1b79e2ca5f | |||
| 4cb0edc90b | |||
| 6201280468 | |||
| 9e36e28217 | |||
| 5edb2c5688 | |||
| ce5e7998ba | |||
| 4a4aca6829 | |||
| f26ed39e86 | |||
| 574d2baf09 | |||
| 1e622979f8 | |||
| 1f969fdc75 | |||
| e206d6ba35 | |||
| 7faa146004 | |||
| 75cb2c68cc | |||
| 942e91f94c | |||
| d8f06ed557 | |||
| a73ff00b02 | |||
| a58a08c0c3 | |||
| 5c533e477d | |||
| 21247721b4 | |||
| 4436d24440 | |||
| 7a16618ec6 | |||
| 2b2abc6b9f | |||
| 056840975f | |||
| 160b23f991 | |||
| a573d076e4 | |||
| 625cb03284 | |||
| 2f8d60ec47 | |||
| 37e33af342 | |||
| f7e0516254 | |||
| eafe57e923 | |||
| c76ab138eb | |||
| 16c176dc89 |
@@ -1 +1,2 @@
|
||||
*.odin linguist-language=Odin
|
||||
* text=auto
|
||||
+52
-16
@@ -3,11 +3,16 @@ on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
name: Ubuntu Build, Check, and Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM, botan
|
||||
run: sudo apt-get install llvm-11 clang-11 libbotan-2-dev botan
|
||||
- name: Download LLVM
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
@@ -46,6 +51,9 @@ jobs:
|
||||
- name: Odin check examples/all for Linux i386
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Linux arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for FreeBSD amd64
|
||||
run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64
|
||||
timeout-minutes: 10
|
||||
@@ -53,15 +61,14 @@ jobs:
|
||||
run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
|
||||
timeout-minutes: 10
|
||||
build_macOS:
|
||||
name: MacOS Build, Check, and Test
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM, botan and setup PATH
|
||||
- name: Download LLVM, and setup PATH
|
||||
run: |
|
||||
brew install llvm@13 botan
|
||||
echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
brew install llvm@17
|
||||
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
@@ -87,9 +94,43 @@ jobs:
|
||||
cd tests/core
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Vendor library tests
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/vendor
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
build_macOS_arm:
|
||||
name: MacOS ARM Build, Check, and Test
|
||||
runs-on: macos-14 # This is an arm/m1 runner.
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@17
|
||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo -vet
|
||||
timeout-minutes: 10
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo -debug
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all
|
||||
run: ./odin check examples/all -strict-style
|
||||
timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
run: |
|
||||
cd tests/core
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin internals tests
|
||||
@@ -97,13 +138,8 @@ jobs:
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Darwin arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Linux arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
|
||||
timeout-minutes: 10
|
||||
build_windows:
|
||||
name: Windows Build, Check, and Test
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
@@ -168,7 +204,7 @@ jobs:
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\documentation
|
||||
call build.bat
|
||||
rem call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: core:math/big tests
|
||||
shell: cmd
|
||||
|
||||
@@ -7,6 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build_windows:
|
||||
name: Windows Build
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
@@ -29,6 +30,7 @@ jobs:
|
||||
cp LICENSE dist
|
||||
cp LLVM-C.dll dist
|
||||
cp -r shared dist
|
||||
cp -r base dist
|
||||
cp -r core dist
|
||||
cp -r vendor dist
|
||||
cp -r bin dist
|
||||
@@ -39,12 +41,17 @@ jobs:
|
||||
name: windows_artifacts
|
||||
path: dist
|
||||
build_ubuntu:
|
||||
name: Ubuntu Build
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: (Linux) Download LLVM
|
||||
run: sudo apt-get install llvm-11 clang-11
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: make nightly
|
||||
- name: Odin run
|
||||
@@ -56,46 +63,88 @@ jobs:
|
||||
cp LICENSE dist
|
||||
cp libLLVM* dist
|
||||
cp -r shared dist
|
||||
cp -r base dist
|
||||
cp -r core dist
|
||||
cp -r vendor dist
|
||||
cp -r examples dist
|
||||
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
||||
zip -r dist.zip dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: ubuntu_artifacts
|
||||
path: dist
|
||||
path: dist.zip
|
||||
build_macos:
|
||||
name: MacOS Build
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: macOS-latest
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@13
|
||||
echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
brew install llvm@17 dylibbundler
|
||||
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: make nightly
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo
|
||||
- name: Copy artifacts
|
||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
|
||||
- name: Bundle
|
||||
run: |
|
||||
mkdir dist
|
||||
cp odin dist
|
||||
cp LICENSE dist
|
||||
cp -r shared dist
|
||||
cp -r base dist
|
||||
cp -r core dist
|
||||
cp -r vendor dist
|
||||
cp -r examples dist
|
||||
dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
|
||||
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
||||
zip -r dist.zip dist
|
||||
- name: Odin run
|
||||
run: ./dist/odin run examples/demo
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: macos_artifacts
|
||||
path: dist
|
||||
path: dist.zip
|
||||
build_macos_arm:
|
||||
name: MacOS ARM Build
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: macos-14 # ARM machine
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@17 dylibbundler
|
||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
|
||||
- name: Bundle
|
||||
run: |
|
||||
mkdir dist
|
||||
cp odin dist
|
||||
cp LICENSE dist
|
||||
cp -r shared dist
|
||||
cp -r base dist
|
||||
cp -r core dist
|
||||
cp -r vendor dist
|
||||
cp -r examples dist
|
||||
dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
|
||||
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
|
||||
zip -r dist.zip dist
|
||||
- name: Odin run
|
||||
run: ./dist/odin run examples/demo
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: macos_arm_artifacts
|
||||
path: dist.zip
|
||||
upload_b2:
|
||||
runs-on: [ubuntu-latest]
|
||||
needs: [build_windows, build_macos, build_ubuntu]
|
||||
needs: [build_windows, build_macos, build_macos_arm, build_ubuntu]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v2
|
||||
@@ -126,6 +175,11 @@ jobs:
|
||||
with:
|
||||
name: macos_artifacts
|
||||
|
||||
- name: Download macOS arm artifacts
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: macos_arm_artifacts
|
||||
|
||||
- name: Create archives and upload
|
||||
shell: bash
|
||||
env:
|
||||
@@ -140,8 +194,9 @@ jobs:
|
||||
echo Uploading artifcates to B2
|
||||
chmod +x ./ci/upload_create_nightly.sh
|
||||
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
|
||||
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/
|
||||
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/
|
||||
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/dist.zip
|
||||
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/dist.zip
|
||||
./ci/upload_create_nightly.sh "$BUCKET" macos-arm64 macos_arm_artifacts/dist.zip
|
||||
|
||||
echo Deleting old artifacts in B2
|
||||
python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
|
||||
|
||||
+29
@@ -25,7 +25,33 @@ bld/
|
||||
tests/documentation/verify/
|
||||
tests/documentation/all.odin-doc
|
||||
tests/internal/test_map
|
||||
tests/internal/test_pow
|
||||
tests/internal/test_rtti
|
||||
tests/core/test_core_compress
|
||||
tests/core/test_core_container
|
||||
tests/core/test_core_filepath
|
||||
tests/core/test_core_fmt
|
||||
tests/core/test_core_i18n
|
||||
tests/core/test_core_image
|
||||
tests/core/test_core_libc
|
||||
tests/core/test_core_match
|
||||
tests/core/test_core_math
|
||||
tests/core/test_core_net
|
||||
tests/core/test_core_os_exit
|
||||
tests/core/test_core_reflect
|
||||
tests/core/test_core_strings
|
||||
tests/core/test_crypto
|
||||
tests/core/test_hash
|
||||
tests/core/test_hxa
|
||||
tests/core/test_json
|
||||
tests/core/test_linalg_glsl_math
|
||||
tests/core/test_noise
|
||||
tests/core/test_varint
|
||||
tests/core/test_xml
|
||||
tests/core/test_core_slice
|
||||
tests/core/test_core_thread
|
||||
tests/core/test_core_runtime
|
||||
tests/vendor/vendor_botan
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Visual Studio Code options directory
|
||||
@@ -290,3 +316,6 @@ shared/
|
||||
examples/bug/
|
||||
build.sh
|
||||
!core/debug/
|
||||
|
||||
# RAD debugger project file
|
||||
*.raddbg
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016-2022 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2024 Ginger Bill. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -110,7 +110,7 @@ typeid_of :: proc($T: typeid) -> typeid ---
|
||||
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
|
||||
|
||||
complex :: proc(real, imag: Float) -> Complex_Type ---
|
||||
quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type ---
|
||||
quaternion :: proc(imag, jmag, kmag, real: Float) -> Quaternion_Type --- // fields must be named
|
||||
real :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||
jmag :: proc(value: Quaternion) -> Float ---
|
||||
@@ -5,6 +5,12 @@ package intrinsics
|
||||
// Package-Related
|
||||
is_package_imported :: proc(package_name: string) -> bool ---
|
||||
|
||||
// Matrix Related Procedures
|
||||
transpose :: proc(m: $T/matrix[$R, $C]$E) -> matrix[C, R]E ---
|
||||
outer_product :: proc(a: $A/[$X]$E, b: $B/[$Y]E) -> matrix[X, Y]E ---
|
||||
hadamard_product :: proc(a, b: $T/matrix[$R, $C]$E) -> T ---
|
||||
matrix_flatten :: proc(m: $T/matrix[$R, $C]$E) -> [R*C]E ---
|
||||
|
||||
// Types
|
||||
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
||||
|
||||
@@ -32,9 +38,9 @@ count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_sim
|
||||
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||
|
||||
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) ---
|
||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) ---
|
||||
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) ---
|
||||
|
||||
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
||||
|
||||
@@ -162,7 +168,14 @@ type_is_matrix :: proc($T: typeid) -> bool ---
|
||||
type_has_nil :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
||||
|
||||
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
|
||||
type_union_tag_type :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||
type_union_tag_offset :: proc($T: typeid) -> uintptr where type_is_union(T) ---
|
||||
type_union_base_tag_value :: proc($T: typeid) -> int where type_is_union(U) ---
|
||||
type_union_variant_count :: proc($T: typeid) -> int where type_is_union(T) ---
|
||||
type_variant_type_of :: proc($T: typeid, $index: int) -> typeid where type_is_union(T) ---
|
||||
type_variant_index_of :: proc($U, $V: typeid) -> int where type_is_union(U) ---
|
||||
|
||||
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
||||
type_field_type :: proc($T: typeid, $name: string) -> typeid ---
|
||||
@@ -280,7 +293,9 @@ wasm_memory_size :: proc(index: uintptr) -> int ---
|
||||
// 0 - indicates that the thread blocked and then was woken up
|
||||
// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
|
||||
// 2 - the thread blocked, but the timeout
|
||||
@(enable_target_feature="atomics")
|
||||
wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
|
||||
@(enable_target_feature="atomics")
|
||||
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
|
||||
|
||||
// x86 Targets (i386, amd64)
|
||||
@@ -18,9 +18,10 @@
|
||||
// This could change at a later date if the all these data structures are
|
||||
// implemented within the compiler rather than in this "preload" file
|
||||
//
|
||||
//+no-instrumentation
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
// NOTE(bill): This must match the compiler's
|
||||
Calling_Convention :: enum u8 {
|
||||
@@ -176,10 +177,22 @@ Type_Info_Matrix :: struct {
|
||||
row_count: int,
|
||||
column_count: int,
|
||||
// Total element count = column_count * elem_stride
|
||||
layout: enum u8 {
|
||||
Column_Major, // array of column vectors
|
||||
Row_Major, // array of row vectors
|
||||
},
|
||||
}
|
||||
Type_Info_Soa_Pointer :: struct {
|
||||
elem: ^Type_Info,
|
||||
}
|
||||
Type_Info_Bit_Field :: struct {
|
||||
backing_type: ^Type_Info,
|
||||
names: []string,
|
||||
types: []^Type_Info,
|
||||
bit_sizes: []uintptr,
|
||||
bit_offsets: []uintptr,
|
||||
tags: []string,
|
||||
}
|
||||
|
||||
Type_Info_Flag :: enum u8 {
|
||||
Comparable = 0,
|
||||
@@ -222,6 +235,7 @@ Type_Info :: struct {
|
||||
Type_Info_Relative_Multi_Pointer,
|
||||
Type_Info_Matrix,
|
||||
Type_Info_Soa_Pointer,
|
||||
Type_Info_Bit_Field,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -255,6 +269,7 @@ Typeid_Kind :: enum u8 {
|
||||
Relative_Multi_Pointer,
|
||||
Matrix,
|
||||
Soa_Pointer,
|
||||
Bit_Field,
|
||||
}
|
||||
#assert(len(Typeid_Kind) < 32)
|
||||
|
||||
@@ -269,7 +284,7 @@ Typeid_Kind :: enum u8 {
|
||||
|
||||
// NOTE(bill): only the ones that are needed (not all types)
|
||||
// This will be set by the compiler
|
||||
type_table: []Type_Info
|
||||
type_table: []^Type_Info
|
||||
|
||||
args__: []cstring
|
||||
|
||||
@@ -295,6 +310,14 @@ Source_Code_Location :: struct {
|
||||
procedure: string,
|
||||
}
|
||||
|
||||
/*
|
||||
Used by the built-in directory `#load_directory(path: string) -> []Load_Directory_File`
|
||||
*/
|
||||
Load_Directory_File :: struct {
|
||||
name: string,
|
||||
data: []byte, // immutable data
|
||||
}
|
||||
|
||||
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location) -> !
|
||||
|
||||
// Allocation Stuff
|
||||
@@ -306,6 +329,7 @@ Allocator_Mode :: enum byte {
|
||||
Query_Features,
|
||||
Query_Info,
|
||||
Alloc_Non_Zeroed,
|
||||
Resize_Non_Zeroed,
|
||||
}
|
||||
|
||||
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
||||
@@ -573,8 +597,9 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
base := info
|
||||
loop: for {
|
||||
#partial switch i in base.variant {
|
||||
case Type_Info_Named: base = i.base
|
||||
case Type_Info_Enum: base = i.base
|
||||
case Type_Info_Named: base = i.base
|
||||
case Type_Info_Enum: base = i.base
|
||||
case Type_Info_Bit_Field: base = i.backing_type
|
||||
case: break loop
|
||||
}
|
||||
}
|
||||
@@ -589,7 +614,7 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
|
||||
if n < 0 || n >= len(type_table) {
|
||||
n = 0
|
||||
}
|
||||
return &type_table[n]
|
||||
return type_table[n]
|
||||
}
|
||||
|
||||
when !ODIN_NO_RTTI {
|
||||
@@ -1,6 +1,6 @@
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
@builtin
|
||||
Maybe :: union($T: typeid) {T}
|
||||
@@ -109,7 +109,7 @@ remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_locatio
|
||||
|
||||
// `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
//
|
||||
// Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
|
||||
// Note: If the dynamic array has no elements (`len(array) == 0`), this procedure will panic.
|
||||
@builtin
|
||||
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
|
||||
assert(len(array) > 0, loc=loc)
|
||||
@@ -122,7 +122,7 @@ pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bou
|
||||
// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@builtin
|
||||
pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
pop_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
if len(array) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -148,7 +148,7 @@ pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #
|
||||
// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
|
||||
// If the operation is not possible, it will return false.
|
||||
@builtin
|
||||
pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
pop_front_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
|
||||
if len(array) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -169,10 +169,16 @@ clear :: proc{clear_dynamic_array, clear_map}
|
||||
@builtin
|
||||
reserve :: proc{reserve_dynamic_array, reserve_map}
|
||||
|
||||
// `resize` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||
@builtin
|
||||
non_zero_reserve :: proc{non_zero_reserve_dynamic_array}
|
||||
|
||||
// `resize` will try to resize memory of a passed dynamic array to the requested element count (setting the `len`, and possibly `cap`).
|
||||
@builtin
|
||||
resize :: proc{resize_dynamic_array}
|
||||
|
||||
@builtin
|
||||
non_zero_resize :: proc{non_zero_resize_dynamic_array}
|
||||
|
||||
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
||||
@builtin
|
||||
shrink :: proc{shrink_dynamic_array, shrink_map}
|
||||
@@ -234,6 +240,8 @@ delete :: proc{
|
||||
delete_dynamic_array,
|
||||
delete_slice,
|
||||
delete_map,
|
||||
delete_soa_slice,
|
||||
delete_soa_dynamic_array,
|
||||
}
|
||||
|
||||
|
||||
@@ -304,6 +312,7 @@ make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, alloca
|
||||
@(builtin, require_results)
|
||||
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
make_dynamic_array_error_loc(loc, len, cap)
|
||||
array.allocator = allocator // initialize allocator before just in case it fails to allocate any memory
|
||||
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
|
||||
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator}
|
||||
if data == nil && size_of(E) != 0 {
|
||||
@@ -346,7 +355,7 @@ make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := con
|
||||
//
|
||||
// Similar to `new`, the first argument is a type, not a value. Unlike new, make's return type is the same as the
|
||||
// type of its argument, not a pointer to it.
|
||||
// Make uses the specified allocator, default is context.allocator, default is context.allocator
|
||||
// Make uses the specified allocator, default is context.allocator.
|
||||
@builtin
|
||||
make :: proc{
|
||||
make_slice,
|
||||
@@ -404,10 +413,7 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@builtin
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
_append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -418,7 +424,13 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) ->
|
||||
} else {
|
||||
if cap(array) < len(array)+1 {
|
||||
cap := 2 * cap(array) + max(8, 1)
|
||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
||||
|
||||
// do not 'or_return' here as it could be a partial success
|
||||
if should_zero {
|
||||
err = reserve(array, cap, loc)
|
||||
} else {
|
||||
err = non_zero_reserve(array, cap, loc)
|
||||
}
|
||||
}
|
||||
if cap(array)-len(array) > 0 {
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
@@ -435,7 +447,16 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) ->
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
return _append_elem(array, arg, true, loc=loc)
|
||||
}
|
||||
|
||||
@builtin
|
||||
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
return _append_elem(array, arg, false, loc=loc)
|
||||
}
|
||||
|
||||
_append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, loc := #caller_location, args: ..E) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
if array == nil {
|
||||
return 0, nil
|
||||
}
|
||||
@@ -452,7 +473,13 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
|
||||
} else {
|
||||
if cap(array) < len(array)+arg_len {
|
||||
cap := 2 * cap(array) + max(8, arg_len)
|
||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
||||
|
||||
// do not 'or_return' here as it could be a partial success
|
||||
if should_zero {
|
||||
err = reserve(array, cap, loc)
|
||||
} else {
|
||||
err = non_zero_reserve(array, cap, loc)
|
||||
}
|
||||
}
|
||||
arg_len = min(cap(array)-len(array), arg_len)
|
||||
if arg_len > 0 {
|
||||
@@ -468,11 +495,33 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
|
||||
}
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
return _append_elems(array, true, loc, ..args)
|
||||
}
|
||||
|
||||
@builtin
|
||||
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
return _append_elems(array, false, loc, ..args)
|
||||
}
|
||||
|
||||
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
||||
_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
args := transmute([]E)arg
|
||||
if should_zero {
|
||||
return append_elems(array, ..args, loc=loc)
|
||||
} else {
|
||||
return non_zero_append_elems(array, ..args, loc=loc)
|
||||
}
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
args := transmute([]E)arg
|
||||
return append_elems(array, ..args, loc=loc)
|
||||
return _append_elem_string(array, arg, true, loc)
|
||||
}
|
||||
@builtin
|
||||
non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
return _append_elem_string(array, arg, false, loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -492,6 +541,7 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
||||
|
||||
// The append built-in procedure appends elements to the end of a dynamic array
|
||||
@builtin append :: proc{append_elem, append_elems, append_elem_string}
|
||||
@builtin non_zero_append :: proc{non_zero_append_elem, non_zero_append_elems, non_zero_append_elem_string}
|
||||
|
||||
|
||||
@builtin
|
||||
@@ -506,7 +556,7 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
|
||||
|
||||
|
||||
@builtin
|
||||
inject_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 {
|
||||
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 {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -524,7 +574,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
||||
}
|
||||
|
||||
@builtin
|
||||
inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
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 {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -587,11 +637,14 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
||||
|
||||
@builtin
|
||||
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
if index+len(args) < len(array) {
|
||||
new_size := index + len(args)
|
||||
if len(args) == 0 {
|
||||
ok = true
|
||||
} else if new_size < len(array) {
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
} else {
|
||||
resize(array, index+1+len(args), loc) or_return
|
||||
resize(array, new_size, loc) or_return
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
}
|
||||
@@ -633,8 +686,7 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
|
||||
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||
//
|
||||
// Note: Prefer the procedure group `reserve`.
|
||||
@builtin
|
||||
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||
_reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||
if array == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -653,7 +705,12 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
||||
new_size := capacity * size_of(E)
|
||||
allocator := a.allocator
|
||||
|
||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||
new_data: []byte
|
||||
if should_zero {
|
||||
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||
} else {
|
||||
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||
}
|
||||
if new_data == nil && new_size > 0 {
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
@@ -663,17 +720,29 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
||||
return nil
|
||||
}
|
||||
|
||||
@builtin
|
||||
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||
return _reserve_dynamic_array(array, capacity, true, loc)
|
||||
}
|
||||
|
||||
@builtin
|
||||
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||
return _reserve_dynamic_array(array, capacity, false, loc)
|
||||
}
|
||||
|
||||
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||
//
|
||||
// Note: Prefer the procedure group `resize`
|
||||
@builtin
|
||||
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||
_resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||
if array == nil {
|
||||
return nil
|
||||
}
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
|
||||
if length <= a.cap {
|
||||
if should_zero && a.len < length {
|
||||
intrinsics.mem_zero(([^]E)(a.data)[a.len:], (length-a.len)*size_of(E))
|
||||
}
|
||||
a.len = max(length, 0)
|
||||
return nil
|
||||
}
|
||||
@@ -687,7 +756,12 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
||||
new_size := length * size_of(E)
|
||||
allocator := a.allocator
|
||||
|
||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||
new_data : []byte
|
||||
if should_zero {
|
||||
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||
} else {
|
||||
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||
}
|
||||
if new_data == nil && new_size > 0 {
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
@@ -698,6 +772,16 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
||||
return nil
|
||||
}
|
||||
|
||||
@builtin
|
||||
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||
return _resize_dynamic_array(array, length, true, loc=loc)
|
||||
}
|
||||
|
||||
@builtin
|
||||
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||
return _resize_dynamic_array(array, length, false, loc=loc)
|
||||
}
|
||||
|
||||
/*
|
||||
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
||||
|
||||
@@ -743,42 +827,25 @@ map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location)
|
||||
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
|
||||
}
|
||||
|
||||
|
||||
@builtin
|
||||
incl_elem :: proc(s: ^$S/bit_set[$E; $U], elem: E) {
|
||||
s^ |= {elem}
|
||||
}
|
||||
@builtin
|
||||
incl_elems :: proc(s: ^$S/bit_set[$E; $U], elems: ..E) {
|
||||
for elem in elems {
|
||||
s^ |= {elem}
|
||||
// Explicitly inserts a key and value into a map `m`, the same as `map_insert`, but the return values differ.
|
||||
// - `prev_key` will return the previous pointer of a key if it exists, check `found_previous` if was previously found
|
||||
// - `value_ptr` will return the pointer of the memory where the insertion happens, and `nil` if the map failed to resize
|
||||
// - `found_previous` will be true a previous key was found
|
||||
@(builtin, require_results)
|
||||
map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (prev_key: K, value_ptr: ^V, found_previous: bool) {
|
||||
key, value := key, value
|
||||
kp, vp := __dynamic_map_set_extra_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc)
|
||||
if kp != nil {
|
||||
prev_key = (^K)(kp)^
|
||||
found_previous = true
|
||||
}
|
||||
value_ptr = (^V)(vp)
|
||||
return
|
||||
}
|
||||
@builtin
|
||||
incl_bit_set :: proc(s: ^$S/bit_set[$E; $U], other: S) {
|
||||
s^ |= other
|
||||
}
|
||||
@builtin
|
||||
excl_elem :: proc(s: ^$S/bit_set[$E; $U], elem: E) {
|
||||
s^ &~= {elem}
|
||||
}
|
||||
@builtin
|
||||
excl_elems :: proc(s: ^$S/bit_set[$E; $U], elems: ..E) {
|
||||
for elem in elems {
|
||||
s^ &~= {elem}
|
||||
}
|
||||
}
|
||||
@builtin
|
||||
excl_bit_set :: proc(s: ^$S/bit_set[$E; $U], other: S) {
|
||||
s^ &~= other
|
||||
}
|
||||
|
||||
@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set}
|
||||
@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set}
|
||||
|
||||
|
||||
@builtin
|
||||
card :: proc(s: $S/bit_set[$E; $U]) -> int {
|
||||
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
||||
when size_of(S) == 1 {
|
||||
return int(intrinsics.count_ones(transmute(u8)s))
|
||||
} else when size_of(S) == 2 {
|
||||
@@ -1,6 +1,6 @@
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
_ :: intrinsics
|
||||
|
||||
/*
|
||||
@@ -287,7 +287,7 @@ append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_locat
|
||||
footer := raw_soa_footer(array)
|
||||
|
||||
if size_of(E) > 0 && cap(array)-len(array) > 0 {
|
||||
ti := type_info_of(typeid_of(T))
|
||||
ti := type_info_of(T)
|
||||
ti = type_info_base(ti)
|
||||
si := &ti.variant.(Type_Info_Struct)
|
||||
field_count: uintptr
|
||||
@@ -425,4 +425,97 @@ clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
|
||||
@builtin
|
||||
clear_soa :: proc{
|
||||
clear_soa_dynamic_array,
|
||||
}
|
||||
}
|
||||
|
||||
// Converts soa slice into a soa dynamic array without cloning or allocating memory
|
||||
@(require_results)
|
||||
into_dynamic_soa :: proc(array: $T/#soa[]$E) -> #soa[dynamic]E {
|
||||
d: #soa[dynamic]E
|
||||
footer := raw_soa_footer_dynamic_array(&d)
|
||||
footer^ = {
|
||||
cap = len(array),
|
||||
len = 0,
|
||||
allocator = nil_allocator(),
|
||||
}
|
||||
|
||||
field_count: uintptr
|
||||
when intrinsics.type_is_array(E) {
|
||||
field_count = len(E)
|
||||
} else {
|
||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
||||
}
|
||||
|
||||
array := array
|
||||
dynamic_data := ([^]rawptr)(&d)[:field_count]
|
||||
slice_data := ([^]rawptr)(&array)[:field_count]
|
||||
copy(dynamic_data, slice_data)
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// `unordered_remove_soa` removed the element at the specified `index`. It does so by replacing the current end value
|
||||
// with the old value, and reducing the length of the dynamic array by 1.
|
||||
//
|
||||
// Note: This is an O(1) operation.
|
||||
// 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.
|
||||
@builtin
|
||||
unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array))
|
||||
if index+1 < len(array) {
|
||||
ti := type_info_of(typeid_of(T))
|
||||
ti = type_info_base(ti)
|
||||
si := &ti.variant.(Type_Info_Struct)
|
||||
|
||||
field_count: uintptr
|
||||
when intrinsics.type_is_array(E) {
|
||||
field_count = len(E)
|
||||
} else {
|
||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
||||
}
|
||||
|
||||
data := uintptr(array)
|
||||
for i in 0..<field_count {
|
||||
type := si.types[i].variant.(Type_Info_Pointer).elem
|
||||
|
||||
offset := rawptr((^uintptr)(data)^ + uintptr(index*type.size))
|
||||
final := rawptr((^uintptr)(data)^ + uintptr((len(array)-1)*type.size))
|
||||
mem_copy(offset, final, type.size)
|
||||
data += size_of(rawptr)
|
||||
}
|
||||
}
|
||||
raw_soa_footer_dynamic_array(array).len -= 1
|
||||
}
|
||||
|
||||
// `ordered_remove_soa` removed the element at the specified `index` whilst keeping the order of the other elements.
|
||||
//
|
||||
// Note: This is an O(N) operation.
|
||||
// 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.
|
||||
@builtin
|
||||
ordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array))
|
||||
if index+1 < len(array) {
|
||||
ti := type_info_of(typeid_of(T))
|
||||
ti = type_info_base(ti)
|
||||
si := &ti.variant.(Type_Info_Struct)
|
||||
|
||||
field_count: uintptr
|
||||
when intrinsics.type_is_array(E) {
|
||||
field_count = len(E)
|
||||
} else {
|
||||
field_count = uintptr(intrinsics.type_struct_field_count(E))
|
||||
}
|
||||
|
||||
data := uintptr(array)
|
||||
for i in 0..<field_count {
|
||||
type := si.types[i].variant.(Type_Info_Pointer).elem
|
||||
|
||||
offset := (^uintptr)(data)^ + uintptr(index*type.size)
|
||||
length := type.size*(len(array) - index - 1)
|
||||
mem_copy(rawptr(offset), rawptr(offset + uintptr(type.size)), length)
|
||||
data += size_of(rawptr)
|
||||
}
|
||||
}
|
||||
raw_soa_footer_dynamic_array(array).len -= 1
|
||||
}
|
||||
+12
-12
@@ -1,6 +1,6 @@
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE)
|
||||
|
||||
@@ -28,11 +28,11 @@ safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
memory_block_alloc :: proc(allocator: Allocator, capacity: uint, loc := #caller_location) -> (block: ^Memory_Block, err: Allocator_Error) {
|
||||
total_size := uint(capacity + size_of(Memory_Block))
|
||||
base_offset := uintptr(size_of(Memory_Block))
|
||||
memory_block_alloc :: proc(allocator: Allocator, capacity: uint, alignment: uint, loc := #caller_location) -> (block: ^Memory_Block, err: Allocator_Error) {
|
||||
total_size := uint(capacity + max(alignment, size_of(Memory_Block)))
|
||||
base_offset := uintptr(max(alignment, size_of(Memory_Block)))
|
||||
|
||||
min_alignment: int = max(16, align_of(Memory_Block))
|
||||
min_alignment: int = max(16, align_of(Memory_Block), int(alignment))
|
||||
data := mem_alloc(int(total_size), min_alignment, allocator, loc) or_return
|
||||
block = (^Memory_Block)(raw_data(data))
|
||||
end := uintptr(raw_data(data)[len(data):])
|
||||
@@ -102,20 +102,20 @@ arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_locatio
|
||||
if size == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.capacity {
|
||||
size = align_forward_uint(size, alignment)
|
||||
|
||||
needed := align_forward_uint(size, alignment)
|
||||
if arena.curr_block == nil || (safe_add(arena.curr_block.used, needed) or_else 0) > arena.curr_block.capacity {
|
||||
if arena.minimum_block_size == 0 {
|
||||
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
||||
}
|
||||
|
||||
block_size := max(size, arena.minimum_block_size)
|
||||
block_size := max(needed, arena.minimum_block_size)
|
||||
|
||||
if arena.backing_allocator.procedure == nil {
|
||||
arena.backing_allocator = default_allocator()
|
||||
}
|
||||
|
||||
new_block := memory_block_alloc(arena.backing_allocator, block_size, loc) or_return
|
||||
new_block := memory_block_alloc(arena.backing_allocator, block_size, alignment, loc) or_return
|
||||
new_block.prev = arena.curr_block
|
||||
arena.curr_block = new_block
|
||||
arena.total_capacity += new_block.capacity
|
||||
@@ -134,7 +134,7 @@ arena_init :: proc(arena: ^Arena, size: uint, backing_allocator: Allocator, loc
|
||||
arena^ = {}
|
||||
arena.backing_allocator = backing_allocator
|
||||
arena.minimum_block_size = max(size, 1<<12) // minimum block size of 4 KiB
|
||||
new_block := memory_block_alloc(arena.backing_allocator, arena.minimum_block_size, loc) or_return
|
||||
new_block := memory_block_alloc(arena.backing_allocator, arena.minimum_block_size, 0, loc) or_return
|
||||
arena.curr_block = new_block
|
||||
arena.total_capacity += new_block.capacity
|
||||
return nil
|
||||
@@ -195,7 +195,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
err = .Mode_Not_Implemented
|
||||
case .Free_All:
|
||||
arena_free_all(arena, location)
|
||||
case .Resize:
|
||||
case .Resize, .Resize_Non_Zeroed:
|
||||
old_data := ([^]byte)(old_memory)
|
||||
|
||||
switch {
|
||||
@@ -0,0 +1,12 @@
|
||||
package runtime
|
||||
|
||||
when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
default_allocator_proc :: nil_allocator_proc
|
||||
default_allocator :: nil_allocator
|
||||
} else when ODIN_DEFAULT_TO_PANIC_ALLOCATOR {
|
||||
default_allocator_proc :: panic_allocator_proc
|
||||
default_allocator :: panic_allocator
|
||||
} else {
|
||||
default_allocator :: heap_allocator
|
||||
default_allocator_proc :: heap_allocator_proc
|
||||
}
|
||||
+6
-12
@@ -10,7 +10,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil, .None
|
||||
case .Free_All:
|
||||
return nil, .Mode_Not_Implemented
|
||||
case .Resize:
|
||||
case .Resize, .Resize_Non_Zeroed:
|
||||
if size == 0 {
|
||||
return nil, .None
|
||||
}
|
||||
@@ -31,14 +31,6 @@ nil_allocator :: proc() -> Allocator {
|
||||
}
|
||||
|
||||
|
||||
|
||||
when ODIN_OS == .Freestanding {
|
||||
default_allocator_proc :: nil_allocator_proc
|
||||
default_allocator :: nil_allocator
|
||||
}
|
||||
|
||||
|
||||
|
||||
panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
@@ -55,6 +47,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
if size > 0 {
|
||||
panic("panic allocator, .Resize called", loc=loc)
|
||||
}
|
||||
case .Resize_Non_Zeroed:
|
||||
if size > 0 {
|
||||
panic("panic allocator, .Alloc_Non_Zeroed called", loc=loc)
|
||||
}
|
||||
case .Free:
|
||||
if old_memory != nil {
|
||||
panic("panic allocator, .Free called", loc=loc)
|
||||
@@ -78,9 +74,7 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
panic_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = nil_allocator_proc,
|
||||
procedure = panic_allocator_proc,
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ memcpy
|
||||
memove
|
||||
|
||||
|
||||
## Procedures required by the LLVM backend
|
||||
## Procedures required by the LLVM backend if u128/i128 is used
|
||||
umodti3
|
||||
udivti3
|
||||
modti3
|
||||
@@ -59,11 +59,12 @@ truncdfhf2
|
||||
gnu_h2f_ieee
|
||||
gnu_f2h_ieee
|
||||
extendhfsf2
|
||||
|
||||
## Procedures required by the LLVM backend if f16 is used
|
||||
__ashlti3 // wasm specific
|
||||
__multi3 // wasm specific
|
||||
|
||||
|
||||
|
||||
## Required an entry point is defined (i.e. 'main')
|
||||
|
||||
args__
|
||||
@@ -1,6 +1,6 @@
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
_ :: intrinsics
|
||||
|
||||
// High performance, cache-friendly, open-addressed Robin Hood hashing hash map
|
||||
@@ -44,7 +44,7 @@ _ :: intrinsics
|
||||
MAP_LOAD_FACTOR :: 75
|
||||
|
||||
// Minimum log2 capacity.
|
||||
MAP_MIN_LOG2_CAPACITY :: 6 // 64 elements
|
||||
MAP_MIN_LOG2_CAPACITY :: 3 // 8 elements
|
||||
|
||||
// Has to be less than 100% though.
|
||||
#assert(MAP_LOAD_FACTOR < 100)
|
||||
@@ -333,7 +333,7 @@ map_kvh_data_values_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^
|
||||
}
|
||||
|
||||
|
||||
@(private, require_results)
|
||||
@(require_results)
|
||||
map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr, info: ^Map_Info) -> uintptr {
|
||||
round :: #force_inline proc "contextless" (value: uintptr) -> uintptr {
|
||||
CACHE_MASK :: MAP_CACHE_LINE_SIZE - 1
|
||||
@@ -350,6 +350,12 @@ map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr
|
||||
return size
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
map_total_allocation_size_from_value :: #force_inline proc "contextless" (m: $M/map[$K]$V) -> uintptr {
|
||||
return map_total_allocation_size(uintptr(cap(m)), map_info(M))
|
||||
}
|
||||
|
||||
|
||||
// The only procedure which needs access to the context is the one which allocates the map.
|
||||
@(require_results)
|
||||
map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, allocator := context.allocator, loc := #caller_location) -> (result: Raw_Map, err: Allocator_Error) {
|
||||
@@ -391,7 +397,8 @@ map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, alloc
|
||||
// arrays to reduce variance. This swapping can only be done with memcpy since
|
||||
// there is no type information.
|
||||
//
|
||||
// This procedure returns the address of the just inserted value.
|
||||
// This procedure returns the address of the just inserted value, and will
|
||||
// return 'nil' if there was no room to insert the entry
|
||||
@(require_results)
|
||||
map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (result: uintptr) {
|
||||
h := h
|
||||
@@ -415,6 +422,11 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
|
||||
tv := map_cell_index_dynamic(sv, info.vs, 1)
|
||||
|
||||
swap_loop: for {
|
||||
if distance > mask {
|
||||
// Failed to find an empty slot and prevent infinite loop
|
||||
panic("unable to insert into a map")
|
||||
}
|
||||
|
||||
element_hash := hs[pos]
|
||||
|
||||
if map_hash_is_empty(element_hash) {
|
||||
@@ -841,6 +853,33 @@ __dynamic_map_get :: proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info:
|
||||
}
|
||||
}
|
||||
|
||||
__dynamic_map_get_key_and_value :: proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, key: rawptr) -> (key_ptr, value_ptr: rawptr) {
|
||||
if m.len == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
pos := map_desired_position(m^, h)
|
||||
distance := uintptr(0)
|
||||
mask := (uintptr(1) << map_log2_cap(m^)) - 1
|
||||
ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
|
||||
for {
|
||||
element_hash := hs[pos]
|
||||
if map_hash_is_empty(element_hash) {
|
||||
return nil, nil
|
||||
} else if distance > map_probe_distance(m^, element_hash, pos) {
|
||||
return nil, nil
|
||||
} else if element_hash == h {
|
||||
other_key := rawptr(map_cell_index_dynamic(ks, info.ks, pos))
|
||||
if info.key_equal(key, other_key) {
|
||||
key_ptr = other_key
|
||||
value_ptr = rawptr(map_cell_index_dynamic(vs, info.vs, pos))
|
||||
return
|
||||
}
|
||||
}
|
||||
pos = (pos + 1) & mask
|
||||
distance += 1
|
||||
}
|
||||
}
|
||||
|
||||
// IMPORTANT: USED WITHIN THE COMPILER
|
||||
__dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (err: Allocator_Error, has_grown: bool) {
|
||||
if m.len >= map_resize_threshold(m^) {
|
||||
@@ -871,9 +910,37 @@ __dynamic_map_set :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_In
|
||||
}
|
||||
|
||||
result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
|
||||
m.len += 1
|
||||
if result != 0 {
|
||||
m.len += 1
|
||||
}
|
||||
return rawptr(result)
|
||||
}
|
||||
__dynamic_map_set_extra_without_hash :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key, value: rawptr, loc := #caller_location) -> (prev_key_ptr, value_ptr: rawptr) {
|
||||
return __dynamic_map_set_extra(m, info, info.key_hasher(key, map_seed(m^)), key, value, loc)
|
||||
}
|
||||
|
||||
__dynamic_map_set_extra :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, hash: Map_Hash, key, value: rawptr, loc := #caller_location) -> (prev_key_ptr, value_ptr: rawptr) {
|
||||
if prev_key_ptr, value_ptr = __dynamic_map_get_key_and_value(m, info, hash, key); value_ptr != nil {
|
||||
intrinsics.mem_copy_non_overlapping(value_ptr, value, info.vs.size_of_type)
|
||||
return
|
||||
}
|
||||
|
||||
hash := hash
|
||||
err, has_grown := __dynamic_map_check_grow(m, info, loc)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
if has_grown {
|
||||
hash = info.key_hasher(key, map_seed(m^))
|
||||
}
|
||||
|
||||
result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
|
||||
if result != 0 {
|
||||
m.len += 1
|
||||
}
|
||||
return nil, rawptr(result)
|
||||
}
|
||||
|
||||
|
||||
// IMPORTANT: USED WITHIN THE COMPILER
|
||||
@(private)
|
||||
@@ -1,8 +1,9 @@
|
||||
//+private
|
||||
//+build linux, darwin, freebsd, openbsd
|
||||
//+build linux, darwin, freebsd, openbsd, haiku
|
||||
//+no-instrumentation
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
when ODIN_BUILD_MODE == .Dynamic {
|
||||
@(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/)
|
||||
@@ -26,8 +27,13 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
// to retrieve argc and argv from the stack
|
||||
when ODIN_ARCH == .amd64 {
|
||||
@require foreign import entry "entry_unix_no_crt_amd64.asm"
|
||||
SYS_exit :: 60
|
||||
} else when ODIN_ARCH == .i386 {
|
||||
@require foreign import entry "entry_unix_no_crt_i386.asm"
|
||||
SYS_exit :: 1
|
||||
} else when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
|
||||
@require foreign import entry "entry_unix_no_crt_darwin_arm64.asm"
|
||||
SYS_exit :: 1
|
||||
}
|
||||
@(link_name="_start_odin", linkage="strong", require)
|
||||
_start_odin :: proc "c" (argc: i32, argv: [^]cstring) -> ! {
|
||||
@@ -36,11 +42,7 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
#force_no_inline _startup_runtime()
|
||||
intrinsics.__entry_point()
|
||||
#force_no_inline _cleanup_runtime()
|
||||
when ODIN_ARCH == .amd64 {
|
||||
intrinsics.syscall(/*SYS_exit = */60)
|
||||
} else when ODIN_ARCH == .i386 {
|
||||
intrinsics.syscall(/*SYS_exit = */1)
|
||||
}
|
||||
intrinsics.syscall(SYS_exit, 0)
|
||||
unreachable()
|
||||
}
|
||||
} else {
|
||||
@@ -0,0 +1,20 @@
|
||||
.section __TEXT,__text
|
||||
|
||||
; NOTE(laytan): this should ideally be the -minimum-os-version flag but there is no nice way of preprocessing assembly in Odin.
|
||||
; 10 seems to be the lowest it goes and I don't see it mess with any targeted os version so this seems fine.
|
||||
.build_version macos, 10, 0
|
||||
|
||||
.extern __start_odin
|
||||
|
||||
.global _main
|
||||
.align 2
|
||||
_main:
|
||||
mov x5, sp ; use x5 as the stack pointer
|
||||
|
||||
str x0, [x5] ; get argc into x0 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment)
|
||||
str x1, [x5, #8] ; get argv into x1
|
||||
|
||||
and sp, x5, #~15 ; force 16-byte alignment of the stack
|
||||
|
||||
bl __start_odin ; call into Odin entry point
|
||||
ret ; should never get here
|
||||
@@ -1,8 +1,9 @@
|
||||
//+private
|
||||
//+build wasm32, wasm64p32
|
||||
//+no-instrumentation
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||
@(link_name="_start", linkage="strong", require, export)
|
||||
@@ -1,12 +1,13 @@
|
||||
//+private
|
||||
//+build windows
|
||||
//+no-instrumentation
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
when ODIN_BUILD_MODE == .Dynamic {
|
||||
@(link_name="DllMain", linkage="strong", require)
|
||||
DllMain :: proc "stdcall" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
||||
DllMain :: proc "system" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
||||
context = default_context()
|
||||
|
||||
// Populate Windows DLL-specific global
|
||||
@@ -28,7 +29,7 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||
when ODIN_ARCH == .i386 || ODIN_NO_CRT {
|
||||
@(link_name="mainCRTStartup", linkage="strong", require)
|
||||
mainCRTStartup :: proc "stdcall" () -> i32 {
|
||||
mainCRTStartup :: proc "system" () -> i32 {
|
||||
context = default_context()
|
||||
#force_no_inline _startup_runtime()
|
||||
intrinsics.__entry_point()
|
||||
@@ -1,5 +1,6 @@
|
||||
package runtime
|
||||
|
||||
@(no_instrumentation)
|
||||
bounds_trap :: proc "contextless" () -> ! {
|
||||
when ODIN_OS == .Windows {
|
||||
windows_trap_array_bounds()
|
||||
@@ -8,6 +9,7 @@ bounds_trap :: proc "contextless" () -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
@(no_instrumentation)
|
||||
type_assertion_trap :: proc "contextless" () -> ! {
|
||||
when ODIN_OS == .Windows {
|
||||
windows_trap_type_assertion()
|
||||
@@ -21,7 +23,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
|
||||
if uint(index) < uint(count) {
|
||||
return
|
||||
}
|
||||
@(cold)
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Index ")
|
||||
@@ -34,6 +36,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
|
||||
handle_error(file, line, column, index, count)
|
||||
}
|
||||
|
||||
@(no_instrumentation)
|
||||
slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid slice indices ")
|
||||
@@ -46,6 +49,7 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h
|
||||
bounds_trap()
|
||||
}
|
||||
|
||||
@(no_instrumentation)
|
||||
multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid slice indices ")
|
||||
@@ -82,7 +86,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
|
||||
if 0 <= low && low <= high && high <= max {
|
||||
return
|
||||
}
|
||||
@(cold)
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid dynamic array indices ")
|
||||
@@ -103,7 +107,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
|
||||
uint(column_index) < uint(column_count) {
|
||||
return
|
||||
}
|
||||
@(cold)
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Matrix indices [")
|
||||
@@ -127,7 +131,7 @@ when ODIN_NO_RTTI {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold)
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion\n")
|
||||
@@ -140,7 +144,7 @@ when ODIN_NO_RTTI {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold)
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion\n")
|
||||
@@ -153,7 +157,7 @@ when ODIN_NO_RTTI {
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
@(cold)
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! {
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""})
|
||||
print_string(" Invalid type assertion from ")
|
||||
@@ -198,7 +202,7 @@ when ODIN_NO_RTTI {
|
||||
return id
|
||||
}
|
||||
|
||||
@(cold)
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
|
||||
|
||||
actual := variant_type(from, from_data)
|
||||
@@ -224,7 +228,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
|
||||
if 0 <= len {
|
||||
return
|
||||
}
|
||||
@(cold)
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! {
|
||||
print_caller_location(loc)
|
||||
print_string(" Invalid slice length for make: ")
|
||||
@@ -239,7 +243,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller
|
||||
if 0 <= len && len <= cap {
|
||||
return
|
||||
}
|
||||
@(cold)
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! {
|
||||
print_caller_location(loc)
|
||||
print_string(" Invalid dynamic array parameters for make: ")
|
||||
@@ -256,7 +260,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
|
||||
if 0 <= cap {
|
||||
return
|
||||
}
|
||||
@(cold)
|
||||
@(cold, no_instrumentation)
|
||||
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! {
|
||||
print_caller_location(loc)
|
||||
print_string(" Invalid map capacity for make: ")
|
||||
@@ -0,0 +1,110 @@
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
heap_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = heap_allocator_proc,
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
|
||||
heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
//
|
||||
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||
// padding. We also store the original pointer returned by heap_alloc right before
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr))
|
||||
space := size + a - 1
|
||||
|
||||
allocated_mem: rawptr
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := ([^]rawptr)(old_ptr)[-1]
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(([^]u8)(allocated_mem)[size_of(rawptr):])
|
||||
|
||||
ptr := uintptr(aligned_mem)
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
|
||||
diff := int(aligned_ptr - ptr)
|
||||
if (size + diff) > space || allocated_mem == nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr)
|
||||
([^]rawptr)(aligned_mem)[-1] = allocated_mem
|
||||
|
||||
return byte_slice(aligned_mem, size), nil
|
||||
}
|
||||
|
||||
aligned_free :: proc(p: rawptr) {
|
||||
if p != nil {
|
||||
heap_free(([^]rawptr)(p)[-1])
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
new_memory = aligned_alloc(new_size, new_alignment, p, zero_memory) or_return
|
||||
|
||||
// NOTE: heap_resize does not zero the new memory, so we do it
|
||||
if zero_memory && new_size > old_size {
|
||||
new_region := raw_data(new_memory[old_size:])
|
||||
intrinsics.mem_zero(new_region, new_size - old_size)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment, nil, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory)
|
||||
|
||||
case .Free_All:
|
||||
return nil, .Mode_Not_Implemented
|
||||
|
||||
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)
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
case .Query_Info:
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
return _heap_alloc(size, zero_memory)
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return _heap_resize(ptr, new_size)
|
||||
}
|
||||
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
_heap_free(ptr)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//+build js, wasi, freestanding, essence
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
unimplemented("base:runtime 'heap_alloc' procedure is not supported on this platform")
|
||||
}
|
||||
|
||||
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
unimplemented("base:runtime 'heap_resize' procedure is not supported on this platform")
|
||||
}
|
||||
|
||||
_heap_free :: proc(ptr: rawptr) {
|
||||
unimplemented("base:runtime 'heap_free' procedure is not supported on this platform")
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//+build linux, darwin, freebsd, openbsd, haiku
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---
|
||||
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---
|
||||
}
|
||||
|
||||
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, size)
|
||||
} else {
|
||||
return _unix_malloc(size)
|
||||
}
|
||||
}
|
||||
|
||||
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
||||
// POSIX platforms. Ensure your caller takes this into account.
|
||||
return _unix_realloc(ptr, new_size)
|
||||
}
|
||||
|
||||
_heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package runtime
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="system")
|
||||
foreign kernel32 {
|
||||
// NOTE(bill): The types are not using the standard names (e.g. DWORD and LPVOID) to just minimizing the dependency
|
||||
|
||||
// default_allocator
|
||||
GetProcessHeap :: proc() -> rawptr ---
|
||||
HeapAlloc :: proc(hHeap: rawptr, dwFlags: u32, dwBytes: uint) -> rawptr ---
|
||||
HeapReAlloc :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr, dwBytes: uint) -> rawptr ---
|
||||
HeapFree :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr) -> b32 ---
|
||||
}
|
||||
|
||||
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
HEAP_ZERO_MEMORY :: 0x00000008
|
||||
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
}
|
||||
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
_heap_free(ptr)
|
||||
return nil
|
||||
}
|
||||
if ptr == nil {
|
||||
return _heap_alloc(new_size)
|
||||
}
|
||||
|
||||
HEAP_ZERO_MEMORY :: 0x00000008
|
||||
return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
||||
}
|
||||
_heap_free :: proc(ptr: rawptr) {
|
||||
if ptr == nil {
|
||||
return
|
||||
}
|
||||
HeapFree(GetProcessHeap(), 0, ptr)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
@(private="file")
|
||||
IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32
|
||||
@@ -11,7 +11,7 @@ RUNTIME_LINKAGE :: "strong" when (
|
||||
ODIN_BUILD_MODE == .Dynamic ||
|
||||
!ODIN_NO_CRT) &&
|
||||
!IS_WASM) else "internal"
|
||||
RUNTIME_REQUIRE :: !ODIN_TILDE
|
||||
RUNTIME_REQUIRE :: false // !ODIN_TILDE
|
||||
|
||||
@(private)
|
||||
__float16 :: f16 when __ODIN_LLVM_F16_SUPPORTED else u16
|
||||
@@ -22,51 +22,7 @@ byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byt
|
||||
return ([^]byte)(data)[:max(len, 0)]
|
||||
}
|
||||
|
||||
bswap_16 :: proc "contextless" (x: u16) -> u16 {
|
||||
return x>>8 | x<<8
|
||||
}
|
||||
|
||||
bswap_32 :: proc "contextless" (x: u32) -> u32 {
|
||||
return x>>24 | (x>>8)&0xff00 | (x<<8)&0xff0000 | x<<24
|
||||
}
|
||||
|
||||
bswap_64 :: proc "contextless" (x: u64) -> u64 {
|
||||
z := x
|
||||
z = (z & 0x00000000ffffffff) << 32 | (z & 0xffffffff00000000) >> 32
|
||||
z = (z & 0x0000ffff0000ffff) << 16 | (z & 0xffff0000ffff0000) >> 16
|
||||
z = (z & 0x00ff00ff00ff00ff) << 8 | (z & 0xff00ff00ff00ff00) >> 8
|
||||
return z
|
||||
}
|
||||
|
||||
bswap_128 :: proc "contextless" (x: u128) -> u128 {
|
||||
z := transmute([4]u32)x
|
||||
z[0], z[3] = bswap_32(z[3]), bswap_32(z[0])
|
||||
z[1], z[2] = bswap_32(z[2]), bswap_32(z[1])
|
||||
return transmute(u128)z
|
||||
}
|
||||
|
||||
bswap_f16 :: proc "contextless" (f: f16) -> f16 {
|
||||
x := transmute(u16)f
|
||||
z := bswap_16(x)
|
||||
return transmute(f16)z
|
||||
|
||||
}
|
||||
|
||||
bswap_f32 :: proc "contextless" (f: f32) -> f32 {
|
||||
x := transmute(u32)f
|
||||
z := bswap_32(x)
|
||||
return transmute(f32)z
|
||||
|
||||
}
|
||||
|
||||
bswap_f64 :: proc "contextless" (f: f64) -> f64 {
|
||||
x := transmute(u64)f
|
||||
z := bswap_64(x)
|
||||
return transmute(f64)z
|
||||
}
|
||||
|
||||
|
||||
is_power_of_two_int :: #force_inline proc(x: int) -> bool {
|
||||
is_power_of_two_int :: #force_inline proc "contextless" (x: int) -> bool {
|
||||
if x <= 0 {
|
||||
return false
|
||||
}
|
||||
@@ -84,7 +40,7 @@ align_forward_int :: #force_inline proc(ptr, align: int) -> int {
|
||||
return p
|
||||
}
|
||||
|
||||
is_power_of_two_uintptr :: #force_inline proc(x: uintptr) -> bool {
|
||||
is_power_of_two_uintptr :: #force_inline proc "contextless" (x: uintptr) -> bool {
|
||||
if x <= 0 {
|
||||
return false
|
||||
}
|
||||
@@ -187,7 +143,7 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
|
||||
return
|
||||
}
|
||||
|
||||
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 :: #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) {
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -198,15 +154,27 @@ mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAUL
|
||||
}
|
||||
return
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||
if should_zero {
|
||||
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||
} else {
|
||||
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, new_size, alignment, nil, 0, loc)
|
||||
}
|
||||
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
|
||||
data = ([^]byte)(ptr)[:old_size]
|
||||
return
|
||||
}
|
||||
|
||||
data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
||||
if should_zero {
|
||||
data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
||||
} else {
|
||||
data, err = allocator.procedure(allocator.data, .Resize_Non_Zeroed, new_size, alignment, ptr, old_size, loc)
|
||||
}
|
||||
if err == .Mode_Not_Implemented {
|
||||
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||
if should_zero {
|
||||
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||
} else {
|
||||
data, err = allocator.procedure(allocator.data, .Alloc_Non_Zeroed, new_size, alignment, nil, 0, loc)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -216,6 +184,13 @@ mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAUL
|
||||
return
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
return _mem_resize(ptr, old_size, new_size, alignment, allocator, false, loc)
|
||||
}
|
||||
|
||||
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
||||
switch {
|
||||
case n == 0: return true
|
||||
@@ -589,36 +564,6 @@ string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
|
||||
return r, size
|
||||
}
|
||||
|
||||
|
||||
abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
|
||||
return -x if x < 0 else x
|
||||
}
|
||||
abs_f32 :: #force_inline proc "contextless" (x: f32) -> f32 {
|
||||
return -x if x < 0 else x
|
||||
}
|
||||
abs_f64 :: #force_inline proc "contextless" (x: f64) -> f64 {
|
||||
return -x if x < 0 else x
|
||||
}
|
||||
|
||||
min_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
|
||||
return a if a < b else b
|
||||
}
|
||||
min_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
|
||||
return a if a < b else b
|
||||
}
|
||||
min_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
|
||||
return a if a < b else b
|
||||
}
|
||||
max_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
|
||||
return a if a > b else b
|
||||
}
|
||||
max_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
|
||||
return a if a > b else b
|
||||
}
|
||||
max_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
|
||||
return a if a > b else b
|
||||
}
|
||||
|
||||
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
|
||||
p, q := abs(real(x)), abs(imag(x))
|
||||
if p < q {
|
||||
@@ -730,7 +675,7 @@ mul_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||
|
||||
return quaternion(t0, t1, t2, t3)
|
||||
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||
}
|
||||
|
||||
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||
@@ -742,7 +687,7 @@ mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||
|
||||
return quaternion(t0, t1, t2, t3)
|
||||
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||
}
|
||||
|
||||
mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||
@@ -754,7 +699,7 @@ mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
|
||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
|
||||
|
||||
return quaternion(t0, t1, t2, t3)
|
||||
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||
}
|
||||
|
||||
quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
||||
@@ -768,7 +713,7 @@ quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
|
||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||
|
||||
return quaternion(t0, t1, t2, t3)
|
||||
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||
}
|
||||
|
||||
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||
@@ -782,7 +727,7 @@ quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||
|
||||
return quaternion(t0, t1, t2, t3)
|
||||
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||
}
|
||||
|
||||
quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||
@@ -796,7 +741,7 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
|
||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
|
||||
|
||||
return quaternion(t0, t1, t2, t3)
|
||||
return quaternion(w=t0, x=t1, y=t2, z=t3)
|
||||
}
|
||||
|
||||
@(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
@@ -1017,9 +962,11 @@ udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||
return udivmod128(a, b, rem)
|
||||
}
|
||||
|
||||
@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
return udivmodti4(a, b, nil)
|
||||
when !IS_WASM {
|
||||
@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
return udivmodti4(a, b, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1089,3 +1036,25 @@ fixdfti :: proc(a: u64) -> i128 {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
|
||||
for i in 0..<size {
|
||||
j := offset+i
|
||||
the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
|
||||
b := the_bit<<(j&7)
|
||||
dst[j/8] &~= b
|
||||
dst[j/8] |= b
|
||||
}
|
||||
}
|
||||
|
||||
__read_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
|
||||
for j in 0..<size {
|
||||
i := offset+j
|
||||
the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
|
||||
b := the_bit<<(j&7)
|
||||
dst[j/8] &~= b
|
||||
dst[j/8] |= b
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package runtime
|
||||
|
||||
_OS_Errno :: distinct int
|
||||
|
||||
stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
return _stderr_write(data)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//+build freebsd, openbsd
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="write")
|
||||
_unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
|
||||
|
||||
__error :: proc() -> ^i32 ---
|
||||
}
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
ret := _unix_write(2, raw_data(data), len(data))
|
||||
if ret < len(data) {
|
||||
err := __error()
|
||||
return int(ret), _OS_Errno(err^ if err != nil else 0)
|
||||
}
|
||||
return int(ret), 0
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
//+build darwin
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
WRITE :: 0x2000004
|
||||
STDERR :: 2
|
||||
ret := intrinsics.syscall(WRITE, STDERR, uintptr(raw_data(data)), uintptr(len(data)))
|
||||
if ret < 0 {
|
||||
return 0, _OS_Errno(-ret)
|
||||
}
|
||||
return int(ret), 0
|
||||
}
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
//+build freestanding
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
// TODO(bill): reimplement `os.write`
|
||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
return 0, -1
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//+build haiku
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
foreign libc {
|
||||
@(link_name="write")
|
||||
_unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
|
||||
|
||||
_errnop :: proc() -> ^i32 ---
|
||||
}
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
ret := _unix_write(2, raw_data(data), len(data))
|
||||
if ret < len(data) {
|
||||
err := _errnop()
|
||||
return int(ret), _OS_Errno(err^ if err != nil else 0)
|
||||
}
|
||||
return int(ret), 0
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
//+build js
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
foreign import "odin_env"
|
||||
|
||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
foreign odin_env {
|
||||
write :: proc "contextless" (fd: u32, p: []byte) ---
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
when ODIN_ARCH == .amd64 {
|
||||
SYS_write :: uintptr(1)
|
||||
} else when ODIN_ARCH == .arm64 {
|
||||
SYS_write :: uintptr(64)
|
||||
} else when ODIN_ARCH == .i386 {
|
||||
SYS_write :: uintptr(4)
|
||||
} else when ODIN_ARCH == .arm32 {
|
||||
SYS_write :: uintptr(4)
|
||||
}
|
||||
|
||||
stderr :: 2
|
||||
|
||||
ret := int(intrinsics.syscall(SYS_write, uintptr(stderr), uintptr(raw_data(data)), uintptr(len(data))))
|
||||
if ret < 0 && ret > -4096 {
|
||||
return 0, _OS_Errno(-ret)
|
||||
}
|
||||
return ret, 0
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
//+build wasi
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
import "core:sys/wasm/wasi"
|
||||
|
||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
data := (wasi.ciovec_t)(data)
|
||||
n, err := wasi.fd_write(1, {data})
|
||||
return int(n), _OS_Errno(err)
|
||||
@@ -0,0 +1,51 @@
|
||||
//+build windows
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="system")
|
||||
foreign kernel32 {
|
||||
// NOTE(bill): The types are not using the standard names (e.g. DWORD and LPVOID) to just minimizing the dependency
|
||||
|
||||
// stderr_write
|
||||
GetStdHandle :: proc(which: u32) -> rawptr ---
|
||||
SetHandleInformation :: proc(hObject: rawptr, dwMask: u32, dwFlags: u32) -> b32 ---
|
||||
WriteFile :: proc(hFile: rawptr, lpBuffer: rawptr, nNumberOfBytesToWrite: u32, lpNumberOfBytesWritten: ^u32, lpOverlapped: rawptr) -> b32 ---
|
||||
GetLastError :: proc() -> u32 ---
|
||||
}
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #no_bounds_check {
|
||||
if len(data) == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
STD_ERROR_HANDLE :: ~u32(0) -12 + 1
|
||||
HANDLE_FLAG_INHERIT :: 0x00000001
|
||||
MAX_RW :: 1<<30
|
||||
|
||||
h := GetStdHandle(STD_ERROR_HANDLE)
|
||||
when size_of(uintptr) == 8 {
|
||||
SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0)
|
||||
}
|
||||
|
||||
single_write_length: u32
|
||||
total_write: i64
|
||||
length := i64(len(data))
|
||||
|
||||
for total_write < length {
|
||||
remaining := length - total_write
|
||||
to_write := u32(min(i32(remaining), MAX_RW))
|
||||
|
||||
e := WriteFile(h, &data[total_write], to_write, &single_write_length, nil)
|
||||
if single_write_length <= 0 || !e {
|
||||
err = _OS_Errno(GetLastError())
|
||||
n = int(total_write)
|
||||
return
|
||||
}
|
||||
total_write += i64(single_write_length)
|
||||
}
|
||||
n = int(total_write)
|
||||
return
|
||||
}
|
||||
@@ -123,13 +123,13 @@ encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
|
||||
}
|
||||
|
||||
print_string :: proc "contextless" (str: string) -> (n: int) {
|
||||
n, _ = os_write(transmute([]byte)str)
|
||||
n, _ = stderr_write(transmute([]byte)str)
|
||||
return
|
||||
}
|
||||
|
||||
print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
||||
for str in args {
|
||||
m, err := os_write(transmute([]byte)str)
|
||||
m, err := stderr_write(transmute([]byte)str)
|
||||
n += m
|
||||
if err != 0 {
|
||||
break
|
||||
@@ -139,7 +139,7 @@ print_strings :: proc "contextless" (args: ..string) -> (n: int) {
|
||||
}
|
||||
|
||||
print_byte :: proc "contextless" (b: byte) -> (n: int) {
|
||||
n, _ = os_write([]byte{b})
|
||||
n, _ = stderr_write([]byte{b})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ print_rune :: proc "contextless" (r: rune) -> int #no_bounds_check {
|
||||
}
|
||||
|
||||
b, n := encode_rune(r)
|
||||
m, _ := os_write(b[:n])
|
||||
m, _ := stderr_write(b[:n])
|
||||
return m
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ print_u64 :: proc "contextless" (x: u64) #no_bounds_check {
|
||||
}
|
||||
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
|
||||
|
||||
os_write(a[i:])
|
||||
stderr_write(a[i:])
|
||||
}
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
|
||||
i -= 1; a[i] = '-'
|
||||
}
|
||||
|
||||
os_write(a[i:])
|
||||
stderr_write(a[i:])
|
||||
}
|
||||
|
||||
print_uint :: proc "contextless" (x: uint) { print_u64(u64(x)) }
|
||||
@@ -459,6 +459,20 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
|
||||
}
|
||||
print_byte(']')
|
||||
|
||||
case Type_Info_Bit_Field:
|
||||
print_string("bit_field ")
|
||||
print_type(info.backing_type)
|
||||
print_string(" {")
|
||||
for name, i in info.names {
|
||||
if i > 0 { print_string(", ") }
|
||||
print_string(name)
|
||||
print_string(": ")
|
||||
print_type(info.types[i])
|
||||
print_string(" | ")
|
||||
print_u64(u64(info.bit_sizes[i]))
|
||||
}
|
||||
print_byte('}')
|
||||
|
||||
|
||||
case Type_Info_Simd_Vector:
|
||||
print_string("#simd[")
|
||||
@@ -4,7 +4,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
||||
foreign import lib "system:NtDll.lib"
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="stdcall")
|
||||
@(default_calling_convention="system")
|
||||
foreign lib {
|
||||
RtlMoveMemory :: proc(dst, s: rawptr, length: int) ---
|
||||
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
|
||||
@@ -37,7 +37,18 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
|
||||
|
||||
@(link_name="bzero", linkage="strong", require)
|
||||
bzero :: proc "c" (ptr: rawptr, len: int) -> rawptr {
|
||||
if ptr != nil && len != 0 {
|
||||
p := ([^]byte)(ptr)
|
||||
for i := 0; i < len; i += 1 {
|
||||
p[i] = 0
|
||||
}
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
|
||||
@(link_name="memmove", linkage="strong", require)
|
||||
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
d, s := ([^]byte)(dst), ([^]byte)(src)
|
||||
@@ -3,7 +3,7 @@ package runtime
|
||||
|
||||
foreign import "system:Foundation.framework"
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
objc_id :: ^intrinsics.objc_object
|
||||
objc_Class :: ^intrinsics.objc_class
|
||||
@@ -7,19 +7,25 @@ ti_int :: struct #raw_union {
|
||||
all: i128,
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
ti_uint :: struct #raw_union {
|
||||
using s: struct { lo, hi: u64 },
|
||||
all: u128,
|
||||
}
|
||||
|
||||
@(link_name="__ashlti3", linkage="strong")
|
||||
__ashlti3 :: proc "contextless" (a: i128, b_: u32) -> i128 {
|
||||
__ashlti3 :: proc "contextless" (la, ha: u64, b_: u32) -> i128 {
|
||||
bits_in_dword :: size_of(u32)*8
|
||||
b := u32(b_)
|
||||
|
||||
input, result: ti_int
|
||||
input.all = a
|
||||
input.lo, input.hi = la, ha
|
||||
if b & bits_in_dword != 0 {
|
||||
result.lo = 0
|
||||
result.hi = input.lo << (b-bits_in_dword)
|
||||
} else {
|
||||
if b == 0 {
|
||||
return a
|
||||
return input.all
|
||||
}
|
||||
result.lo = input.lo<<b
|
||||
result.hi = (input.hi<<b) | (input.lo>>(bits_in_dword-b))
|
||||
@@ -29,12 +35,20 @@ __ashlti3 :: proc "contextless" (a: i128, b_: u32) -> i128 {
|
||||
|
||||
|
||||
@(link_name="__multi3", linkage="strong")
|
||||
__multi3 :: proc "contextless" (a, b: i128) -> i128 {
|
||||
__multi3 :: proc "contextless" (la, ha, lb, hb: u64) -> i128 {
|
||||
x, y, r: ti_int
|
||||
|
||||
x.all = a
|
||||
y.all = b
|
||||
|
||||
x.lo, x.hi = la, ha
|
||||
y.lo, y.hi = lb, hb
|
||||
r.all = i128(x.lo * y.lo) // TODO this is incorrect
|
||||
r.hi += x.hi*y.lo + x.lo*y.hi
|
||||
return r.all
|
||||
}
|
||||
}
|
||||
|
||||
@(link_name="__udivti3", linkage="strong")
|
||||
udivti3 :: proc "c" (la, ha, lb, hb: u64) -> u128 {
|
||||
a, b: ti_uint
|
||||
a.lo, a.hi = la, ha
|
||||
b.lo, b.hi = lb, hb
|
||||
return udivmodti4(a.all, b.all, nil)
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
//+private
|
||||
//+no-instrumentation
|
||||
package runtime
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
@(private)
|
||||
foreign kernel32 {
|
||||
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
|
||||
RaiseException :: proc "system" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: u32, lpArguments: ^uint) -> ! ---
|
||||
}
|
||||
|
||||
windows_trap_array_bounds :: proc "contextless" () -> ! {
|
||||
@@ -1,4 +1,5 @@
|
||||
//+private
|
||||
//+no-instrumentation
|
||||
package runtime
|
||||
|
||||
@require foreign import "system:int64.lib"
|
||||
@@ -12,7 +13,7 @@ windows_trap_array_bounds :: proc "contextless" () -> ! {
|
||||
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C
|
||||
|
||||
foreign kernel32 {
|
||||
RaiseException :: proc "stdcall" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
|
||||
RaiseException :: proc "system" (dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
|
||||
}
|
||||
|
||||
RaiseException(EXCEPTION_ARRAY_BOUNDS_EXCEEDED, 0, 0, nil)
|
||||
@@ -1,6 +1,6 @@
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||
_ctz :: intrinsics.count_trailing_zeros
|
||||
Binary file not shown.
Binary file not shown.
@@ -110,7 +110,8 @@ if %errorlevel% neq 0 goto end_of_build
|
||||
call build_vendor.bat
|
||||
if %errorlevel% neq 0 goto end_of_build
|
||||
|
||||
if %release_mode% EQU 0 odin run examples/demo
|
||||
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 -- Hellope World
|
||||
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
|
||||
+11
-5
@@ -27,11 +27,13 @@ error() {
|
||||
if [ -z "$LLVM_CONFIG" ]; then
|
||||
# darwin, linux, openbsd
|
||||
if [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
|
||||
elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
|
||||
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
|
||||
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
|
||||
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
|
||||
# freebsd
|
||||
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config-17"
|
||||
elif [ -n "$(command -v llvm-config14)" ]; then LLVM_CONFIG="llvm-config-14"
|
||||
elif [ -n "$(command -v llvm-config13)" ]; then LLVM_CONFIG="llvm-config-13"
|
||||
elif [ -n "$(command -v llvm-config12)" ]; then LLVM_CONFIG="llvm-config-12"
|
||||
elif [ -n "$(command -v llvm-config11)" ]; then LLVM_CONFIG="llvm-config-11"
|
||||
@@ -54,15 +56,14 @@ fi
|
||||
|
||||
case "$OS_NAME" in
|
||||
Darwin)
|
||||
if [ "$OS_ARCH" == "arm64" ]; then
|
||||
if [ "$OS_ARCH" = "arm64" ]; then
|
||||
if [ $LLVM_VERSION_MAJOR -lt 13 ] || [ $LLVM_VERSION_MAJOR -gt 17 ]; then
|
||||
error "Darwin Arm64 requires LLVM 13, 14 or 17"
|
||||
fi
|
||||
fi
|
||||
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS -liconv -ldl -framework System"
|
||||
LDFLAGS="$LDFLAGS -lLLVM-C"
|
||||
LDFLAGS="$LDFLAGS -liconv -ldl -framework System -lLLVM"
|
||||
;;
|
||||
FreeBSD)
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
@@ -81,6 +82,11 @@ OpenBSD)
|
||||
LDFLAGS="$LDFLAGS -liconv"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
;;
|
||||
Haiku)
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags) -I/system/develop/headers/private/shared -I/system/develop/headers/private/kernel"
|
||||
LDFLAGS="$LDFLAGS -liconv"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
;;
|
||||
*)
|
||||
error "Platform \"$OS_NAME\" unsupported"
|
||||
;;
|
||||
@@ -95,7 +101,7 @@ build_odin() {
|
||||
EXTRAFLAGS="-O3"
|
||||
;;
|
||||
release-native)
|
||||
if [ "$OS_ARCH" == "arm64" ]; then
|
||||
if [ "$OS_ARCH" = "arm64" ]; then
|
||||
# Use preferred flag for Arm (ie arm64 / aarch64 / etc)
|
||||
EXTRAFLAGS="-O3 -mcpu=native"
|
||||
else
|
||||
@@ -117,7 +123,7 @@ build_odin() {
|
||||
}
|
||||
|
||||
run_demo() {
|
||||
./odin run examples/demo/demo.odin -file
|
||||
./odin run examples/demo/demo.odin -file -- Hellope World
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
|
||||
Regular → Executable
+14
-2
@@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
bucket=$1
|
||||
platform=$2
|
||||
artifact=$3
|
||||
@@ -9,5 +11,15 @@ filename="odin-$platform-nightly+$now.zip"
|
||||
|
||||
echo "Creating archive $filename from $artifact and uploading to $bucket"
|
||||
|
||||
7z a -bd "output/$filename" -r "$artifact"
|
||||
b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"
|
||||
# If this is already zipped up (done before artifact upload to keep permissions in tact), just move it.
|
||||
if [ "${artifact: -4}" == ".zip" ]
|
||||
then
|
||||
echo "Artifact already a zip"
|
||||
mkdir -p "output"
|
||||
mv "$artifact" "output/$filename"
|
||||
else
|
||||
echo "Artifact needs to be zipped"
|
||||
7z a -bd "output/$filename" -r "$artifact"
|
||||
fi
|
||||
|
||||
b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"
|
||||
|
||||
@@ -4,7 +4,7 @@ import "core:bytes"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
// Extra errors returns by scanning procedures
|
||||
Scanner_Extra_Error :: enum i32 {
|
||||
|
||||
@@ -226,7 +226,6 @@ writer_to_writer :: proc(b: ^Writer) -> (s: io.Writer) {
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
_writer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
b := (^Writer)(stream_data)
|
||||
#partial switch mode {
|
||||
|
||||
@@ -895,7 +895,7 @@ split_multi_iterator :: proc(s: ^[]byte, substrs: [][]byte, skip_empty := false)
|
||||
|
||||
|
||||
|
||||
// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
|
||||
// Scrubs invalid utf-8 characters and replaces them with the replacement string
|
||||
// Adjacent invalid bytes are only replaced once
|
||||
scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte {
|
||||
str := s
|
||||
|
||||
+11
-1
@@ -1,6 +1,6 @@
|
||||
package c
|
||||
|
||||
import builtin "core:builtin"
|
||||
import builtin "base:builtin"
|
||||
|
||||
char :: builtin.u8 // assuming -funsigned-char
|
||||
|
||||
@@ -104,3 +104,13 @@ NULL :: rawptr(uintptr(0))
|
||||
NDEBUG :: !ODIN_DEBUG
|
||||
|
||||
CHAR_BIT :: 8
|
||||
|
||||
// Since there are no types in C with an alignment larger than that of
|
||||
// max_align_t, which cannot be larger than sizeof(long double) as any other
|
||||
// exposed type wouldn't be valid C, the maximum alignment possible in a
|
||||
// strictly conformant C implementation is 16 on the platforms we care about.
|
||||
// The choice of 4096 bytes for storage of this type is more than enough on all
|
||||
// relevant platforms.
|
||||
va_list :: struct #align(16) {
|
||||
_: [4096]u8,
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ foreign libc {
|
||||
crealf :: proc(z: complex_float) -> float ---
|
||||
}
|
||||
|
||||
import builtin "core:builtin"
|
||||
import builtin "base:builtin"
|
||||
|
||||
complex_float :: distinct builtin.complex64
|
||||
complex_double :: distinct builtin.complex128
|
||||
|
||||
@@ -80,6 +80,24 @@ when ODIN_OS == .Darwin {
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
when ODIN_OS == .Haiku {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="_errnop")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
B_GENERAL_ERROR_BASE :: min(i32)
|
||||
@(private="file")
|
||||
B_POSIX_ERROR_BASE :: B_GENERAL_ERROR_BASE + 0x7000
|
||||
|
||||
EDOM :: B_POSIX_ERROR_BASE + 16
|
||||
EILSEQ :: B_POSIX_ERROR_BASE + 38
|
||||
ERANGE :: B_POSIX_ERROR_BASE + 17
|
||||
}
|
||||
|
||||
// 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
|
||||
// error value to errno. To work around this, just expose it as a function like
|
||||
|
||||
@@ -2,7 +2,7 @@ package libc
|
||||
|
||||
// 7.12 Mathematics
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
|
||||
+4
-10
@@ -2,7 +2,9 @@ package libc
|
||||
|
||||
// 7.16 Variable arguments
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:c"
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="none")
|
||||
@@ -12,15 +14,7 @@ foreign _ {
|
||||
@(link_name="llvm.va_copy") _va_copy :: proc(dst, src: ^i8) ---
|
||||
}
|
||||
|
||||
// Since there are no types in C with an alignment larger than that of
|
||||
// max_align_t, which cannot be larger than sizeof(long double) as any other
|
||||
// exposed type wouldn't be valid C, the maximum alignment possible in a
|
||||
// strictly conformant C implementation is 16 on the platforms we care about.
|
||||
// The choice of 4096 bytes for storage of this type is more than enough on all
|
||||
// relevant platforms.
|
||||
va_list :: struct #align(16) {
|
||||
_: [4096]u8,
|
||||
}
|
||||
va_list :: c.va_list
|
||||
|
||||
va_start :: #force_inline proc(ap: ^va_list, _: any) {
|
||||
_va_start(cast(^i8)ap)
|
||||
|
||||
@@ -2,7 +2,7 @@ package libc
|
||||
|
||||
// 7.17 Atomics
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
ATOMIC_BOOL_LOCK_FREE :: true
|
||||
ATOMIC_CHAR_LOCK_FREE :: true
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package libc
|
||||
|
||||
import "core:io"
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc {
|
||||
"system:libucrt.lib",
|
||||
@@ -161,6 +163,36 @@ when ODIN_OS == .Darwin {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Haiku {
|
||||
fpos_t :: distinct i64
|
||||
|
||||
_IOFBF :: 0
|
||||
_IOLBF :: 1
|
||||
_IONBF :: 2
|
||||
|
||||
BUFSIZ :: 8192
|
||||
|
||||
EOF :: int(-1)
|
||||
|
||||
FOPEN_MAX :: 128
|
||||
|
||||
FILENAME_MAX :: 256
|
||||
|
||||
L_tmpnam :: 512
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
TMP_MAX :: 32768
|
||||
|
||||
foreign libc {
|
||||
stderr: ^FILE
|
||||
stdin: ^FILE
|
||||
stdout: ^FILE
|
||||
}
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.21.4 Operations on files
|
||||
@@ -218,3 +250,102 @@ foreign libc {
|
||||
ferror :: proc(stream: ^FILE) -> int ---
|
||||
perror :: proc(s: cstring) ---
|
||||
}
|
||||
|
||||
to_stream :: proc(file: ^FILE) -> io.Stream {
|
||||
stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
unknown_or_eof :: proc(f: ^FILE) -> io.Error {
|
||||
switch {
|
||||
case ferror(f) != 0:
|
||||
return .Unknown
|
||||
case feof(f) != 0:
|
||||
return .EOF
|
||||
case:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
file := (^FILE)(stream_data)
|
||||
switch mode {
|
||||
case .Close:
|
||||
if fclose(file) != 0 {
|
||||
return 0, unknown_or_eof(file)
|
||||
}
|
||||
|
||||
case .Flush:
|
||||
if fflush(file) != 0 {
|
||||
return 0, unknown_or_eof(file)
|
||||
}
|
||||
|
||||
case .Read:
|
||||
n = i64(fread(raw_data(p), size_of(byte), len(p), file))
|
||||
if n == 0 { err = unknown_or_eof(file) }
|
||||
|
||||
case .Read_At:
|
||||
curr := ftell(file)
|
||||
if curr == -1 {
|
||||
return 0, unknown_or_eof(file)
|
||||
}
|
||||
|
||||
if fseek(file, long(offset), SEEK_SET) != 0 {
|
||||
return 0, unknown_or_eof(file)
|
||||
}
|
||||
|
||||
defer fseek(file, long(curr), SEEK_SET)
|
||||
|
||||
n = i64(fread(raw_data(p), size_of(byte), len(p), file))
|
||||
if n == 0 { err = unknown_or_eof(file) }
|
||||
|
||||
case .Write:
|
||||
n = i64(fwrite(raw_data(p), size_of(byte), len(p), file))
|
||||
if n == 0 { err = unknown_or_eof(file) }
|
||||
|
||||
case .Write_At:
|
||||
curr := ftell(file)
|
||||
if curr == -1 {
|
||||
return 0, unknown_or_eof(file)
|
||||
}
|
||||
|
||||
if fseek(file, long(offset), SEEK_SET) != 0 {
|
||||
return 0, unknown_or_eof(file)
|
||||
}
|
||||
|
||||
defer fseek(file, long(curr), SEEK_SET)
|
||||
|
||||
n = i64(fwrite(raw_data(p), size_of(byte), len(p), file))
|
||||
if n == 0 { err = unknown_or_eof(file) }
|
||||
|
||||
case .Seek:
|
||||
if fseek(file, long(offset), int(whence)) != 0 {
|
||||
return 0, unknown_or_eof(file)
|
||||
}
|
||||
|
||||
case .Size:
|
||||
curr := ftell(file)
|
||||
if curr == -1 {
|
||||
return 0, unknown_or_eof(file)
|
||||
}
|
||||
defer fseek(file, curr, SEEK_SET)
|
||||
|
||||
if fseek(file, 0, SEEK_END) != 0 {
|
||||
return 0, unknown_or_eof(file)
|
||||
}
|
||||
|
||||
n = i64(ftell(file))
|
||||
if n == -1 {
|
||||
return 0, unknown_or_eof(file)
|
||||
}
|
||||
|
||||
case .Destroy:
|
||||
return 0, .Empty
|
||||
|
||||
case .Query:
|
||||
return io.query_utility({ .Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size })
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return {
|
||||
data = file,
|
||||
procedure = stream_proc,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package libc
|
||||
|
||||
import "core:runtime"
|
||||
import "base:runtime"
|
||||
|
||||
// 7.24 String handling
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ when ODIN_OS == .Windows {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku {
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.27.2 Time manipulation functions
|
||||
|
||||
@@ -2,6 +2,8 @@ package libc
|
||||
|
||||
import "core:c"
|
||||
|
||||
#assert(!ODIN_NO_CRT, `"core:c/libc" cannot be imported when '-no-crt' is used`)
|
||||
|
||||
char :: c.char // assuming -funsigned-char
|
||||
|
||||
schar :: c.schar
|
||||
|
||||
@@ -29,7 +29,11 @@ when ODIN_OS == .Windows {
|
||||
} else when ODIN_OS == .FreeBSD {
|
||||
wctrans_t :: distinct int
|
||||
wctype_t :: distinct ulong
|
||||
|
||||
|
||||
} else when ODIN_OS == .Haiku {
|
||||
wctrans_t :: distinct i32
|
||||
wctype_t :: distinct i32
|
||||
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
|
||||
+33
-43
@@ -12,7 +12,7 @@ package compress
|
||||
|
||||
import "core:io"
|
||||
import "core:bytes"
|
||||
import "core:runtime"
|
||||
import "base:runtime"
|
||||
|
||||
/*
|
||||
These settings bound how much compression algorithms will allocate for their output buffer.
|
||||
@@ -20,10 +20,9 @@ import "core:runtime"
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
When a decompression routine doesn't stream its output, but writes to a buffer,
|
||||
we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
|
||||
*/
|
||||
|
||||
// When a decompression routine doesn't stream its output, but writes to a buffer,
|
||||
// we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
|
||||
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20))
|
||||
|
||||
/*
|
||||
@@ -34,16 +33,14 @@ COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 2
|
||||
|
||||
*/
|
||||
when size_of(uintptr) == 8 {
|
||||
/*
|
||||
For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
||||
which is GZIP and PKZIP's max payload size.
|
||||
*/
|
||||
|
||||
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
||||
// which is GZIP and PKZIP's max payload size.
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
|
||||
} else {
|
||||
/*
|
||||
For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
||||
*/
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
||||
|
||||
// For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
||||
}
|
||||
|
||||
|
||||
@@ -69,9 +66,8 @@ General_Error :: enum {
|
||||
Incompatible_Options,
|
||||
Unimplemented,
|
||||
|
||||
/*
|
||||
Memory errors
|
||||
*/
|
||||
// Memory errors
|
||||
|
||||
Allocation_Failed,
|
||||
Resize_Failed,
|
||||
}
|
||||
@@ -86,17 +82,16 @@ GZIP_Error :: enum {
|
||||
Payload_Length_Invalid,
|
||||
Payload_CRC_Invalid,
|
||||
|
||||
/*
|
||||
GZIP's payload can be a maximum of max(u32le), or 4 GiB.
|
||||
If you tell it you expect it to contain more, that's obviously an error.
|
||||
*/
|
||||
Payload_Size_Exceeds_Max_Payload,
|
||||
/*
|
||||
For buffered instead of streamed output, the payload size can't exceed
|
||||
the max set by the `COMPRESS_OUTPUT_ALLOCATE_MAX` switch in compress/common.odin.
|
||||
// GZIP's payload can be a maximum of max(u32le), or 4 GiB.
|
||||
// If you tell it you expect it to contain more, that's obviously an error.
|
||||
|
||||
Payload_Size_Exceeds_Max_Payload,
|
||||
|
||||
// For buffered instead of streamed output, the payload size can't exceed
|
||||
// the max set by the `COMPRESS_OUTPUT_ALLOCATE_MAX` switch in compress/common.odin.
|
||||
//
|
||||
// You can tweak this setting using `-define:COMPRESS_OUTPUT_ALLOCATE_MAX=size_in_bytes`
|
||||
|
||||
You can tweak this setting using `-define:COMPRESS_OUTPUT_ALLOCATE_MAX=size_in_bytes`
|
||||
*/
|
||||
Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX,
|
||||
|
||||
}
|
||||
@@ -137,9 +132,8 @@ Context_Memory_Input :: struct #packed {
|
||||
code_buffer: u64,
|
||||
num_bits: u64,
|
||||
|
||||
/*
|
||||
If we know the data size, we can optimize the reads and writes.
|
||||
*/
|
||||
// If we know the data size, we can optimize the reads and writes.
|
||||
|
||||
size_packed: i64,
|
||||
size_unpacked: i64,
|
||||
}
|
||||
@@ -159,18 +153,16 @@ Context_Stream_Input :: struct #packed {
|
||||
code_buffer: u64,
|
||||
num_bits: u64,
|
||||
|
||||
/*
|
||||
If we know the data size, we can optimize the reads and writes.
|
||||
*/
|
||||
// If we know the data size, we can optimize the reads and writes.
|
||||
|
||||
size_packed: i64,
|
||||
size_unpacked: i64,
|
||||
|
||||
/*
|
||||
Flags:
|
||||
`input_fully_in_memory`
|
||||
true = This tells us we read input from `input_data` exclusively. [] = EOF.
|
||||
false = Try to refill `input_data` from the `input` stream.
|
||||
*/
|
||||
// Flags:
|
||||
// `input_fully_in_memory`
|
||||
// true = This tells us we read input from `input_data` exclusively. [] = EOF.
|
||||
// false = Try to refill `input_data` from the `input` stream.
|
||||
|
||||
input_fully_in_memory: b8,
|
||||
|
||||
padding: [1]u8,
|
||||
@@ -214,7 +206,7 @@ read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int
|
||||
@(optimization_mode="speed")
|
||||
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||
// TODO: REMOVE ALL USE OF context.temp_allocator here
|
||||
// the is literally no need for it
|
||||
// there is literally no need for it
|
||||
b := make([]u8, size, context.temp_allocator)
|
||||
_ = io.read(z.input, b[:]) or_return
|
||||
return b, nil
|
||||
@@ -248,10 +240,8 @@ read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8,
|
||||
|
||||
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
|
||||
|
||||
/*
|
||||
You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
||||
preferentially.
|
||||
*/
|
||||
// You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
||||
// preferentially.
|
||||
@(optimization_mode="speed")
|
||||
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
||||
if z.num_bits >= 8 {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
// package shoco is an implementation of the shoco short string compressor
|
||||
package shoco
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
import "core:compress"
|
||||
|
||||
Shoco_Pack :: struct {
|
||||
|
||||
@@ -0,0 +1,678 @@
|
||||
/*
|
||||
package avl implements an AVL tree.
|
||||
|
||||
The implementation is non-intrusive, and non-recursive.
|
||||
*/
|
||||
package container_avl
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:slice"
|
||||
|
||||
_ :: intrinsics
|
||||
_ :: runtime
|
||||
|
||||
// Originally based on the CC0 implementation by Eric Biggers
|
||||
// See: https://github.com/ebiggers/avl_tree/
|
||||
|
||||
// Direction specifies the traversal direction for a tree iterator.
|
||||
Direction :: enum i8 {
|
||||
// Backward is the in-order backwards direction.
|
||||
Backward = -1,
|
||||
// Forward is the in-order forwards direction.
|
||||
Forward = 1,
|
||||
}
|
||||
|
||||
// Ordering specifies order when inserting/finding values into the tree.
|
||||
Ordering :: slice.Ordering
|
||||
|
||||
// Tree is an AVL tree.
|
||||
Tree :: struct($Value: typeid) {
|
||||
// user_data is a parameter that will be passed to the on_remove
|
||||
// callback.
|
||||
user_data: rawptr,
|
||||
// on_remove is an optional callback that can be called immediately
|
||||
// after a node is removed from the tree.
|
||||
on_remove: proc(value: Value, user_data: rawptr),
|
||||
|
||||
_root: ^Node(Value),
|
||||
_node_allocator: runtime.Allocator,
|
||||
_cmp_fn: proc(a, b: Value) -> Ordering,
|
||||
_size: int,
|
||||
}
|
||||
|
||||
// Node is an AVL tree node.
|
||||
//
|
||||
// WARNING: It is unsafe to mutate value if the node is part of a tree
|
||||
// if doing so will alter the Node's sort position relative to other
|
||||
// elements in the tree.
|
||||
Node :: struct($Value: typeid) {
|
||||
value: Value,
|
||||
|
||||
_parent: ^Node(Value),
|
||||
_left: ^Node(Value),
|
||||
_right: ^Node(Value),
|
||||
_balance: i8,
|
||||
}
|
||||
|
||||
// Iterator is a tree iterator.
|
||||
//
|
||||
// WARNING: It is unsafe to modify the tree while iterating, except via
|
||||
// the iterator_remove method.
|
||||
Iterator :: struct($Value: typeid) {
|
||||
_tree: ^Tree(Value),
|
||||
_cur: ^Node(Value),
|
||||
_next: ^Node(Value),
|
||||
_direction: Direction,
|
||||
_called_next: bool,
|
||||
}
|
||||
|
||||
// init initializes a tree.
|
||||
init :: proc {
|
||||
init_ordered,
|
||||
init_cmp,
|
||||
}
|
||||
|
||||
// init_cmp initializes a tree.
|
||||
init_cmp :: proc(
|
||||
t: ^$T/Tree($Value),
|
||||
cmp_fn: proc(a, b: Value) -> Ordering,
|
||||
node_allocator := context.allocator,
|
||||
) {
|
||||
t._root = nil
|
||||
t._node_allocator = node_allocator
|
||||
t._cmp_fn = cmp_fn
|
||||
t._size = 0
|
||||
}
|
||||
|
||||
// init_ordered initializes a tree containing ordered items, with
|
||||
// a comparison function that results in an ascending order sort.
|
||||
init_ordered :: proc(
|
||||
t: ^$T/Tree($Value),
|
||||
node_allocator := context.allocator,
|
||||
) where intrinsics.type_is_ordered_numeric(Value) {
|
||||
init_cmp(t, slice.cmp_proc(Value), node_allocator)
|
||||
}
|
||||
|
||||
// destroy de-initializes a tree.
|
||||
destroy :: proc(t: ^$T/Tree($Value), call_on_remove: bool = true) {
|
||||
iter := iterator(t, Direction.Forward)
|
||||
for _ in iterator_next(&iter) {
|
||||
iterator_remove(&iter, call_on_remove)
|
||||
}
|
||||
}
|
||||
|
||||
// len returns the number of elements in the tree.
|
||||
len :: proc "contextless" (t: ^$T/Tree($Value)) -> int {
|
||||
return t._size
|
||||
}
|
||||
|
||||
// first returns the first node in the tree (in-order) or nil iff
|
||||
// the tree is empty.
|
||||
first :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
|
||||
return tree_first_or_last_in_order(t, Direction.Backward)
|
||||
}
|
||||
|
||||
// last returns the last element in the tree (in-order) or nil iff
|
||||
// the tree is empty.
|
||||
last :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
|
||||
return tree_first_or_last_in_order(t, Direction.Forward)
|
||||
}
|
||||
|
||||
// find finds the value in the tree, and returns the corresponding
|
||||
// node or nil iff the value is not present.
|
||||
find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) {
|
||||
cur := t._root
|
||||
descend_loop: for cur != nil {
|
||||
switch t._cmp_fn(value, cur.value) {
|
||||
case .Less:
|
||||
cur = cur._left
|
||||
case .Greater:
|
||||
cur = cur._right
|
||||
case .Equal:
|
||||
break descend_loop
|
||||
}
|
||||
}
|
||||
|
||||
return cur
|
||||
}
|
||||
|
||||
// find_or_insert attempts to insert the value into the tree, and returns
|
||||
// the node, a boolean indicating if the value was inserted, and the
|
||||
// node allocator error if relevant. If the value is already
|
||||
// present, the existing node is returned un-altered.
|
||||
find_or_insert :: proc(
|
||||
t: ^$T/Tree($Value),
|
||||
value: Value,
|
||||
) -> (
|
||||
n: ^Node(Value),
|
||||
inserted: bool,
|
||||
err: runtime.Allocator_Error,
|
||||
) {
|
||||
n_ptr := &t._root
|
||||
for n_ptr^ != nil {
|
||||
n = n_ptr^
|
||||
switch t._cmp_fn(value, n.value) {
|
||||
case .Less:
|
||||
n_ptr = &n._left
|
||||
case .Greater:
|
||||
n_ptr = &n._right
|
||||
case .Equal:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
parent := n
|
||||
n = new(Node(Value), t._node_allocator) or_return
|
||||
n.value = value
|
||||
n._parent = parent
|
||||
n_ptr^ = n
|
||||
tree_rebalance_after_insert(t, n)
|
||||
|
||||
t._size += 1
|
||||
inserted = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// remove removes a node or value from the tree, and returns true iff the
|
||||
// removal was successful. While the node's value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove :: proc {
|
||||
remove_value,
|
||||
remove_node,
|
||||
}
|
||||
|
||||
// remove_value removes a value from the tree, and returns true iff the
|
||||
// removal was successful. While the node's value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = true) -> bool {
|
||||
n := find(t, value)
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
return remove_node(t, n, call_on_remove)
|
||||
}
|
||||
|
||||
// remove_node removes a node from the tree, and returns true iff the
|
||||
// removal was successful. While the node's value will be left intact,
|
||||
// the node itself will be freed via the tree's node allocator.
|
||||
remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: bool = true) -> bool {
|
||||
if node._parent == node || (node._parent == nil && t._root != node) {
|
||||
return false
|
||||
}
|
||||
defer {
|
||||
if call_on_remove && t.on_remove != nil {
|
||||
t.on_remove(node.value, t.user_data)
|
||||
}
|
||||
free(node, t._node_allocator)
|
||||
}
|
||||
|
||||
parent: ^Node(Value)
|
||||
left_deleted: bool
|
||||
|
||||
t._size -= 1
|
||||
if node._left != nil && node._right != nil {
|
||||
parent, left_deleted = tree_swap_with_successor(t, node)
|
||||
} else {
|
||||
child := node._left
|
||||
if child == nil {
|
||||
child = node._right
|
||||
}
|
||||
parent = node._parent
|
||||
if parent != nil {
|
||||
if node == parent._left {
|
||||
parent._left = child
|
||||
left_deleted = true
|
||||
} else {
|
||||
parent._right = child
|
||||
left_deleted = false
|
||||
}
|
||||
if child != nil {
|
||||
child._parent = parent
|
||||
}
|
||||
} else {
|
||||
if child != nil {
|
||||
child._parent = parent
|
||||
}
|
||||
t._root = child
|
||||
node_reset(node)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
if left_deleted {
|
||||
parent = tree_handle_subtree_shrink(t, parent, +1, &left_deleted)
|
||||
} else {
|
||||
parent = tree_handle_subtree_shrink(t, parent, -1, &left_deleted)
|
||||
}
|
||||
if parent == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
node_reset(node)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// iterator returns a tree iterator in the specified direction.
|
||||
iterator :: proc "contextless" (t: ^$T/Tree($Value), direction: Direction) -> Iterator(Value) {
|
||||
it: Iterator(Value)
|
||||
it._tree = transmute(^Tree(Value))t
|
||||
it._direction = direction
|
||||
|
||||
iterator_first(&it)
|
||||
|
||||
return it
|
||||
}
|
||||
|
||||
// iterator_from_pos returns a tree iterator in the specified direction,
|
||||
// spanning the range [pos, last] (inclusive).
|
||||
iterator_from_pos :: proc "contextless" (
|
||||
t: ^$T/Tree($Value),
|
||||
pos: ^Node(Value),
|
||||
direction: Direction,
|
||||
) -> Iterator(Value) {
|
||||
it: Iterator(Value)
|
||||
it._tree = transmute(^Tree(Value))t
|
||||
it._direction = direction
|
||||
it._next = nil
|
||||
it._called_next = false
|
||||
|
||||
if it._cur = pos; pos != nil {
|
||||
it._next = node_next_or_prev_in_order(it._cur, it._direction)
|
||||
}
|
||||
|
||||
return it
|
||||
}
|
||||
|
||||
// iterator_get returns the node currently pointed to by the iterator,
|
||||
// or nil iff the node has been removed, the tree is empty, or the end
|
||||
// of the tree has been reached.
|
||||
iterator_get :: proc "contextless" (it: ^$I/Iterator($Value)) -> ^Node(Value) {
|
||||
return it._cur
|
||||
}
|
||||
|
||||
// iterator_remove removes the node currently pointed to by the iterator,
|
||||
// and returns true iff the removal was successful. Semantics are the
|
||||
// same as the Tree remove.
|
||||
iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -> bool {
|
||||
if it._cur == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
ok := remove_node(it._tree, it._cur, call_on_remove)
|
||||
if ok {
|
||||
it._cur = nil
|
||||
}
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
// iterator_next advances the iterator and returns the (node, true) or
|
||||
// or (nil, false) iff the end of the tree has been reached.
|
||||
//
|
||||
// Note: The first call to iterator_next will return the first node instead
|
||||
// of advancing the iterator.
|
||||
iterator_next :: proc "contextless" (it: ^$I/Iterator($Value)) -> (^Node(Value), bool) {
|
||||
// This check is needed so that the first element gets returned from
|
||||
// a brand-new iterator, and so that the somewhat contrived case where
|
||||
// iterator_remove is called before the first call to iterator_next
|
||||
// returns the correct value.
|
||||
if !it._called_next {
|
||||
it._called_next = true
|
||||
|
||||
// There can be the contrived case where iterator_remove is
|
||||
// called before ever calling iterator_next, which needs to be
|
||||
// handled as an actual call to next.
|
||||
//
|
||||
// If this happens it._cur will be nil, so only return the
|
||||
// first value, if it._cur is valid.
|
||||
if it._cur != nil {
|
||||
return it._cur, true
|
||||
}
|
||||
}
|
||||
|
||||
if it._next == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
it._cur = it._next
|
||||
it._next = node_next_or_prev_in_order(it._cur, it._direction)
|
||||
|
||||
return it._cur, true
|
||||
}
|
||||
|
||||
@(private)
|
||||
tree_first_or_last_in_order :: proc "contextless" (
|
||||
t: ^$T/Tree($Value),
|
||||
direction: Direction,
|
||||
) -> ^Node(Value) {
|
||||
first, sign := t._root, i8(direction)
|
||||
if first != nil {
|
||||
for {
|
||||
tmp := node_get_child(first, +sign)
|
||||
if tmp == nil {
|
||||
break
|
||||
}
|
||||
first = tmp
|
||||
}
|
||||
}
|
||||
|
||||
return first
|
||||
}
|
||||
|
||||
@(private)
|
||||
tree_replace_child :: proc "contextless" (
|
||||
t: ^$T/Tree($Value),
|
||||
parent, old_child, new_child: ^Node(Value),
|
||||
) {
|
||||
if parent != nil {
|
||||
if old_child == parent._left {
|
||||
parent._left = new_child
|
||||
} else {
|
||||
parent._right = new_child
|
||||
}
|
||||
} else {
|
||||
t._root = new_child
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
tree_rotate :: proc "contextless" (t: ^$T/Tree($Value), a: ^Node(Value), sign: i8) {
|
||||
b := node_get_child(a, -sign)
|
||||
e := node_get_child(b, +sign)
|
||||
p := a._parent
|
||||
|
||||
node_set_child(a, -sign, e)
|
||||
a._parent = b
|
||||
|
||||
node_set_child(b, +sign, a)
|
||||
b._parent = p
|
||||
|
||||
if e != nil {
|
||||
e._parent = a
|
||||
}
|
||||
|
||||
tree_replace_child(t, p, a, b)
|
||||
}
|
||||
|
||||
@(private)
|
||||
tree_double_rotate :: proc "contextless" (
|
||||
t: ^$T/Tree($Value),
|
||||
b, a: ^Node(Value),
|
||||
sign: i8,
|
||||
) -> ^Node(Value) {
|
||||
e := node_get_child(b, +sign)
|
||||
f := node_get_child(e, -sign)
|
||||
g := node_get_child(e, +sign)
|
||||
p := a._parent
|
||||
e_bal := e._balance
|
||||
|
||||
node_set_child(a, -sign, g)
|
||||
a_bal := -e_bal
|
||||
if sign * e_bal >= 0 {
|
||||
a_bal = 0
|
||||
}
|
||||
node_set_parent_balance(a, e, a_bal)
|
||||
|
||||
node_set_child(b, +sign, f)
|
||||
b_bal := -e_bal
|
||||
if sign * e_bal <= 0 {
|
||||
b_bal = 0
|
||||
}
|
||||
node_set_parent_balance(b, e, b_bal)
|
||||
|
||||
node_set_child(e, +sign, a)
|
||||
node_set_child(e, -sign, b)
|
||||
node_set_parent_balance(e, p, 0)
|
||||
|
||||
if g != nil {
|
||||
g._parent = a
|
||||
}
|
||||
|
||||
if f != nil {
|
||||
f._parent = b
|
||||
}
|
||||
|
||||
tree_replace_child(t, p, a, e)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
@(private)
|
||||
tree_handle_subtree_growth :: proc "contextless" (
|
||||
t: ^$T/Tree($Value),
|
||||
node, parent: ^Node(Value),
|
||||
sign: i8,
|
||||
) -> bool {
|
||||
old_balance_factor := parent._balance
|
||||
if old_balance_factor == 0 {
|
||||
node_adjust_balance_factor(parent, sign)
|
||||
return false
|
||||
}
|
||||
|
||||
new_balance_factor := old_balance_factor + sign
|
||||
if new_balance_factor == 0 {
|
||||
node_adjust_balance_factor(parent, sign)
|
||||
return true
|
||||
}
|
||||
|
||||
if sign * node._balance > 0 {
|
||||
tree_rotate(t, parent, -sign)
|
||||
node_adjust_balance_factor(parent, -sign)
|
||||
node_adjust_balance_factor(node, -sign)
|
||||
} else {
|
||||
tree_double_rotate(t, node, parent, -sign)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@(private)
|
||||
tree_rebalance_after_insert :: proc "contextless" (t: ^$T/Tree($Value), inserted: ^Node(Value)) {
|
||||
node, parent := inserted, inserted._parent
|
||||
switch {
|
||||
case parent == nil:
|
||||
return
|
||||
case node == parent._left:
|
||||
node_adjust_balance_factor(parent, -1)
|
||||
case:
|
||||
node_adjust_balance_factor(parent, +1)
|
||||
}
|
||||
|
||||
if parent._balance == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for done := false; !done; {
|
||||
node = parent
|
||||
if parent = node._parent; parent == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if node == parent._left {
|
||||
done = tree_handle_subtree_growth(t, node, parent, -1)
|
||||
} else {
|
||||
done = tree_handle_subtree_growth(t, node, parent, +1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
tree_swap_with_successor :: proc "contextless" (
|
||||
t: ^$T/Tree($Value),
|
||||
x: ^Node(Value),
|
||||
) -> (
|
||||
^Node(Value),
|
||||
bool,
|
||||
) {
|
||||
ret: ^Node(Value)
|
||||
left_deleted: bool
|
||||
|
||||
y := x._right
|
||||
if y._left == nil {
|
||||
ret = y
|
||||
} else {
|
||||
q: ^Node(Value)
|
||||
|
||||
for {
|
||||
q = y
|
||||
if y = y._left; y._left == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if q._left = y._right; q._left != nil {
|
||||
q._left._parent = q
|
||||
}
|
||||
y._right = x._right
|
||||
x._right._parent = y
|
||||
ret = q
|
||||
left_deleted = true
|
||||
}
|
||||
|
||||
y._left = x._left
|
||||
x._left._parent = y
|
||||
|
||||
y._parent = x._parent
|
||||
y._balance = x._balance
|
||||
|
||||
tree_replace_child(t, x._parent, x, y)
|
||||
|
||||
return ret, left_deleted
|
||||
}
|
||||
|
||||
@(private)
|
||||
tree_handle_subtree_shrink :: proc "contextless" (
|
||||
t: ^$T/Tree($Value),
|
||||
parent: ^Node(Value),
|
||||
sign: i8,
|
||||
left_deleted: ^bool,
|
||||
) -> ^Node(Value) {
|
||||
old_balance_factor := parent._balance
|
||||
if old_balance_factor == 0 {
|
||||
node_adjust_balance_factor(parent, sign)
|
||||
return nil
|
||||
}
|
||||
|
||||
node: ^Node(Value)
|
||||
new_balance_factor := old_balance_factor + sign
|
||||
if new_balance_factor == 0 {
|
||||
node_adjust_balance_factor(parent, sign)
|
||||
node = parent
|
||||
} else {
|
||||
node = node_get_child(parent, sign)
|
||||
if sign * node._balance >= 0 {
|
||||
tree_rotate(t, parent, -sign)
|
||||
if node._balance == 0 {
|
||||
node_adjust_balance_factor(node, -sign)
|
||||
return nil
|
||||
}
|
||||
node_adjust_balance_factor(parent, -sign)
|
||||
node_adjust_balance_factor(node, -sign)
|
||||
} else {
|
||||
node = tree_double_rotate(t, node, parent, -sign)
|
||||
}
|
||||
}
|
||||
|
||||
parent := parent
|
||||
if parent = node._parent; parent != nil {
|
||||
left_deleted^ = node == parent._left
|
||||
}
|
||||
return parent
|
||||
}
|
||||
|
||||
@(private)
|
||||
node_reset :: proc "contextless" (n: ^Node($Value)) {
|
||||
// Mostly pointless as n will be deleted after this is called, but
|
||||
// attempt to be able to catch cases of n not being in the tree.
|
||||
n._parent = n
|
||||
n._left = nil
|
||||
n._right = nil
|
||||
n._balance = 0
|
||||
}
|
||||
|
||||
@(private)
|
||||
node_set_parent_balance :: #force_inline proc "contextless" (
|
||||
n, parent: ^Node($Value),
|
||||
balance: i8,
|
||||
) {
|
||||
n._parent = parent
|
||||
n._balance = balance
|
||||
}
|
||||
|
||||
@(private)
|
||||
node_get_child :: #force_inline proc "contextless" (n: ^Node($Value), sign: i8) -> ^Node(Value) {
|
||||
if sign < 0 {
|
||||
return n._left
|
||||
}
|
||||
return n._right
|
||||
}
|
||||
|
||||
@(private)
|
||||
node_next_or_prev_in_order :: proc "contextless" (
|
||||
n: ^Node($Value),
|
||||
direction: Direction,
|
||||
) -> ^Node(Value) {
|
||||
next, tmp: ^Node(Value)
|
||||
sign := i8(direction)
|
||||
|
||||
if next = node_get_child(n, +sign); next != nil {
|
||||
for {
|
||||
tmp = node_get_child(next, -sign)
|
||||
if tmp == nil {
|
||||
break
|
||||
}
|
||||
next = tmp
|
||||
}
|
||||
} else {
|
||||
tmp, next = n, n._parent
|
||||
for next != nil && tmp == node_get_child(next, +sign) {
|
||||
tmp, next = next, next._parent
|
||||
}
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
@(private)
|
||||
node_set_child :: #force_inline proc "contextless" (
|
||||
n: ^Node($Value),
|
||||
sign: i8,
|
||||
child: ^Node(Value),
|
||||
) {
|
||||
if sign < 0 {
|
||||
n._left = child
|
||||
} else {
|
||||
n._right = child
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
node_adjust_balance_factor :: #force_inline proc "contextless" (n: ^Node($Value), amount: i8) {
|
||||
n._balance += amount
|
||||
}
|
||||
|
||||
@(private)
|
||||
iterator_first :: proc "contextless" (it: ^Iterator($Value)) {
|
||||
// This is private because behavior when the user manually calls
|
||||
// iterator_first followed by iterator_next is unintuitive, since
|
||||
// the first call to iterator_next MUST return the first node
|
||||
// instead of advancing so that `for node in iterator_next(&next)`
|
||||
// works as expected.
|
||||
|
||||
switch it._direction {
|
||||
case .Forward:
|
||||
it._cur = tree_first_or_last_in_order(it._tree, .Backward)
|
||||
case .Backward:
|
||||
it._cur = tree_first_or_last_in_order(it._tree, .Forward)
|
||||
}
|
||||
|
||||
it._next = nil
|
||||
it._called_next = false
|
||||
|
||||
if it._cur != nil {
|
||||
it._next = node_next_or_prev_in_order(it._cur, it._direction)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package dynamic_bit_array
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
import "core:mem"
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package container_intrusive_list
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
// An intrusive doubly-linked list
|
||||
//
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package container_lru
|
||||
|
||||
import "core:runtime"
|
||||
import "core:intrinsics"
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
_ :: runtime
|
||||
_ :: intrinsics
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package container_priority_queue
|
||||
|
||||
import "core:builtin"
|
||||
import "base:builtin"
|
||||
|
||||
Priority_Queue :: struct($T: typeid) {
|
||||
queue: [dynamic]T,
|
||||
@@ -140,3 +140,18 @@ remove :: proc(pq: ^$Q/Priority_Queue($T), i: int) -> (value: T, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
peek_safe :: proc(pq: $Q/Priority_Queue($T), loc := #caller_location) -> (res: T, ok: bool) {
|
||||
if builtin.len(pq.queue) > 0 {
|
||||
return pq.queue[0], true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
peek :: proc(pq: $Q/Priority_Queue($T), loc := #caller_location) -> (res: T) {
|
||||
assert(condition=builtin.len(pq.queue)>0, loc=loc)
|
||||
|
||||
if builtin.len(pq.queue) > 0 {
|
||||
return pq.queue[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package container_queue
|
||||
|
||||
import "core:builtin"
|
||||
import "core:runtime"
|
||||
import "base:builtin"
|
||||
import "base:runtime"
|
||||
_ :: runtime
|
||||
|
||||
// Dynamically resizable double-ended queue/ring-buffer
|
||||
@@ -22,7 +22,9 @@ init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := contex
|
||||
return reserve(q, capacity)
|
||||
}
|
||||
|
||||
// Procedure to initialize a queue from a fixed backing slice
|
||||
// Procedure to initialize a queue from a fixed backing slice.
|
||||
// The contents of the `backing` will be overwritten as items are pushed onto the `Queue`.
|
||||
// Any previous contents are not available.
|
||||
init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
||||
clear(q)
|
||||
q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
|
||||
@@ -34,6 +36,21 @@ init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Procedure to initialize a queue from a fixed backing slice.
|
||||
// Existing contents are preserved and available on the queue.
|
||||
init_with_contents :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
||||
clear(q)
|
||||
q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
|
||||
data = raw_data(backing),
|
||||
len = builtin.len(backing),
|
||||
cap = builtin.len(backing),
|
||||
allocator = {procedure=runtime.nil_allocator_proc, data=nil},
|
||||
}
|
||||
q.len = len(backing)
|
||||
q.offset = len(backing)
|
||||
return true
|
||||
}
|
||||
|
||||
// Procedure to destroy a queue
|
||||
destroy :: proc(q: ^$Q/Queue($T)) {
|
||||
delete(q.data)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package container_small_array
|
||||
|
||||
import "core:builtin"
|
||||
import "core:runtime"
|
||||
import "base:builtin"
|
||||
import "base:runtime"
|
||||
_ :: runtime
|
||||
|
||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
// map type is being used to accelerate lookups.
|
||||
package container_topological_sort
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:runtime"
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
_ :: intrinsics
|
||||
_ :: runtime
|
||||
|
||||
@@ -80,11 +80,13 @@ sort :: proc(sorter: ^$S/Sorter($K)) -> (sorted, cycled: [dynamic]K) {
|
||||
}
|
||||
}
|
||||
|
||||
for root in sorted do for k, _ in relations[root].dependents {
|
||||
relation := &relations[k]
|
||||
relation.dependencies -= 1
|
||||
if relation.dependencies == 0 {
|
||||
append(&sorted, k)
|
||||
for root in sorted {
|
||||
for k, _ in relations[root].dependents {
|
||||
relation := &relations[k]
|
||||
relation.dependencies -= 1
|
||||
if relation.dependencies == 0 {
|
||||
append(&sorted, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-86
@@ -1,95 +1,32 @@
|
||||
# crypto
|
||||
A crypto library for the Odin language
|
||||
|
||||
A cryptography library for the Odin language.
|
||||
|
||||
## Supported
|
||||
This library offers various algorithms implemented in Odin.
|
||||
Please see the chart below for the options.
|
||||
|
||||
## Hashing algorithms
|
||||
| Algorithm | |
|
||||
|:-------------------------------------------------------------------------------------------------------------|:-----------------|
|
||||
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | ✔️ |
|
||||
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
||||
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
||||
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | ✔️ |
|
||||
| [Grøstl](http://www.groestl.info/Groestl.zip) | ✔️ |
|
||||
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | ✔️ |
|
||||
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | ✔️ |
|
||||
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | ✔️ |
|
||||
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | ✔️ |
|
||||
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ |
|
||||
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | ✔️ |
|
||||
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | ✔️ |
|
||||
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | ✔️ |
|
||||
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | ✔️ |
|
||||
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | ✔️ |
|
||||
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ |
|
||||
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ |
|
||||
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | ✔️ |
|
||||
This package offers various algorithms implemented in Odin, along with
|
||||
useful helpers such as access to the system entropy source, and a
|
||||
constant-time byte comparison.
|
||||
|
||||
#### High level API
|
||||
Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_<size>`\*.
|
||||
Included in these groups are six procedures.
|
||||
* `hash_string` - Hash a given string and return the computed hash. Just calls `hash_bytes` internally
|
||||
* `hash_bytes` - Hash a given byte slice and return the computed hash
|
||||
* `hash_string_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. Just calls `hash_bytes_to_buffer` internally
|
||||
* `hash_bytes_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. The destination buffer has to be at least as big as the digest size of the hash
|
||||
* `hash_stream` - Takes a stream from io.Stream and returns the computed hash from it
|
||||
* `hash_file` - Takes a file handle and returns the computed hash from it. A second optional boolean parameter controls if the file is streamed (this is the default) or read at once (set to true)
|
||||
## Implementation considerations
|
||||
|
||||
\* On some algorithms there is another part to the name, since they might offer control about additional parameters.
|
||||
For instance, `HAVAL` offers different sizes as well as three different round amounts.
|
||||
Computing a 256-bit hash with 3 rounds is therefore achieved by calling `haval.hash_256_3(...)`.
|
||||
- The crypto packages are not thread-safe.
|
||||
- Best-effort is make to mitigate timing side-channels on reasonable
|
||||
architectures. Architectures that are known to be unreasonable include
|
||||
but are not limited to i386, i486, and WebAssembly.
|
||||
- Implementations assume a 64-bit architecture (64-bit integer arithmetic
|
||||
is fast, and includes add-with-carry, sub-with-borrow, and full-result
|
||||
multiply).
|
||||
- Hardware sidechannels are explicitly out of scope for this package.
|
||||
Notable examples include but are not limited to:
|
||||
- Power/RF side-channels etc.
|
||||
- Fault injection attacks etc.
|
||||
- Hardware vulnerabilities ("apply mitigations or buy a new CPU").
|
||||
- The packages attempt to santize sensitive data, however this is, and
|
||||
will remain a "best-effort" implementation decision. As Thomas Pornin
|
||||
puts it "In general, such memory cleansing is a fool's quest."
|
||||
- All of these packages have not received independent third party review.
|
||||
|
||||
#### Low level API
|
||||
The above mentioned procedures internally call three procedures: `init`, `update` and `final`.
|
||||
You may also directly call them, if you wish.
|
||||
## License
|
||||
|
||||
#### Example
|
||||
```odin
|
||||
package crypto_example
|
||||
|
||||
// Import the desired package
|
||||
import "core:crypto/md4"
|
||||
|
||||
main :: proc() {
|
||||
input := "foo"
|
||||
|
||||
// Compute the hash, using the high level API
|
||||
computed_hash := md4.hash(input)
|
||||
|
||||
// Variant that takes a destination buffer, instead of returning the computed hash
|
||||
hash := make([]byte, md4.DIGEST_SIZE) // @note: Destination buffer has to be at least as big as the digest size of the hash
|
||||
md4.hash(input, hash[:])
|
||||
|
||||
// Compute the hash, using the low level API
|
||||
ctx: md4.Md4_Context
|
||||
computed_hash_low: [16]byte
|
||||
md4.init(&ctx)
|
||||
md4.update(&ctx, transmute([]byte)input)
|
||||
md4.final(&ctx, computed_hash_low[:])
|
||||
}
|
||||
```
|
||||
For example uses of all available algorithms, please see the tests within `tests/core/crypto`.
|
||||
|
||||
#### Thread safety
|
||||
The crypto package is not thread-safe at the moment. This may change in the future.
|
||||
|
||||
### Disclaimer
|
||||
The algorithms were ported out of curiosity and due to interest in the field.
|
||||
We have not had any of the code verified by a third party or tested/fuzzed by any automatic means.
|
||||
Wherever we were able to find official test vectors, those were used to verify the implementation.
|
||||
We do not recommend using them in a production environment, without any additional testing and/or verification.
|
||||
|
||||
### ToDo
|
||||
* Ciphers (Symmetric, Asymmetric)
|
||||
* MACs (Message Authentication Code)
|
||||
* CSPRNGs (Cryptographically Secure PseudoRandom Number Generator)
|
||||
* KDFs (Key Derivation Function)
|
||||
* KEAs (Key Exchange Algorithm)
|
||||
|
||||
### License
|
||||
This library is made available under the BSD-3 license.
|
||||
+182
-91
@@ -10,12 +10,13 @@ package _blake2
|
||||
Implementation of the BLAKE2 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/rfc7693> and <https://www.blake2.net/>
|
||||
*/
|
||||
|
||||
import "../util"
|
||||
import "core:encoding/endian"
|
||||
import "core:mem"
|
||||
|
||||
BLAKE2S_BLOCK_SIZE :: 64
|
||||
BLAKE2S_SIZE :: 32
|
||||
BLAKE2B_BLOCK_SIZE :: 128
|
||||
BLAKE2B_SIZE :: 64
|
||||
BLAKE2S_BLOCK_SIZE :: 64
|
||||
BLAKE2S_SIZE :: 32
|
||||
BLAKE2B_BLOCK_SIZE :: 128
|
||||
BLAKE2B_SIZE :: 64
|
||||
|
||||
Blake2s_Context :: struct {
|
||||
h: [8]u32,
|
||||
@@ -28,7 +29,8 @@ Blake2s_Context :: struct {
|
||||
is_keyed: bool,
|
||||
size: byte,
|
||||
is_last_node: bool,
|
||||
cfg: Blake2_Config,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
Blake2b_Context :: struct {
|
||||
@@ -42,15 +44,18 @@ Blake2b_Context :: struct {
|
||||
is_keyed: bool,
|
||||
size: byte,
|
||||
is_last_node: bool,
|
||||
cfg: Blake2_Config,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
Blake2_Config :: struct {
|
||||
size: byte,
|
||||
key: []byte,
|
||||
salt: []byte,
|
||||
size: byte,
|
||||
key: []byte,
|
||||
salt: []byte,
|
||||
person: []byte,
|
||||
tree: union{Blake2_Tree},
|
||||
tree: union {
|
||||
Blake2_Tree,
|
||||
},
|
||||
}
|
||||
|
||||
Blake2_Tree :: struct {
|
||||
@@ -63,11 +68,13 @@ Blake2_Tree :: struct {
|
||||
is_last_node: bool,
|
||||
}
|
||||
|
||||
@(private)
|
||||
BLAKE2S_IV := [8]u32 {
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
|
||||
}
|
||||
|
||||
@(private)
|
||||
BLAKE2B_IV := [8]u64 {
|
||||
0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
|
||||
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
||||
@@ -75,80 +82,94 @@ BLAKE2B_IV := [8]u64 {
|
||||
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
|
||||
}
|
||||
|
||||
init :: proc(ctx: ^$T) {
|
||||
init :: proc(ctx: ^$T, cfg: ^Blake2_Config) {
|
||||
when T == Blake2s_Context {
|
||||
block_size :: BLAKE2S_BLOCK_SIZE
|
||||
max_size :: BLAKE2S_SIZE
|
||||
} else when T == Blake2b_Context {
|
||||
block_size :: BLAKE2B_BLOCK_SIZE
|
||||
max_size :: BLAKE2B_SIZE
|
||||
}
|
||||
|
||||
p := make([]byte, block_size)
|
||||
defer delete(p)
|
||||
if cfg.size > max_size {
|
||||
panic("blake2: requested output size exceeeds algorithm max")
|
||||
}
|
||||
|
||||
p[0] = ctx.cfg.size
|
||||
p[1] = byte(len(ctx.cfg.key))
|
||||
// To save having to allocate a scratch buffer, use the internal
|
||||
// data buffer (`ctx.x`), as it is exactly the correct size.
|
||||
p := ctx.x[:]
|
||||
|
||||
if ctx.cfg.salt != nil {
|
||||
p[0] = cfg.size
|
||||
p[1] = byte(len(cfg.key))
|
||||
|
||||
if cfg.salt != nil {
|
||||
when T == Blake2s_Context {
|
||||
copy(p[16:], ctx.cfg.salt)
|
||||
copy(p[16:], cfg.salt)
|
||||
} else when T == Blake2b_Context {
|
||||
copy(p[32:], ctx.cfg.salt)
|
||||
copy(p[32:], cfg.salt)
|
||||
}
|
||||
}
|
||||
if ctx.cfg.person != nil {
|
||||
if cfg.person != nil {
|
||||
when T == Blake2s_Context {
|
||||
copy(p[24:], ctx.cfg.person)
|
||||
copy(p[24:], cfg.person)
|
||||
} else when T == Blake2b_Context {
|
||||
copy(p[48:], ctx.cfg.person)
|
||||
copy(p[48:], cfg.person)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.cfg.tree != nil {
|
||||
p[2] = ctx.cfg.tree.(Blake2_Tree).fanout
|
||||
p[3] = ctx.cfg.tree.(Blake2_Tree).max_depth
|
||||
util.PUT_U32_LE(p[4:], ctx.cfg.tree.(Blake2_Tree).leaf_size)
|
||||
if cfg.tree != nil {
|
||||
p[2] = cfg.tree.(Blake2_Tree).fanout
|
||||
p[3] = cfg.tree.(Blake2_Tree).max_depth
|
||||
endian.unchecked_put_u32le(p[4:], cfg.tree.(Blake2_Tree).leaf_size)
|
||||
when T == Blake2s_Context {
|
||||
p[8] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset)
|
||||
p[9] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 8)
|
||||
p[10] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 16)
|
||||
p[11] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 24)
|
||||
p[12] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 32)
|
||||
p[13] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 40)
|
||||
p[14] = ctx.cfg.tree.(Blake2_Tree).node_depth
|
||||
p[15] = ctx.cfg.tree.(Blake2_Tree).inner_hash_size
|
||||
p[8] = byte(cfg.tree.(Blake2_Tree).node_offset)
|
||||
p[9] = byte(cfg.tree.(Blake2_Tree).node_offset >> 8)
|
||||
p[10] = byte(cfg.tree.(Blake2_Tree).node_offset >> 16)
|
||||
p[11] = byte(cfg.tree.(Blake2_Tree).node_offset >> 24)
|
||||
p[12] = byte(cfg.tree.(Blake2_Tree).node_offset >> 32)
|
||||
p[13] = byte(cfg.tree.(Blake2_Tree).node_offset >> 40)
|
||||
p[14] = cfg.tree.(Blake2_Tree).node_depth
|
||||
p[15] = cfg.tree.(Blake2_Tree).inner_hash_size
|
||||
} else when T == Blake2b_Context {
|
||||
util.PUT_U64_LE(p[8:], ctx.cfg.tree.(Blake2_Tree).node_offset)
|
||||
p[16] = ctx.cfg.tree.(Blake2_Tree).node_depth
|
||||
p[17] = ctx.cfg.tree.(Blake2_Tree).inner_hash_size
|
||||
endian.unchecked_put_u64le(p[8:], cfg.tree.(Blake2_Tree).node_offset)
|
||||
p[16] = cfg.tree.(Blake2_Tree).node_depth
|
||||
p[17] = cfg.tree.(Blake2_Tree).inner_hash_size
|
||||
}
|
||||
} else {
|
||||
p[2], p[3] = 1, 1
|
||||
}
|
||||
ctx.size = ctx.cfg.size
|
||||
ctx.size = cfg.size
|
||||
for i := 0; i < 8; i += 1 {
|
||||
when T == Blake2s_Context {
|
||||
ctx.h[i] = BLAKE2S_IV[i] ~ util.U32_LE(p[i * 4:])
|
||||
ctx.h[i] = BLAKE2S_IV[i] ~ endian.unchecked_get_u32le(p[i * 4:])
|
||||
}
|
||||
when T == Blake2b_Context {
|
||||
ctx.h[i] = BLAKE2B_IV[i] ~ util.U64_LE(p[i * 8:])
|
||||
ctx.h[i] = BLAKE2B_IV[i] ~ endian.unchecked_get_u64le(p[i * 8:])
|
||||
}
|
||||
}
|
||||
if ctx.cfg.tree != nil && ctx.cfg.tree.(Blake2_Tree).is_last_node {
|
||||
|
||||
mem.zero(&ctx.x, size_of(ctx.x)) // Done with the scratch space, no barrier.
|
||||
|
||||
if cfg.tree != nil && cfg.tree.(Blake2_Tree).is_last_node {
|
||||
ctx.is_last_node = true
|
||||
}
|
||||
if len(ctx.cfg.key) > 0 {
|
||||
copy(ctx.padded_key[:], ctx.cfg.key)
|
||||
if len(cfg.key) > 0 {
|
||||
copy(ctx.padded_key[:], cfg.key)
|
||||
update(ctx, ctx.padded_key[:])
|
||||
ctx.is_keyed = true
|
||||
}
|
||||
copy(ctx.ih[:], ctx.h[:])
|
||||
copy(ctx.h[:], ctx.ih[:])
|
||||
copy(ctx.h[:], ctx.ih[:])
|
||||
if ctx.is_keyed {
|
||||
update(ctx, ctx.padded_key[:])
|
||||
}
|
||||
|
||||
ctx.nx = 0
|
||||
|
||||
ctx.is_initialized = true
|
||||
}
|
||||
|
||||
update :: proc "contextless" (ctx: ^$T, p: []byte) {
|
||||
update :: proc(ctx: ^$T, p: []byte) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
p := p
|
||||
when T == Blake2s_Context {
|
||||
block_size :: BLAKE2S_BLOCK_SIZE
|
||||
@@ -174,15 +195,43 @@ update :: proc "contextless" (ctx: ^$T, p: []byte) {
|
||||
ctx.nx += copy(ctx.x[ctx.nx:], p)
|
||||
}
|
||||
|
||||
final :: proc "contextless" (ctx: ^$T, hash: []byte) {
|
||||
when T == Blake2s_Context {
|
||||
blake2s_final(ctx, hash)
|
||||
final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
ctx := ctx
|
||||
if finalize_clone {
|
||||
tmp_ctx: T
|
||||
clone(&tmp_ctx, ctx)
|
||||
ctx = &tmp_ctx
|
||||
}
|
||||
when T == Blake2b_Context {
|
||||
defer(reset(ctx))
|
||||
|
||||
when T == Blake2s_Context {
|
||||
if len(hash) < int(ctx.size) {
|
||||
panic("crypto/blake2s: invalid destination digest size")
|
||||
}
|
||||
blake2s_final(ctx, hash)
|
||||
} else when T == Blake2b_Context {
|
||||
if len(hash) < int(ctx.size) {
|
||||
panic("crypto/blake2b: invalid destination digest size")
|
||||
}
|
||||
blake2b_final(ctx, hash)
|
||||
}
|
||||
}
|
||||
|
||||
clone :: proc(ctx, other: ^$T) {
|
||||
ctx^ = other^
|
||||
}
|
||||
|
||||
reset :: proc(ctx: ^$T) {
|
||||
if !ctx.is_initialized {
|
||||
return
|
||||
}
|
||||
|
||||
mem.zero_explicit(ctx, size_of(ctx^))
|
||||
}
|
||||
|
||||
@(private)
|
||||
blake2s_final :: proc "contextless" (ctx: ^Blake2s_Context, hash: []byte) {
|
||||
if ctx.is_keyed {
|
||||
for i := 0; i < len(ctx.padded_key); i += 1 {
|
||||
@@ -203,16 +252,14 @@ blake2s_final :: proc "contextless" (ctx: ^Blake2s_Context, hash: []byte) {
|
||||
|
||||
blocks(ctx, ctx.x[:])
|
||||
|
||||
j := 0
|
||||
for s, _ in ctx.h[:(ctx.size - 1) / 4 + 1] {
|
||||
hash[j + 0] = byte(s >> 0)
|
||||
hash[j + 1] = byte(s >> 8)
|
||||
hash[j + 2] = byte(s >> 16)
|
||||
hash[j + 3] = byte(s >> 24)
|
||||
j += 4
|
||||
dst: [BLAKE2S_SIZE]byte
|
||||
for i := 0; i < BLAKE2S_SIZE / 4; i += 1 {
|
||||
endian.unchecked_put_u32le(dst[i * 4:], ctx.h[i])
|
||||
}
|
||||
copy(hash, dst[:])
|
||||
}
|
||||
|
||||
@(private)
|
||||
blake2b_final :: proc "contextless" (ctx: ^Blake2b_Context, hash: []byte) {
|
||||
if ctx.is_keyed {
|
||||
for i := 0; i < len(ctx.padded_key); i += 1 {
|
||||
@@ -229,56 +276,52 @@ blake2b_final :: proc "contextless" (ctx: ^Blake2b_Context, hash: []byte) {
|
||||
ctx.f[0] = 0xffffffffffffffff
|
||||
if ctx.is_last_node {
|
||||
ctx.f[1] = 0xffffffffffffffff
|
||||
}
|
||||
}
|
||||
|
||||
blocks(ctx, ctx.x[:])
|
||||
|
||||
j := 0
|
||||
for s, _ in ctx.h[:(ctx.size - 1) / 8 + 1] {
|
||||
hash[j + 0] = byte(s >> 0)
|
||||
hash[j + 1] = byte(s >> 8)
|
||||
hash[j + 2] = byte(s >> 16)
|
||||
hash[j + 3] = byte(s >> 24)
|
||||
hash[j + 4] = byte(s >> 32)
|
||||
hash[j + 5] = byte(s >> 40)
|
||||
hash[j + 6] = byte(s >> 48)
|
||||
hash[j + 7] = byte(s >> 56)
|
||||
j += 8
|
||||
dst: [BLAKE2B_SIZE]byte
|
||||
for i := 0; i < BLAKE2B_SIZE / 8; i += 1 {
|
||||
endian.unchecked_put_u64le(dst[i * 8:], ctx.h[i])
|
||||
}
|
||||
copy(hash, dst[:])
|
||||
}
|
||||
|
||||
@(private)
|
||||
blocks :: proc "contextless" (ctx: ^$T, p: []byte) {
|
||||
when T == Blake2s_Context {
|
||||
blake2s_blocks(ctx, p)
|
||||
}
|
||||
when T == Blake2b_Context {
|
||||
} else when T == Blake2b_Context {
|
||||
blake2b_blocks(ctx, p)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []byte) {
|
||||
h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
|
||||
h0, h1, h2, h3, h4, h5, h6, h7 :=
|
||||
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
|
||||
p := p
|
||||
for len(p) >= BLAKE2S_BLOCK_SIZE {
|
||||
ctx.t[0] += BLAKE2S_BLOCK_SIZE
|
||||
if ctx.t[0] < BLAKE2S_BLOCK_SIZE {
|
||||
ctx.t[1] += 1
|
||||
}
|
||||
}
|
||||
v0, v1, v2, v3, v4, v5, v6, v7 := h0, h1, h2, h3, h4, h5, h6, h7
|
||||
v8 := BLAKE2S_IV[0]
|
||||
v9 := BLAKE2S_IV[1]
|
||||
v8 := BLAKE2S_IV[0]
|
||||
v9 := BLAKE2S_IV[1]
|
||||
v10 := BLAKE2S_IV[2]
|
||||
v11 := BLAKE2S_IV[3]
|
||||
v12 := BLAKE2S_IV[4] ~ ctx.t[0]
|
||||
v13 := BLAKE2S_IV[5] ~ ctx.t[1]
|
||||
v14 := BLAKE2S_IV[6] ~ ctx.f[0]
|
||||
v15 := BLAKE2S_IV[7] ~ ctx.f[1]
|
||||
m: [16]u32
|
||||
j := 0
|
||||
|
||||
m: [16]u32 = ---
|
||||
for i := 0; i < 16; i += 1 {
|
||||
m[i] = u32(p[j]) | u32(p[j + 1]) << 8 | u32(p[j + 2]) << 16 | u32(p[j + 3]) << 24
|
||||
j += 4
|
||||
m[i] = endian.unchecked_get_u32le(p[i * 4:])
|
||||
}
|
||||
|
||||
// Round 1
|
||||
v0 += m[0]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -391,6 +434,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (32 - 7) | v5 >> 7
|
||||
|
||||
// Round 2
|
||||
v0 += m[14]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -503,6 +548,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (32 - 7) | v5 >> 7
|
||||
|
||||
// Round 3
|
||||
v0 += m[11]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -615,6 +662,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (32 - 7) | v5 >> 7
|
||||
|
||||
// Round 4
|
||||
v0 += m[7]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -727,6 +776,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (32 - 7) | v5 >> 7
|
||||
|
||||
// Round 5
|
||||
v0 += m[9]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -839,6 +890,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (32 - 7) | v5 >> 7
|
||||
|
||||
// Round 6
|
||||
v0 += m[2]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -951,6 +1004,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (32 - 7) | v5 >> 7
|
||||
|
||||
// Round 7
|
||||
v0 += m[12]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -1063,6 +1118,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (32 - 7) | v5 >> 7
|
||||
|
||||
// Round 8
|
||||
v0 += m[13]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -1175,6 +1232,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (32 - 7) | v5 >> 7
|
||||
|
||||
// Round 9
|
||||
v0 += m[6]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -1287,6 +1346,8 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (32 - 7) | v5 >> 7
|
||||
|
||||
// Round 10
|
||||
v0 += m[10]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -1399,6 +1460,7 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (32 - 7) | v5 >> 7
|
||||
|
||||
h0 ~= v0 ~ v8
|
||||
h1 ~= v1 ~ v9
|
||||
h2 ~= v2 ~ v10
|
||||
@@ -1407,19 +1469,23 @@ blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []
|
||||
h5 ~= v5 ~ v13
|
||||
h6 ~= v6 ~ v14
|
||||
h7 ~= v7 ~ v15
|
||||
|
||||
p = p[BLAKE2S_BLOCK_SIZE:]
|
||||
}
|
||||
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
|
||||
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] =
|
||||
h0, h1, h2, h3, h4, h5, h6, h7
|
||||
}
|
||||
|
||||
@(private)
|
||||
blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []byte) {
|
||||
h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
|
||||
h0, h1, h2, h3, h4, h5, h6, h7 :=
|
||||
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
|
||||
p := p
|
||||
for len(p) >= BLAKE2B_BLOCK_SIZE {
|
||||
ctx.t[0] += BLAKE2B_BLOCK_SIZE
|
||||
if ctx.t[0] < BLAKE2B_BLOCK_SIZE {
|
||||
ctx.t[1]+=1
|
||||
}
|
||||
ctx.t[1] += 1
|
||||
}
|
||||
v0, v1, v2, v3, v4, v5, v6, v7 := h0, h1, h2, h3, h4, h5, h6, h7
|
||||
v8 := BLAKE2B_IV[0]
|
||||
v9 := BLAKE2B_IV[1]
|
||||
@@ -1429,13 +1495,13 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v13 := BLAKE2B_IV[5] ~ ctx.t[1]
|
||||
v14 := BLAKE2B_IV[6] ~ ctx.f[0]
|
||||
v15 := BLAKE2B_IV[7] ~ ctx.f[1]
|
||||
|
||||
m: [16]u64 = ---
|
||||
j := 0
|
||||
for i := 0; i < 16; i+=1 {
|
||||
m[i] = u64(p[j]) | u64(p[j + 1]) << 8 | u64(p[j + 2]) << 16 | u64(p[j + 3]) << 24 |
|
||||
u64(p[j + 4]) << 32 | u64(p[j + 5]) << 40 | u64(p[j + 6]) << 48 | u64(p[j + 7]) << 56
|
||||
j += 8
|
||||
for i := 0; i < 16; i += 1 {
|
||||
m[i] = endian.unchecked_get_u64le(p[i * 8:])
|
||||
}
|
||||
|
||||
// Round 1
|
||||
v0 += m[0]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -1548,6 +1614,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
// Round 2
|
||||
v0 += m[14]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -1660,6 +1728,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
// Round 3
|
||||
v0 += m[11]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -1772,6 +1842,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
// Round 4
|
||||
v0 += m[7]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -1884,6 +1956,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
// Round 5
|
||||
v0 += m[9]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -1996,6 +2070,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
// Round 6
|
||||
v0 += m[2]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -2108,6 +2184,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
// Round 7
|
||||
v0 += m[12]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -2220,6 +2298,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
// Round 8
|
||||
v0 += m[13]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -2332,6 +2412,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
// Round 9
|
||||
v0 += m[6]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -2444,6 +2526,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
// Round 10
|
||||
v0 += m[10]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -2556,6 +2640,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
// Round 11
|
||||
v0 += m[0]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -2668,6 +2754,8 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
// Round 12
|
||||
v0 += m[14]
|
||||
v0 += v4
|
||||
v12 ~= v0
|
||||
@@ -2780,6 +2868,7 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
v10 += v15
|
||||
v5 ~= v10
|
||||
v5 = v5 << (64 - 63) | v5 >> 63
|
||||
|
||||
h0 ~= v0 ~ v8
|
||||
h1 ~= v1 ~ v9
|
||||
h2 ~= v2 ~ v10
|
||||
@@ -2788,7 +2877,9 @@ blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []
|
||||
h5 ~= v5 ~ v13
|
||||
h6 ~= v6 ~ v14
|
||||
h7 ~= v7 ~ v15
|
||||
|
||||
p = p[BLAKE2B_BLOCK_SIZE:]
|
||||
}
|
||||
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
|
||||
}
|
||||
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] =
|
||||
h0, h1, h2, h3, h4, h5, h6, h7
|
||||
}
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
package _edwards25519
|
||||
|
||||
/*
|
||||
This implements the edwards25519 composite-order group, primarily for
|
||||
the purpose of implementing X25519, Ed25519, and ristretto255. Use of
|
||||
this package for other purposes is NOT RECOMMENDED.
|
||||
|
||||
See:
|
||||
- https://eprint.iacr.org/2011/368.pdf
|
||||
- https://datatracker.ietf.org/doc/html/rfc8032
|
||||
- https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
|
||||
*/
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:crypto"
|
||||
import field "core:crypto/_fiat/field_curve25519"
|
||||
import "core:mem"
|
||||
|
||||
// Group_Element is an edwards25519 group element, as extended homogenous
|
||||
// coordinates, which represents the affine point `(x, y)` as `(X, Y, Z, T)`,
|
||||
// with the relations `x = X/Z`, `y = Y/Z`, and `x * y = T/Z`.
|
||||
//
|
||||
// d = -121665/121666 = 37095705934669439343138083508754565189542113879843219016388785533085940283555
|
||||
// a = -1
|
||||
//
|
||||
// Notes:
|
||||
// - There is considerable scope for optimization, however that
|
||||
// will not change the external API, and this is simple and reasonably
|
||||
// performant.
|
||||
// - The API delibarately makes it hard to create arbitrary group
|
||||
// elements that are not on the curve.
|
||||
// - The group element decoding routine takes the opinionated stance of
|
||||
// rejecting non-canonical encodings.
|
||||
|
||||
FE_D := field.Tight_Field_Element {
|
||||
929955233495203,
|
||||
466365720129213,
|
||||
1662059464998953,
|
||||
2033849074728123,
|
||||
1442794654840575,
|
||||
}
|
||||
@(private)
|
||||
FE_A := field.Tight_Field_Element {
|
||||
2251799813685228,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
}
|
||||
@(private)
|
||||
FE_D2 := field.Tight_Field_Element {
|
||||
1859910466990425,
|
||||
932731440258426,
|
||||
1072319116312658,
|
||||
1815898335770999,
|
||||
633789495995903,
|
||||
}
|
||||
@(private)
|
||||
GE_BASEPOINT := Group_Element {
|
||||
field.Tight_Field_Element {
|
||||
1738742601995546,
|
||||
1146398526822698,
|
||||
2070867633025821,
|
||||
562264141797630,
|
||||
587772402128613,
|
||||
},
|
||||
field.Tight_Field_Element {
|
||||
1801439850948184,
|
||||
1351079888211148,
|
||||
450359962737049,
|
||||
900719925474099,
|
||||
1801439850948198,
|
||||
},
|
||||
field.Tight_Field_Element{1, 0, 0, 0, 0},
|
||||
field.Tight_Field_Element {
|
||||
1841354044333475,
|
||||
16398895984059,
|
||||
755974180946558,
|
||||
900171276175154,
|
||||
1821297809914039,
|
||||
},
|
||||
}
|
||||
GE_IDENTITY := Group_Element {
|
||||
field.Tight_Field_Element{0, 0, 0, 0, 0},
|
||||
field.Tight_Field_Element{1, 0, 0, 0, 0},
|
||||
field.Tight_Field_Element{1, 0, 0, 0, 0},
|
||||
field.Tight_Field_Element{0, 0, 0, 0, 0},
|
||||
}
|
||||
|
||||
Group_Element :: struct {
|
||||
x: field.Tight_Field_Element,
|
||||
y: field.Tight_Field_Element,
|
||||
z: field.Tight_Field_Element,
|
||||
t: field.Tight_Field_Element,
|
||||
}
|
||||
|
||||
ge_clear :: proc "contextless" (ge: ^Group_Element) {
|
||||
mem.zero_explicit(ge, size_of(Group_Element))
|
||||
}
|
||||
|
||||
ge_set :: proc "contextless" (ge, a: ^Group_Element) {
|
||||
field.fe_set(&ge.x, &a.x)
|
||||
field.fe_set(&ge.y, &a.y)
|
||||
field.fe_set(&ge.z, &a.z)
|
||||
field.fe_set(&ge.t, &a.t)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
|
||||
if len(b) != 32 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
b_ := transmute(^[32]byte)(raw_data(b))
|
||||
|
||||
// Do the work in a scratch element, so that ge is unchanged on
|
||||
// failure.
|
||||
tmp: Group_Element = ---
|
||||
defer ge_clear(&tmp)
|
||||
field.fe_one(&tmp.z) // Z = 1
|
||||
|
||||
// The encoding is the y-coordinate, with the x-coordinate polarity
|
||||
// (odd/even) encoded in the MSB.
|
||||
field.fe_from_bytes(&tmp.y, b_) // ignores high bit
|
||||
|
||||
// Recover the candidate x-coordinate via the curve equation:
|
||||
// x^2 = (y^2 - 1) / (d * y^2 + 1) (mod p)
|
||||
|
||||
fe_tmp := &tmp.t // Use this to store intermediaries.
|
||||
fe_one := &tmp.z
|
||||
|
||||
// x = num = y^2 - 1
|
||||
field.fe_carry_square(fe_tmp, field.fe_relax_cast(&tmp.y)) // fe_tmp = y^2
|
||||
field.fe_carry_sub(&tmp.x, fe_tmp, fe_one)
|
||||
|
||||
// den = d * y^2 + 1
|
||||
field.fe_carry_mul(fe_tmp, field.fe_relax_cast(fe_tmp), field.fe_relax_cast(&FE_D))
|
||||
field.fe_carry_add(fe_tmp, fe_tmp, fe_one)
|
||||
|
||||
// x = invsqrt(den/num)
|
||||
is_square := field.fe_carry_sqrt_ratio_m1(
|
||||
&tmp.x,
|
||||
field.fe_relax_cast(&tmp.x),
|
||||
field.fe_relax_cast(fe_tmp),
|
||||
)
|
||||
if is_square == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Pick the right x-coordinate.
|
||||
field.fe_cond_negate(&tmp.x, &tmp.x, int(b[31] >> 7))
|
||||
|
||||
// t = x * y
|
||||
field.fe_carry_mul(&tmp.t, field.fe_relax_cast(&tmp.x), field.fe_relax_cast(&tmp.y))
|
||||
|
||||
// Reject non-canonical encodings of ge.
|
||||
buf: [32]byte = ---
|
||||
field.fe_to_bytes(&buf, &tmp.y)
|
||||
buf[31] |= byte(field.fe_is_negative(&tmp.x)) << 7
|
||||
is_canonical := crypto.compare_constant_time(b, buf[:])
|
||||
|
||||
ge_cond_assign(ge, &tmp, is_canonical)
|
||||
|
||||
mem.zero_explicit(&buf, size_of(buf))
|
||||
|
||||
return is_canonical == 1
|
||||
}
|
||||
|
||||
ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
|
||||
if len(dst) != 32 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
dst_ := transmute(^[32]byte)(raw_data(dst))
|
||||
|
||||
// Convert the element to affine (x, y) representation.
|
||||
x, y, z_inv: field.Tight_Field_Element = ---, ---, ---
|
||||
field.fe_carry_inv(&z_inv, field.fe_relax_cast(&ge.z))
|
||||
field.fe_carry_mul(&x, field.fe_relax_cast(&ge.x), field.fe_relax_cast(&z_inv))
|
||||
field.fe_carry_mul(&y, field.fe_relax_cast(&ge.y), field.fe_relax_cast(&z_inv))
|
||||
|
||||
// Encode the y-coordinate.
|
||||
field.fe_to_bytes(dst_, &y)
|
||||
|
||||
// Copy the least significant bit of the x-coordinate to the most
|
||||
// significant bit of the encoded y-coordinate.
|
||||
dst_[31] |= byte((x[0] & 1) << 7)
|
||||
|
||||
field.fe_clear_vec([]^field.Tight_Field_Element{&x, &y, &z_inv})
|
||||
}
|
||||
|
||||
ge_identity :: proc "contextless" (ge: ^Group_Element) {
|
||||
field.fe_zero(&ge.x)
|
||||
field.fe_one(&ge.y)
|
||||
field.fe_one(&ge.z)
|
||||
field.fe_zero(&ge.t)
|
||||
}
|
||||
|
||||
ge_generator :: proc "contextless" (ge: ^Group_Element) {
|
||||
ge_set(ge, &GE_BASEPOINT)
|
||||
}
|
||||
|
||||
@(private)
|
||||
Addend_Group_Element :: struct {
|
||||
y2_minus_x2: field.Loose_Field_Element, // t1
|
||||
y2_plus_x2: field.Loose_Field_Element, // t3
|
||||
k_times_t2: field.Tight_Field_Element, // t4
|
||||
two_times_z2: field.Loose_Field_Element, // t5
|
||||
}
|
||||
|
||||
@(private)
|
||||
ge_addend_set :: proc "contextless" (ge_a: ^Addend_Group_Element, ge: ^Group_Element) {
|
||||
field.fe_sub(&ge_a.y2_minus_x2, &ge.y, &ge.x)
|
||||
field.fe_add(&ge_a.y2_plus_x2, &ge.y, &ge.x)
|
||||
field.fe_carry_mul(&ge_a.k_times_t2, field.fe_relax_cast(&FE_D2), field.fe_relax_cast(&ge.t))
|
||||
field.fe_add(&ge_a.two_times_z2, &ge.z, &ge.z)
|
||||
}
|
||||
|
||||
@(private)
|
||||
ge_addend_conditional_assign :: proc "contextless" (ge_a, a: ^Addend_Group_Element, ctrl: int) {
|
||||
field.fe_cond_select(&ge_a.y2_minus_x2, &ge_a.y2_minus_x2, &a.y2_minus_x2, ctrl)
|
||||
field.fe_cond_select(&ge_a.y2_plus_x2, &ge_a.y2_plus_x2, &a.y2_plus_x2, ctrl)
|
||||
field.fe_cond_select(&ge_a.k_times_t2, &ge_a.k_times_t2, &a.k_times_t2, ctrl)
|
||||
field.fe_cond_select(&ge_a.two_times_z2, &ge_a.two_times_z2, &a.two_times_z2, ctrl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
Add_Scratch :: struct {
|
||||
A, B, C, D: field.Tight_Field_Element,
|
||||
E, F, G, H: field.Loose_Field_Element,
|
||||
t0, t2: field.Loose_Field_Element,
|
||||
}
|
||||
|
||||
ge_add :: proc "contextless" (ge, a, b: ^Group_Element) {
|
||||
b_: Addend_Group_Element = ---
|
||||
ge_addend_set(&b_, b)
|
||||
|
||||
scratch: Add_Scratch = ---
|
||||
ge_add_addend(ge, a, &b_, &scratch)
|
||||
|
||||
mem.zero_explicit(&b_, size_of(Addend_Group_Element))
|
||||
mem.zero_explicit(&scratch, size_of(Add_Scratch))
|
||||
}
|
||||
|
||||
@(private)
|
||||
ge_add_addend :: proc "contextless" (
|
||||
ge, a: ^Group_Element,
|
||||
b: ^Addend_Group_Element,
|
||||
scratch: ^Add_Scratch,
|
||||
) {
|
||||
// https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
|
||||
// Assumptions: k=2*d.
|
||||
//
|
||||
// t0 = Y1-X1
|
||||
// t1 = Y2-X2
|
||||
// A = t0*t1
|
||||
// t2 = Y1+X1
|
||||
// t3 = Y2+X2
|
||||
// B = t2*t3
|
||||
// t4 = k*T2
|
||||
// C = T1*t4
|
||||
// t5 = 2*Z2
|
||||
// D = Z1*t5
|
||||
// E = B-A
|
||||
// F = D-C
|
||||
// G = D+C
|
||||
// H = B+A
|
||||
// X3 = E*F
|
||||
// Y3 = G*H
|
||||
// T3 = E*H
|
||||
// Z3 = F*G
|
||||
//
|
||||
// In order to make the scalar multiply faster, the addend is provided
|
||||
// as a `Addend_Group_Element` with t1, t3, t4, and t5 precomputed, as
|
||||
// it is trivially obvious that those are the only values used by the
|
||||
// formula that are directly dependent on `b`, and are only dependent
|
||||
// on `b` and constants. This saves 1 sub, 2 adds, and 1 multiply,
|
||||
// each time the intermediate representation can be reused.
|
||||
|
||||
A, B, C, D := &scratch.A, &scratch.B, &scratch.C, &scratch.D
|
||||
E, F, G, H := &scratch.E, &scratch.F, &scratch.G, &scratch.H
|
||||
t0, t2 := &scratch.t0, &scratch.t2
|
||||
|
||||
field.fe_sub(t0, &a.y, &a.x)
|
||||
t1 := &b.y2_minus_x2
|
||||
field.fe_carry_mul(A, t0, t1)
|
||||
field.fe_add(t2, &a.y, &a.x)
|
||||
t3 := &b.y2_plus_x2
|
||||
field.fe_carry_mul(B, t2, t3)
|
||||
t4 := &b.k_times_t2
|
||||
field.fe_carry_mul(C, field.fe_relax_cast(&a.t), field.fe_relax_cast(t4))
|
||||
t5 := &b.two_times_z2
|
||||
field.fe_carry_mul(D, field.fe_relax_cast(&a.z), t5)
|
||||
field.fe_sub(E, B, A)
|
||||
field.fe_sub(F, D, C)
|
||||
field.fe_add(G, D, C)
|
||||
field.fe_add(H, B, A)
|
||||
field.fe_carry_mul(&ge.x, E, F)
|
||||
field.fe_carry_mul(&ge.y, G, H)
|
||||
field.fe_carry_mul(&ge.t, E, H)
|
||||
field.fe_carry_mul(&ge.z, F, G)
|
||||
}
|
||||
|
||||
@(private)
|
||||
Double_Scratch :: struct {
|
||||
A, B, C, D, G: field.Tight_Field_Element,
|
||||
t0, t2, t3: field.Tight_Field_Element,
|
||||
E, F, H: field.Loose_Field_Element,
|
||||
t1: field.Loose_Field_Element,
|
||||
}
|
||||
|
||||
ge_double :: proc "contextless" (ge, a: ^Group_Element, scratch: ^Double_Scratch = nil) {
|
||||
// https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd
|
||||
//
|
||||
// A = X1^2
|
||||
// B = Y1^2
|
||||
// t0 = Z1^2
|
||||
// C = 2*t0
|
||||
// D = a*A
|
||||
// t1 = X1+Y1
|
||||
// t2 = t1^2
|
||||
// t3 = t2-A
|
||||
// E = t3-B
|
||||
// G = D+B
|
||||
// F = G-C
|
||||
// H = D-B
|
||||
// X3 = E*F
|
||||
// Y3 = G*H
|
||||
// T3 = E*H
|
||||
// Z3 = F*G
|
||||
|
||||
sanitize, scratch := scratch == nil, scratch
|
||||
if sanitize {
|
||||
tmp: Double_Scratch = ---
|
||||
scratch = &tmp
|
||||
}
|
||||
|
||||
A, B, C, D, G := &scratch.A, &scratch.B, &scratch.C, &scratch.D, &scratch.G
|
||||
t0, t2, t3 := &scratch.t0, &scratch.t2, &scratch.t3
|
||||
E, F, H := &scratch.E, &scratch.F, &scratch.H
|
||||
t1 := &scratch.t1
|
||||
|
||||
field.fe_carry_square(A, field.fe_relax_cast(&a.x))
|
||||
field.fe_carry_square(B, field.fe_relax_cast(&a.y))
|
||||
field.fe_carry_square(t0, field.fe_relax_cast(&a.z))
|
||||
field.fe_carry_add(C, t0, t0)
|
||||
field.fe_carry_mul(D, field.fe_relax_cast(&FE_A), field.fe_relax_cast(A))
|
||||
field.fe_add(t1, &a.x, &a.y)
|
||||
field.fe_carry_square(t2, t1)
|
||||
field.fe_carry_sub(t3, t2, A)
|
||||
field.fe_sub(E, t3, B)
|
||||
field.fe_carry_add(G, D, B)
|
||||
field.fe_sub(F, G, C)
|
||||
field.fe_sub(H, D, B)
|
||||
G_ := field.fe_relax_cast(G)
|
||||
field.fe_carry_mul(&ge.x, E, F)
|
||||
field.fe_carry_mul(&ge.y, G_, H)
|
||||
field.fe_carry_mul(&ge.t, E, H)
|
||||
field.fe_carry_mul(&ge.z, F, G_)
|
||||
|
||||
if sanitize {
|
||||
mem.zero_explicit(scratch, size_of(Double_Scratch))
|
||||
}
|
||||
}
|
||||
|
||||
ge_negate :: proc "contextless" (ge, a: ^Group_Element) {
|
||||
field.fe_carry_opp(&ge.x, &a.x)
|
||||
field.fe_set(&ge.y, &a.y)
|
||||
field.fe_set(&ge.z, &a.z)
|
||||
field.fe_carry_opp(&ge.t, &a.t)
|
||||
}
|
||||
|
||||
ge_cond_negate :: proc "contextless" (ge, a: ^Group_Element, ctrl: int) {
|
||||
tmp: Group_Element = ---
|
||||
ge_negate(&tmp, a)
|
||||
ge_cond_assign(ge, &tmp, ctrl)
|
||||
|
||||
ge_clear(&tmp)
|
||||
}
|
||||
|
||||
ge_cond_assign :: proc "contextless" (ge, a: ^Group_Element, ctrl: int) {
|
||||
field.fe_cond_assign(&ge.x, &a.x, ctrl)
|
||||
field.fe_cond_assign(&ge.y, &a.y, ctrl)
|
||||
field.fe_cond_assign(&ge.z, &a.z, ctrl)
|
||||
field.fe_cond_assign(&ge.t, &a.t, ctrl)
|
||||
}
|
||||
|
||||
ge_cond_select :: proc "contextless" (ge, a, b: ^Group_Element, ctrl: int) {
|
||||
field.fe_cond_select(&ge.x, &a.x, &b.x, ctrl)
|
||||
field.fe_cond_select(&ge.y, &a.y, &b.y, ctrl)
|
||||
field.fe_cond_select(&ge.z, &a.z, &b.z, ctrl)
|
||||
field.fe_cond_select(&ge.t, &a.t, &b.t, ctrl)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
ge_equal :: proc "contextless" (a, b: ^Group_Element) -> int {
|
||||
// (x, y) ?= (x', y') -> (X/Z, Y/Z) ?= (X'/Z', Y'/Z')
|
||||
// X/Z ?= X'/Z', Y/Z ?= Y'/Z' -> X*Z' ?= X'*Z, Y*Z' ?= Y'*Z
|
||||
ax_bz, bx_az, ay_bz, by_az: field.Tight_Field_Element = ---, ---, ---, ---
|
||||
field.fe_carry_mul(&ax_bz, field.fe_relax_cast(&a.x), field.fe_relax_cast(&b.z))
|
||||
field.fe_carry_mul(&bx_az, field.fe_relax_cast(&b.x), field.fe_relax_cast(&a.z))
|
||||
field.fe_carry_mul(&ay_bz, field.fe_relax_cast(&a.y), field.fe_relax_cast(&b.z))
|
||||
field.fe_carry_mul(&by_az, field.fe_relax_cast(&b.y), field.fe_relax_cast(&a.z))
|
||||
|
||||
ret := field.fe_equal(&ax_bz, &bx_az) & field.fe_equal(&ay_bz, &by_az)
|
||||
|
||||
field.fe_clear_vec([]^field.Tight_Field_Element{&ax_bz, &ay_bz, &bx_az, &by_az})
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
ge_is_small_order :: proc "contextless" (ge: ^Group_Element) -> bool {
|
||||
tmp: Group_Element = ---
|
||||
ge_double(&tmp, ge)
|
||||
ge_double(&tmp, &tmp)
|
||||
ge_double(&tmp, &tmp)
|
||||
return ge_equal(&tmp, &GE_IDENTITY) == 1
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
ge_in_prime_order_subgroup_vartime :: proc "contextless" (ge: ^Group_Element) -> bool {
|
||||
// This is currently *very* expensive. The faster method would be
|
||||
// something like (https://eprint.iacr.org/2022/1164.pdf), however
|
||||
// that is a ~50% speedup, and a lot of added complexity for something
|
||||
// that is better solved by "just use ristretto255".
|
||||
tmp: Group_Element = ---
|
||||
_ge_scalarmult(&tmp, ge, &SC_ELL, true)
|
||||
return ge_equal(&tmp, &GE_IDENTITY) == 1
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package _edwards25519
|
||||
|
||||
import "base:intrinsics"
|
||||
import field "core:crypto/_fiat/field_scalar25519"
|
||||
import "core:mem"
|
||||
|
||||
Scalar :: field.Montgomery_Domain_Field_Element
|
||||
|
||||
// WARNING: This is non-canonical and only to be used when checking if
|
||||
// a group element is on the prime-order subgroup.
|
||||
@(private)
|
||||
SC_ELL := field.Non_Montgomery_Domain_Field_Element {
|
||||
field.ELL[0],
|
||||
field.ELL[1],
|
||||
field.ELL[2],
|
||||
field.ELL[3],
|
||||
}
|
||||
|
||||
sc_set_u64 :: proc "contextless" (sc: ^Scalar, i: u64) {
|
||||
tmp := field.Non_Montgomery_Domain_Field_Element{i, 0, 0, 0}
|
||||
field.fe_to_montgomery(sc, &tmp)
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
sc_set_bytes :: proc "contextless" (sc: ^Scalar, b: []byte) -> bool {
|
||||
if len(b) != 32 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
b_ := transmute(^[32]byte)(raw_data(b))
|
||||
return field.fe_from_bytes(sc, b_)
|
||||
}
|
||||
|
||||
sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
|
||||
if len(b) != 32 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
b_ := transmute(^[32]byte)(raw_data(b))
|
||||
field.fe_from_bytes_rfc8032(sc, b_)
|
||||
}
|
||||
|
||||
sc_clear :: proc "contextless" (sc: ^Scalar) {
|
||||
mem.zero_explicit(sc, size_of(Scalar))
|
||||
}
|
||||
|
||||
sc_set :: field.fe_set
|
||||
sc_set_bytes_wide :: field.fe_from_bytes_wide
|
||||
sc_bytes :: field.fe_to_bytes
|
||||
|
||||
sc_zero :: field.fe_zero
|
||||
sc_one :: field.fe_one
|
||||
|
||||
sc_add :: field.fe_add
|
||||
sc_sub :: field.fe_sub
|
||||
sc_negate :: field.fe_opp
|
||||
sc_mul :: field.fe_mul
|
||||
sc_square :: field.fe_square
|
||||
|
||||
sc_cond_assign :: field.fe_cond_assign
|
||||
sc_equal :: field.fe_equal
|
||||
@@ -0,0 +1,288 @@
|
||||
package _edwards25519
|
||||
|
||||
import field "core:crypto/_fiat/field_scalar25519"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
// GE_BASEPOINT_TABLE is 1 * G, ... 15 * G, in precomputed format.
|
||||
//
|
||||
// Note: When generating, the values were reduced to Tight_Field_Element
|
||||
// ranges, even though that is not required.
|
||||
@(private)
|
||||
GE_BASEPOINT_TABLE := Multiply_Table {
|
||||
{
|
||||
{62697248952638, 204681361388450, 631292143396476, 338455783676468, 1213667448819585},
|
||||
{1288382639258501, 245678601348599, 269427782077623, 1462984067271730, 137412439391563},
|
||||
{301289933810280, 1259582250014073, 1422107436869536, 796239922652654, 1953934009299142},
|
||||
{2, 0, 0, 0, 0},
|
||||
},
|
||||
{
|
||||
{1519297034332653, 1098796920435767, 1823476547744119, 808144629470969, 2110930855619772},
|
||||
{338005982828284, 1667856962156925, 100399270107451, 1604566703601691, 1950338038771369},
|
||||
{1920505767731247, 1443759578976892, 1659852098357048, 1484431291070208, 275018744912646},
|
||||
{763163817085987, 2195095074806923, 2167883174351839, 1868059999999762, 911071066608705},
|
||||
},
|
||||
{
|
||||
{960627541894068, 1314966688943942, 1126875971034044, 2059608312958945, 605975666152586},
|
||||
{1714478358025626, 2209607666607510, 1600912834284834, 496072478982142, 481970031861896},
|
||||
{851735079403194, 1088965826757164, 141569479297499, 602804610059257, 2004026468601520},
|
||||
{197585529552380, 324719066578543, 564481854250498, 1173818332764578, 35452976395676},
|
||||
},
|
||||
{
|
||||
{1152980410747203, 2196804280851952, 25745194962557, 1915167295473129, 1266299690309224},
|
||||
{809905889679060, 979732230071345, 1509972345538142, 188492426534402, 818965583123815},
|
||||
{997685409185036, 1451818320876327, 2126681166774509, 2000509606057528, 235432372486854},
|
||||
{887734189279642, 1460338685162044, 877378220074262, 102436391401299, 153369156847490},
|
||||
},
|
||||
{
|
||||
{2056621900836770, 1821657694132497, 1627986892909426, 1163363868678833, 1108873376459226},
|
||||
{1187697490593623, 1066539945237335, 885654531892000, 1357534489491782, 359370291392448},
|
||||
{1509033452137525, 1305318174298508, 613642471748944, 1987256352550234, 1044283663101541},
|
||||
{220105720697037, 387661783287620, 328296827867762, 360035589590664, 795213236824054},
|
||||
},
|
||||
{
|
||||
{1820794733038396, 1612235121681074, 757405923441402, 1094031020892801, 231025333128907},
|
||||
{1639067873254194, 1484176557946322, 300800382144789, 1329915446659183, 1211704578730455},
|
||||
{641900794791527, 1711751746971612, 179044712319955, 576455585963824, 1852617592509865},
|
||||
{743549047192397, 685091042550147, 1952415336873496, 1965124675654685, 513364998442917},
|
||||
},
|
||||
{
|
||||
{1004557076870448, 1762911374844520, 1330807633622723, 384072910939787, 953849032243810},
|
||||
{2178275058221458, 257933183722891, 376684351537894, 2010189102001786, 1981824297484148},
|
||||
{1332915663881114, 1286540505502549, 1741691283561518, 977214932156314, 1764059494778091},
|
||||
{429702949064027, 1368332611650677, 2019867176450999, 2212258376161746, 526160996742554},
|
||||
},
|
||||
{
|
||||
{2098932988258576, 2203688382075948, 2120400160059479, 1748488020948146, 1203264167282624},
|
||||
{677131386735829, 1850249298025188, 672782146532031, 2144145693078904, 2088656272813787},
|
||||
{1065622343976192, 1573853211848116, 223560413590068, 333846833073379, 27832122205830},
|
||||
{1781008836504573, 917619542051793, 544322748939913, 882577394308384, 1720521246471195},
|
||||
},
|
||||
{
|
||||
{660120928379860, 2081944024858618, 1878411111349191, 424587356517195, 2111317439894005},
|
||||
{1834193977811532, 1864164086863319, 797334633289424, 150410812403062, 2085177078466389},
|
||||
{1438117271371866, 783915531014482, 388731514584658, 292113935417795, 1945855002546714},
|
||||
{1678140823166658, 679103239148744, 614102761596238, 1052962498997885, 1863983323810390},
|
||||
},
|
||||
{
|
||||
{1690309392496233, 1116333140326275, 1377242323631039, 717196888780674, 82724646713353},
|
||||
{1722370213432106, 74265192976253, 264239578448472, 1714909985012994, 2216984958602173},
|
||||
{2010482366920922, 1294036471886319, 566466395005815, 1631955803657320, 1751698647538458},
|
||||
{1073230604155753, 1159087041338551, 1664057985455483, 127472702826203, 1339591128522371},
|
||||
},
|
||||
{
|
||||
{478053307175577, 2179515791720985, 21146535423512, 1831683844029536, 462805561553981},
|
||||
{1945267486565588, 1298536818409655, 2214511796262989, 1904981051429012, 252904800782086},
|
||||
{268945954671210, 222740425595395, 1208025911856230, 1080418823003555, 75929831922483},
|
||||
{1884784014268948, 643868448202966, 978736549726821, 46385971089796, 1296884812292320},
|
||||
},
|
||||
{
|
||||
{1861159462859103, 7077532564710, 963010365896826, 1938780006785270, 766241051941647},
|
||||
{1778966986051906, 1713995999765361, 1394565822271816, 1366699246468722, 1213407027149475},
|
||||
{1978989286560907, 2135084162045594, 1951565508865477, 671788336314416, 293123929458176},
|
||||
{902608944504080, 2167765718046481, 1285718473078022, 1222562171329269, 492109027844479},
|
||||
},
|
||||
{
|
||||
{1820807832746213, 1029220580458586, 1101997555432203, 1039081975563572, 202477981158221},
|
||||
{1866134980680205, 2222325502763386, 1830284629571201, 1046966214478970, 418381946936795},
|
||||
{1783460633291322, 1719505443254998, 1810489639976220, 877049370713018, 2187801198742619},
|
||||
{197118243000763, 305493867565736, 518814410156522, 1656246186645170, 901894734874934},
|
||||
},
|
||||
{
|
||||
{225454942125915, 478410476654509, 600524586037746, 643450007230715, 1018615928259319},
|
||||
{1733330584845708, 881092297970296, 507039890129464, 496397090721598, 2230888519577628},
|
||||
{690155664737246, 1010454785646677, 753170144375012, 1651277613844874, 1622648796364156},
|
||||
{1321310321891618, 1089655277873603, 235891750867089, 815878279563688, 1709264240047556},
|
||||
},
|
||||
{
|
||||
{805027036551342, 1387174275567452, 1156538511461704, 1465897486692171, 1208567094120903},
|
||||
{2228417017817483, 202885584970535, 2182114782271881, 2077405042592934, 1029684358182774},
|
||||
{460447547653983, 627817697755692, 524899434670834, 1228019344939427, 740684787777653},
|
||||
{849757462467675, 447476306919899, 422618957298818, 302134659227815, 675831828440895},
|
||||
},
|
||||
}
|
||||
|
||||
ge_scalarmult :: proc "contextless" (ge, p: ^Group_Element, sc: ^Scalar) {
|
||||
tmp: field.Non_Montgomery_Domain_Field_Element
|
||||
field.fe_from_montgomery(&tmp, sc)
|
||||
|
||||
_ge_scalarmult(ge, p, &tmp)
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
ge_scalarmult_basepoint :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar) {
|
||||
// Something like the comb method from "Fast and compact elliptic-curve
|
||||
// cryptography" Section 3.3, would be more performant, but more
|
||||
// complex.
|
||||
//
|
||||
// - https://eprint.iacr.org/2012/309
|
||||
ge_scalarmult(ge, &GE_BASEPOINT, sc)
|
||||
}
|
||||
|
||||
ge_scalarmult_vartime :: proc "contextless" (ge, p: ^Group_Element, sc: ^Scalar) {
|
||||
tmp: field.Non_Montgomery_Domain_Field_Element
|
||||
field.fe_from_montgomery(&tmp, sc)
|
||||
|
||||
_ge_scalarmult(ge, p, &tmp, true)
|
||||
}
|
||||
|
||||
ge_double_scalarmult_basepoint_vartime :: proc "contextless" (
|
||||
ge: ^Group_Element,
|
||||
a: ^Scalar,
|
||||
A: ^Group_Element,
|
||||
b: ^Scalar,
|
||||
) {
|
||||
// Strauss-Shamir, commonly referred to as the "Shamir trick",
|
||||
// saves half the doublings, relative to doing this the naive way.
|
||||
//
|
||||
// ABGLSV-Pornin (https://eprint.iacr.org/2020/454) is faster,
|
||||
// but significantly more complex, and has incompatibilities with
|
||||
// mixed-order group elements.
|
||||
|
||||
tmp_add: Add_Scratch = ---
|
||||
tmp_addend: Addend_Group_Element = ---
|
||||
tmp_dbl: Double_Scratch = ---
|
||||
tmp: Group_Element = ---
|
||||
|
||||
A_tbl: Multiply_Table = ---
|
||||
mul_tbl_set(&A_tbl, A, &tmp_add)
|
||||
|
||||
sc_a, sc_b: field.Non_Montgomery_Domain_Field_Element
|
||||
field.fe_from_montgomery(&sc_a, a)
|
||||
field.fe_from_montgomery(&sc_b, b)
|
||||
|
||||
ge_identity(&tmp)
|
||||
for i := 31; i >= 0; i = i - 1 {
|
||||
limb := i / 8
|
||||
shift := uint(i & 7) * 8
|
||||
|
||||
limb_byte_a := sc_a[limb] >> shift
|
||||
limb_byte_b := sc_b[limb] >> shift
|
||||
|
||||
hi_a, lo_a := (limb_byte_a >> 4) & 0x0f, limb_byte_a & 0x0f
|
||||
hi_b, lo_b := (limb_byte_b >> 4) & 0x0f, limb_byte_b & 0x0f
|
||||
|
||||
if i != 31 {
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
}
|
||||
mul_tbl_add(&tmp, &A_tbl, hi_a, &tmp_add, &tmp_addend, true)
|
||||
mul_tbl_add(&tmp, &GE_BASEPOINT_TABLE, hi_b, &tmp_add, &tmp_addend, true)
|
||||
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
mul_tbl_add(&tmp, &A_tbl, lo_a, &tmp_add, &tmp_addend, true)
|
||||
mul_tbl_add(&tmp, &GE_BASEPOINT_TABLE, lo_b, &tmp_add, &tmp_addend, true)
|
||||
}
|
||||
|
||||
ge_set(ge, &tmp)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_ge_scalarmult :: proc "contextless" (
|
||||
ge, p: ^Group_Element,
|
||||
sc: ^field.Non_Montgomery_Domain_Field_Element,
|
||||
unsafe_is_vartime := false,
|
||||
) {
|
||||
// Do the simplest possible thing that works and provides adequate,
|
||||
// performance, which is windowed add-then-multiply.
|
||||
|
||||
tmp_add: Add_Scratch = ---
|
||||
tmp_addend: Addend_Group_Element = ---
|
||||
tmp_dbl: Double_Scratch = ---
|
||||
tmp: Group_Element = ---
|
||||
|
||||
p_tbl: Multiply_Table = ---
|
||||
mul_tbl_set(&p_tbl, p, &tmp_add)
|
||||
|
||||
ge_identity(&tmp)
|
||||
for i := 31; i >= 0; i = i - 1 {
|
||||
limb := i / 8
|
||||
shift := uint(i & 7) * 8
|
||||
limb_byte := sc[limb] >> shift
|
||||
|
||||
hi, lo := (limb_byte >> 4) & 0x0f, limb_byte & 0x0f
|
||||
|
||||
if i != 31 {
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
}
|
||||
mul_tbl_add(&tmp, &p_tbl, hi, &tmp_add, &tmp_addend, unsafe_is_vartime)
|
||||
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
ge_double(&tmp, &tmp, &tmp_dbl)
|
||||
mul_tbl_add(&tmp, &p_tbl, lo, &tmp_add, &tmp_addend, unsafe_is_vartime)
|
||||
}
|
||||
|
||||
ge_set(ge, &tmp)
|
||||
|
||||
if !unsafe_is_vartime {
|
||||
ge_clear(&tmp)
|
||||
mem.zero_explicit(&tmp_add, size_of(Add_Scratch))
|
||||
mem.zero_explicit(&tmp_addend, size_of(Addend_Group_Element))
|
||||
mem.zero_explicit(&tmp_dbl, size_of(Double_Scratch))
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
Multiply_Table :: [15]Addend_Group_Element // 0 = inf, which is implicit.
|
||||
|
||||
@(private)
|
||||
mul_tbl_set :: proc "contextless" (
|
||||
tbl: ^Multiply_Table,
|
||||
ge: ^Group_Element,
|
||||
tmp_add: ^Add_Scratch,
|
||||
) {
|
||||
tmp: Group_Element = ---
|
||||
ge_set(&tmp, ge)
|
||||
|
||||
ge_addend_set(&tbl[0], ge)
|
||||
for i := 1; i < 15; i = i + 1 {
|
||||
ge_add_addend(&tmp, &tmp, &tbl[0], tmp_add)
|
||||
ge_addend_set(&tbl[i], &tmp)
|
||||
}
|
||||
|
||||
ge_clear(&tmp)
|
||||
}
|
||||
|
||||
@(private)
|
||||
mul_tbl_add :: proc "contextless" (
|
||||
ge: ^Group_Element,
|
||||
tbl: ^Multiply_Table,
|
||||
idx: u64,
|
||||
tmp_add: ^Add_Scratch,
|
||||
tmp_addend: ^Addend_Group_Element,
|
||||
unsafe_is_vartime: bool,
|
||||
) {
|
||||
// Variable time lookup, with the addition omitted entirely if idx == 0.
|
||||
if unsafe_is_vartime {
|
||||
// Skip adding the point at infinity.
|
||||
if idx != 0 {
|
||||
ge_add_addend(ge, ge, &tbl[idx - 1], tmp_add)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Constant time lookup.
|
||||
tmp_addend^ = {
|
||||
// Point at infinity (0, 1, 1, 0) in precomputed form
|
||||
{1, 0, 0, 0, 0}, // y - x
|
||||
{1, 0, 0, 0, 0}, // y + x
|
||||
{0, 0, 0, 0, 0}, // t * 2d
|
||||
{2, 0, 0, 0, 0}, // z * 2
|
||||
}
|
||||
for i := u64(1); i < 16; i = i + 1 {
|
||||
_, ctrl := bits.sub_u64(0, (i ~ idx), 0)
|
||||
ge_addend_conditional_assign(tmp_addend, &tbl[i - 1], int(~ctrl) & 1)
|
||||
}
|
||||
ge_add_addend(ge, ge, tmp_addend, tmp_add)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ package fiat
|
||||
u1 :: distinct u8
|
||||
i1 :: distinct i8
|
||||
|
||||
@(optimization_mode="none")
|
||||
@(optimization_mode = "none")
|
||||
cmovznz_u64 :: proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) {
|
||||
x1 := (u64(arg1) * 0xffffffffffffffff)
|
||||
x2 := ((x1 & arg3) | ((~x1) & arg2))
|
||||
@@ -17,7 +17,7 @@ cmovznz_u64 :: proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) {
|
||||
return
|
||||
}
|
||||
|
||||
@(optimization_mode="none")
|
||||
@(optimization_mode = "none")
|
||||
cmovznz_u32 :: proc "contextless" (arg1: u1, arg2, arg3: u32) -> (out1: u32) {
|
||||
x1 := (u32(arg1) * 0xffffffff)
|
||||
x2 := ((x1 & arg3) | ((~x1) & arg2))
|
||||
|
||||
@@ -3,14 +3,32 @@ package field_curve25519
|
||||
import "core:crypto"
|
||||
import "core:mem"
|
||||
|
||||
fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element {
|
||||
fe_relax_cast :: #force_inline proc "contextless" (
|
||||
arg1: ^Tight_Field_Element,
|
||||
) -> ^Loose_Field_Element {
|
||||
return transmute(^Loose_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_tighten_cast :: #force_inline proc "contextless" (arg1: ^Loose_Field_Element) -> ^Tight_Field_Element {
|
||||
fe_tighten_cast :: #force_inline proc "contextless" (
|
||||
arg1: ^Loose_Field_Element,
|
||||
) -> ^Tight_Field_Element {
|
||||
return transmute(^Tight_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_clear :: proc "contextless" (
|
||||
arg1: $T,
|
||||
) where T == ^Tight_Field_Element || T == ^Loose_Field_Element {
|
||||
mem.zero_explicit(arg1, size_of(arg1^))
|
||||
}
|
||||
|
||||
fe_clear_vec :: proc "contextless" (
|
||||
arg1: $T,
|
||||
) where T == []^Tight_Field_Element || T == []^Loose_Field_Element {
|
||||
for fe in arg1 {
|
||||
fe_clear(fe)
|
||||
}
|
||||
}
|
||||
|
||||
fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) {
|
||||
// Ignore the unused bit by copying the input and masking the bit off
|
||||
// prior to deserialization.
|
||||
@@ -23,12 +41,25 @@ fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
}
|
||||
|
||||
fe_is_negative :: proc "contextless" (arg1: ^Tight_Field_Element) -> int {
|
||||
tmp1: [32]byte = ---
|
||||
|
||||
fe_to_bytes(&tmp1, arg1)
|
||||
ret := tmp1[0] & 1
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
|
||||
return int(ret)
|
||||
}
|
||||
|
||||
fe_equal :: proc "contextless" (arg1, arg2: ^Tight_Field_Element) -> int {
|
||||
tmp2: [32]byte = ---
|
||||
tmp1, tmp2: [32]byte = ---, ---
|
||||
|
||||
fe_to_bytes(&tmp1, arg1)
|
||||
fe_to_bytes(&tmp2, arg2)
|
||||
ret := fe_equal_bytes(arg1, &tmp2)
|
||||
ret := crypto.compare_constant_time(tmp1[:], tmp2[:])
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
mem.zero_explicit(&tmp2, size_of(tmp2))
|
||||
|
||||
return ret
|
||||
@@ -46,7 +77,11 @@ fe_equal_bytes :: proc "contextless" (arg1: ^Tight_Field_Element, arg2: ^[32]byt
|
||||
return ret
|
||||
}
|
||||
|
||||
fe_carry_pow2k :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element, arg2: uint) {
|
||||
fe_carry_pow2k :: proc "contextless" (
|
||||
out1: ^Tight_Field_Element,
|
||||
arg1: ^Loose_Field_Element,
|
||||
arg2: uint,
|
||||
) {
|
||||
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
|
||||
if arg2 == 0 {
|
||||
fe_one(out1)
|
||||
@@ -54,27 +89,46 @@ fe_carry_pow2k :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element,
|
||||
}
|
||||
|
||||
fe_carry_square(out1, arg1)
|
||||
for _ in 1..<arg2 {
|
||||
for _ in 1 ..< arg2 {
|
||||
fe_carry_square(out1, fe_relax_cast(out1))
|
||||
}
|
||||
}
|
||||
|
||||
fe_carry_add :: #force_inline proc "contextless" (out1, arg1, arg2: ^Tight_Field_Element) {
|
||||
fe_add(fe_relax_cast(out1), arg1, arg2)
|
||||
fe_carry(out1, fe_relax_cast(out1))
|
||||
}
|
||||
|
||||
fe_carry_sub :: #force_inline proc "contextless" (out1, arg1, arg2: ^Tight_Field_Element) {
|
||||
fe_sub(fe_relax_cast(out1), arg1, arg2)
|
||||
fe_carry(out1, fe_relax_cast(out1))
|
||||
}
|
||||
|
||||
fe_carry_opp :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
|
||||
fe_opp(fe_relax_cast(out1), arg1)
|
||||
fe_carry(out1, fe_relax_cast(out1))
|
||||
}
|
||||
|
||||
fe_carry_invsqrt :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) -> int {
|
||||
// Inverse square root taken from Monocypher.
|
||||
fe_carry_abs :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
|
||||
fe_cond_negate(out1, arg1, fe_is_negative(arg1))
|
||||
}
|
||||
|
||||
fe_carry_sqrt_ratio_m1 :: proc "contextless" (
|
||||
out1: ^Tight_Field_Element,
|
||||
arg1: ^Loose_Field_Element, // u
|
||||
arg2: ^Loose_Field_Element, // v
|
||||
) -> int {
|
||||
// SQRT_RATIO_M1(u, v) from RFC 9496 - 4.2, based on the inverse
|
||||
// square root from Monocypher.
|
||||
|
||||
w: Tight_Field_Element = ---
|
||||
fe_carry_mul(&w, arg1, arg2) // u * v
|
||||
|
||||
// r = tmp1 = u * w^((p-5)/8)
|
||||
tmp1, tmp2, tmp3: Tight_Field_Element = ---, ---, ---
|
||||
|
||||
// t0 = x^((p-5)/8)
|
||||
// Can be achieved with a simple double & add ladder,
|
||||
// but it would be slower.
|
||||
fe_carry_pow2k(&tmp1, arg1, 1)
|
||||
fe_carry_pow2k(&tmp1, fe_relax_cast(&w), 1)
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 2)
|
||||
fe_carry_mul(&tmp2, arg1, fe_relax_cast(&tmp2))
|
||||
fe_carry_mul(&tmp2, fe_relax_cast(&w), fe_relax_cast(&tmp2))
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), fe_relax_cast(&tmp2))
|
||||
fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 1)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
@@ -93,46 +147,121 @@ fe_carry_invsqrt :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp2), 50)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 2)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), arg1)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), fe_relax_cast(&w)) // w^((p-5)/8)
|
||||
|
||||
// quartic = x^((p-1)/4)
|
||||
quartic := &tmp2
|
||||
fe_carry_square(quartic, fe_relax_cast(&tmp1))
|
||||
fe_carry_mul(quartic, fe_relax_cast(quartic), arg1)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), arg1) // u * w^((p-5)/8)
|
||||
|
||||
// Serialize quartic once to save on repeated serialization/sanitization.
|
||||
quartic_buf: [32]byte = ---
|
||||
fe_to_bytes(&quartic_buf, quartic)
|
||||
check := &tmp3
|
||||
// Serialize `check` once to save on repeated serialization.
|
||||
r, check := &tmp1, &tmp2
|
||||
b: [32]byte = ---
|
||||
fe_carry_square(check, fe_relax_cast(r))
|
||||
fe_carry_mul(check, fe_relax_cast(check), arg2) // check * v
|
||||
fe_to_bytes(&b, check)
|
||||
|
||||
fe_one(check)
|
||||
p1 := fe_equal_bytes(check, &quartic_buf)
|
||||
fe_carry_opp(check, check)
|
||||
m1 := fe_equal_bytes(check, &quartic_buf)
|
||||
fe_carry_opp(check, &SQRT_M1)
|
||||
ms := fe_equal_bytes(check, &quartic_buf)
|
||||
u, neg_u, neg_u_i := &tmp3, &w, check
|
||||
fe_carry(u, arg1)
|
||||
fe_carry_opp(neg_u, u)
|
||||
fe_carry_mul(neg_u_i, fe_relax_cast(neg_u), fe_relax_cast(&FE_SQRT_M1))
|
||||
|
||||
// if quartic == -1 or sqrt(-1)
|
||||
// then isr = x^((p-1)/4) * sqrt(-1)
|
||||
// else isr = x^((p-1)/4)
|
||||
fe_carry_mul(out1, fe_relax_cast(&tmp1), fe_relax_cast(&SQRT_M1))
|
||||
fe_cond_assign(out1, &tmp1, (m1|ms) ~ 1)
|
||||
correct_sign_sqrt := fe_equal_bytes(u, &b)
|
||||
flipped_sign_sqrt := fe_equal_bytes(neg_u, &b)
|
||||
flipped_sign_sqrt_i := fe_equal_bytes(neg_u_i, &b)
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
mem.zero_explicit(&tmp2, size_of(tmp2))
|
||||
mem.zero_explicit(&tmp3, size_of(tmp3))
|
||||
mem.zero_explicit(&quartic_buf, size_of(quartic_buf))
|
||||
r_prime := check
|
||||
fe_carry_mul(r_prime, fe_relax_cast(r), fe_relax_cast(&FE_SQRT_M1))
|
||||
fe_cond_assign(r, r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i)
|
||||
|
||||
return p1 | m1
|
||||
// Pick the non-negative square root.
|
||||
fe_carry_abs(out1, r)
|
||||
|
||||
fe_clear_vec([]^Tight_Field_Element{&w, &tmp1, &tmp2, &tmp3})
|
||||
mem.zero_explicit(&b, size_of(b))
|
||||
|
||||
return correct_sign_sqrt | flipped_sign_sqrt
|
||||
}
|
||||
|
||||
fe_carry_inv :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
fe_carry_inv :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
tmp1: Tight_Field_Element
|
||||
|
||||
fe_carry_square(&tmp1, arg1)
|
||||
_ = fe_carry_invsqrt(&tmp1, fe_relax_cast(&tmp1))
|
||||
_ = fe_carry_sqrt_ratio_m1(&tmp1, fe_relax_cast(&FE_ONE), fe_relax_cast(&tmp1))
|
||||
fe_carry_square(&tmp1, fe_relax_cast(&tmp1))
|
||||
fe_carry_mul(out1, fe_relax_cast(&tmp1), arg1)
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
fe_clear(&tmp1)
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
out1[4] = 0
|
||||
}
|
||||
|
||||
fe_one :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||
out1[0] = 1
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
out1[4] = 0
|
||||
}
|
||||
|
||||
fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
x4 := arg1[3]
|
||||
x5 := arg1[4]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) {
|
||||
mask := (u64(arg1) * 0xffffffffffffffff)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
x = (out1[3] ~ out2[3]) & mask
|
||||
x4, y4 := out1[3] ~ x, out2[3] ~ x
|
||||
x = (out1[4] ~ out2[4]) & mask
|
||||
x5, y5 := out1[4] ~ x, out2[4] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
out1[3], out2[3] = x4, y4
|
||||
out1[4], out2[4] = x5, y5
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_select :: #force_no_inline proc "contextless" (
|
||||
out1, arg1, arg2: $T,
|
||||
arg3: int,
|
||||
) where T == ^Tight_Field_Element || T == ^Loose_Field_Element {
|
||||
mask := (u64(arg3) * 0xffffffffffffffff)
|
||||
x1 := ((mask & arg2[0]) | ((~mask) & arg1[0]))
|
||||
x2 := ((mask & arg2[1]) | ((~mask) & arg1[1]))
|
||||
x3 := ((mask & arg2[2]) | ((~mask) & arg1[2]))
|
||||
x4 := ((mask & arg2[3]) | ((~mask) & arg1[3]))
|
||||
x5 := ((mask & arg2[4]) | ((~mask) & arg1[4]))
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_cond_negate :: proc "contextless" (out1, arg1: ^Tight_Field_Element, ctrl: int) {
|
||||
tmp1: Tight_Field_Element = ---
|
||||
fe_carry_opp(&tmp1, arg1)
|
||||
fe_cond_select(out1, arg1, &tmp1, ctrl)
|
||||
|
||||
fe_clear(&tmp1)
|
||||
}
|
||||
|
||||
@@ -30,8 +30,6 @@ package field_curve25519
|
||||
//
|
||||
// While the base implementation is provably correct, this implementation
|
||||
// makes no such claims as the port and optimizations were done by hand.
|
||||
// At some point, it may be worth adding support to fiat-crypto for
|
||||
// generating Odin output.
|
||||
//
|
||||
// TODO:
|
||||
// * When fiat-crypto supports it, using a saturated 64-bit limbs
|
||||
@@ -44,7 +42,10 @@ import "core:math/bits"
|
||||
Loose_Field_Element :: distinct [5]u64
|
||||
Tight_Field_Element :: distinct [5]u64
|
||||
|
||||
SQRT_M1 := Tight_Field_Element{
|
||||
FE_ZERO := Tight_Field_Element{0, 0, 0, 0, 0}
|
||||
FE_ONE := Tight_Field_Element{1, 0, 0, 0, 0}
|
||||
|
||||
FE_SQRT_M1 := Tight_Field_Element {
|
||||
1718705420411056,
|
||||
234908883556509,
|
||||
2233514472574048,
|
||||
@@ -52,7 +53,13 @@ SQRT_M1 := Tight_Field_Element{
|
||||
765476049583133,
|
||||
}
|
||||
|
||||
_addcarryx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
_addcarryx_u51 :: #force_inline proc "contextless" (
|
||||
arg1: fiat.u1,
|
||||
arg2, arg3: u64,
|
||||
) -> (
|
||||
out1: u64,
|
||||
out2: fiat.u1,
|
||||
) {
|
||||
x1 := ((u64(arg1) + arg2) + arg3)
|
||||
x2 := (x1 & 0x7ffffffffffff)
|
||||
x3 := fiat.u1((x1 >> 51))
|
||||
@@ -61,7 +68,13 @@ _addcarryx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u
|
||||
return
|
||||
}
|
||||
|
||||
_subborrowx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
_subborrowx_u51 :: #force_inline proc "contextless" (
|
||||
arg1: fiat.u1,
|
||||
arg2, arg3: u64,
|
||||
) -> (
|
||||
out1: u64,
|
||||
out2: fiat.u1,
|
||||
) {
|
||||
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
|
||||
x2 := fiat.i1((x1 >> 51))
|
||||
x3 := (u64(x1) & 0x7ffffffffffff)
|
||||
@@ -70,7 +83,7 @@ _subborrowx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3:
|
||||
return
|
||||
}
|
||||
|
||||
fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
|
||||
fe_carry_mul :: proc "contextless" (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
|
||||
x2, x1 := bits.mul_u64(arg1[4], (arg2[4] * 0x13))
|
||||
x4, x3 := bits.mul_u64(arg1[4], (arg2[3] * 0x13))
|
||||
x6, x5 := bits.mul_u64(arg1[4], (arg2[2] * 0x13))
|
||||
@@ -169,7 +182,7 @@ fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Eleme
|
||||
out1[4] = x152
|
||||
}
|
||||
|
||||
fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
fe_carry_square :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
x1 := (arg1[4] * 0x13)
|
||||
x2 := (x1 * 0x2)
|
||||
x3 := (arg1[4] * 0x2)
|
||||
@@ -305,8 +318,11 @@ fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Ele
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
@(optimization_mode="none")
|
||||
fe_cond_assign :: #force_no_inline proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: int) {
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_assign :: #force_no_inline proc "contextless" (
|
||||
out1, arg1: ^Tight_Field_Element,
|
||||
arg2: int,
|
||||
) {
|
||||
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
|
||||
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
|
||||
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
|
||||
@@ -527,7 +543,10 @@ fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_E
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_carry_scmul_121666 :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
fe_carry_scmul_121666 :: proc "contextless" (
|
||||
out1: ^Tight_Field_Element,
|
||||
arg1: ^Loose_Field_Element,
|
||||
) {
|
||||
x2, x1 := bits.mul_u64(0x1db42, arg1[4])
|
||||
x4, x3 := bits.mul_u64(0x1db42, arg1[3])
|
||||
x6, x5 := bits.mul_u64(0x1db42, arg1[2])
|
||||
@@ -565,54 +584,3 @@ fe_carry_scmul_121666 :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_El
|
||||
out1[3] = x27
|
||||
out1[4] = x32
|
||||
}
|
||||
|
||||
// The following routines were added by hand, and do not come from fiat-crypto.
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
out1[4] = 0
|
||||
}
|
||||
|
||||
fe_one :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||
out1[0] = 1
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
out1[4] = 0
|
||||
}
|
||||
|
||||
fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
x4 := arg1[3]
|
||||
x5 := arg1[4]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
@(optimization_mode="none")
|
||||
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) {
|
||||
mask := -u64(arg1)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
x = (out1[3] ~ out2[3]) & mask
|
||||
x4, y4 := out1[3] ~ x, out2[3] ~ x
|
||||
x = (out1[4] ~ out2[4]) & mask
|
||||
x5, y5 := out1[4] ~ x, out2[4] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
out1[3], out2[3] = x4, y4
|
||||
out1[4], out2[4] = x5, y5
|
||||
}
|
||||
|
||||
@@ -1,17 +1,26 @@
|
||||
package field_poly1305
|
||||
|
||||
import "core:crypto/util"
|
||||
import "base:intrinsics"
|
||||
import "core:encoding/endian"
|
||||
import "core:mem"
|
||||
|
||||
fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element {
|
||||
fe_relax_cast :: #force_inline proc "contextless" (
|
||||
arg1: ^Tight_Field_Element,
|
||||
) -> ^Loose_Field_Element {
|
||||
return transmute(^Loose_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_tighten_cast :: #force_inline proc "contextless" (arg1: ^Loose_Field_Element) -> ^Tight_Field_Element {
|
||||
fe_tighten_cast :: #force_inline proc "contextless" (
|
||||
arg1: ^Loose_Field_Element,
|
||||
) -> ^Tight_Field_Element {
|
||||
return transmute(^Tight_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, arg2: byte, sanitize: bool = true) {
|
||||
fe_from_bytes :: #force_inline proc "contextless" (
|
||||
out1: ^Tight_Field_Element,
|
||||
arg1: []byte,
|
||||
arg2: byte,
|
||||
) {
|
||||
// fiat-crypto's deserialization routine effectively processes a
|
||||
// single byte at a time, and wants 256-bits of input for a value
|
||||
// that will be 128-bits or 129-bits.
|
||||
@@ -20,47 +29,68 @@ fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, a
|
||||
// makes implementing the actual MAC block processing considerably
|
||||
// neater.
|
||||
|
||||
assert(len(arg1) == 16)
|
||||
|
||||
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||
// While it may be unwise to do deserialization here on our
|
||||
// own when fiat-crypto provides equivalent functionality,
|
||||
// doing it this way provides a little under 3x performance
|
||||
// improvement when optimization is enabled.
|
||||
src_p := transmute(^[2]u64)(&arg1[0])
|
||||
lo := src_p[0]
|
||||
hi := src_p[1]
|
||||
|
||||
// This is inspired by poly1305-donna, though adjustments were
|
||||
// made since a Tight_Field_Element's limbs are 44-bits, 43-bits,
|
||||
// and 43-bits wide.
|
||||
//
|
||||
// Note: This could be transplated into fe_from_u64s, but that
|
||||
// code is called once per MAC, and is non-criticial path.
|
||||
hibit := u64(arg2) << 41 // arg2 << 128
|
||||
out1[0] = lo & 0xfffffffffff
|
||||
out1[1] = ((lo >> 44) | (hi << 20)) & 0x7ffffffffff
|
||||
out1[2] = ((hi >> 23) & 0x7ffffffffff) | hibit
|
||||
} else {
|
||||
tmp: [32]byte
|
||||
copy_slice(tmp[0:16], arg1[:])
|
||||
tmp[16] = arg2
|
||||
|
||||
_fe_from_bytes(out1, &tmp)
|
||||
if sanitize {
|
||||
// This is used to deserialize `s` which is confidential.
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
if len(arg1) != 16 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
|
||||
// While it may be unwise to do deserialization here on our
|
||||
// own when fiat-crypto provides equivalent functionality,
|
||||
// doing it this way provides a little under 3x performance
|
||||
// improvement when optimization is enabled.
|
||||
lo := endian.unchecked_get_u64le(arg1[0:])
|
||||
hi := endian.unchecked_get_u64le(arg1[8:])
|
||||
|
||||
// This is inspired by poly1305-donna, though adjustments were
|
||||
// made since a Tight_Field_Element's limbs are 44-bits, 43-bits,
|
||||
// and 43-bits wide.
|
||||
//
|
||||
// Note: This could be transplated into fe_from_u64s, but that
|
||||
// code is called once per MAC, and is non-criticial path.
|
||||
hibit := u64(arg2) << 41 // arg2 << 128
|
||||
out1[0] = lo & 0xfffffffffff
|
||||
out1[1] = ((lo >> 44) | (hi << 20)) & 0x7ffffffffff
|
||||
out1[2] = ((hi >> 23) & 0x7ffffffffff) | hibit
|
||||
}
|
||||
|
||||
fe_from_u64s :: proc "contextless" (out1: ^Tight_Field_Element, lo, hi: u64) {
|
||||
tmp: [32]byte
|
||||
util.PUT_U64_LE(tmp[0:8], lo)
|
||||
util.PUT_U64_LE(tmp[8:16], hi)
|
||||
endian.unchecked_put_u64le(tmp[0:], lo)
|
||||
endian.unchecked_put_u64le(tmp[8:], hi)
|
||||
|
||||
_fe_from_bytes(out1, &tmp)
|
||||
|
||||
// This routine is only used to deserialize `r` which is confidential.
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
}
|
||||
|
||||
fe_set :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_swap :: #force_no_inline proc "contextless" (
|
||||
out1, out2: ^Tight_Field_Element,
|
||||
arg1: bool,
|
||||
) {
|
||||
mask := (u64(arg1) * 0xffffffffffffffff)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
}
|
||||
|
||||
@@ -39,7 +39,13 @@ import "core:math/bits"
|
||||
Loose_Field_Element :: distinct [3]u64
|
||||
Tight_Field_Element :: distinct [3]u64
|
||||
|
||||
_addcarryx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
_addcarryx_u44 :: #force_inline proc "contextless" (
|
||||
arg1: fiat.u1,
|
||||
arg2, arg3: u64,
|
||||
) -> (
|
||||
out1: u64,
|
||||
out2: fiat.u1,
|
||||
) {
|
||||
x1 := ((u64(arg1) + arg2) + arg3)
|
||||
x2 := (x1 & 0xfffffffffff)
|
||||
x3 := fiat.u1((x1 >> 44))
|
||||
@@ -48,7 +54,13 @@ _addcarryx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u
|
||||
return
|
||||
}
|
||||
|
||||
_subborrowx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
_subborrowx_u44 :: #force_inline proc "contextless" (
|
||||
arg1: fiat.u1,
|
||||
arg2, arg3: u64,
|
||||
) -> (
|
||||
out1: u64,
|
||||
out2: fiat.u1,
|
||||
) {
|
||||
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
|
||||
x2 := fiat.i1((x1 >> 44))
|
||||
x3 := (u64(x1) & 0xfffffffffff)
|
||||
@@ -57,7 +69,13 @@ _subborrowx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3:
|
||||
return
|
||||
}
|
||||
|
||||
_addcarryx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
_addcarryx_u43 :: #force_inline proc "contextless" (
|
||||
arg1: fiat.u1,
|
||||
arg2, arg3: u64,
|
||||
) -> (
|
||||
out1: u64,
|
||||
out2: fiat.u1,
|
||||
) {
|
||||
x1 := ((u64(arg1) + arg2) + arg3)
|
||||
x2 := (x1 & 0x7ffffffffff)
|
||||
x3 := fiat.u1((x1 >> 43))
|
||||
@@ -66,7 +84,13 @@ _addcarryx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u
|
||||
return
|
||||
}
|
||||
|
||||
_subborrowx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
|
||||
_subborrowx_u43 :: #force_inline proc "contextless" (
|
||||
arg1: fiat.u1,
|
||||
arg2, arg3: u64,
|
||||
) -> (
|
||||
out1: u64,
|
||||
out2: fiat.u1,
|
||||
) {
|
||||
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
|
||||
x2 := fiat.i1((x1 >> 43))
|
||||
x3 := (u64(x1) & 0x7ffffffffff)
|
||||
@@ -75,7 +99,7 @@ _subborrowx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3:
|
||||
return
|
||||
}
|
||||
|
||||
fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
|
||||
fe_carry_mul :: proc "contextless" (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
|
||||
x2, x1 := bits.mul_u64(arg1[2], (arg2[2] * 0x5))
|
||||
x4, x3 := bits.mul_u64(arg1[2], (arg2[1] * 0xa))
|
||||
x6, x5 := bits.mul_u64(arg1[1], (arg2[2] * 0xa))
|
||||
@@ -120,7 +144,7 @@ fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Eleme
|
||||
out1[2] = x62
|
||||
}
|
||||
|
||||
fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
fe_carry_square :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
x1 := (arg1[2] * 0x5)
|
||||
x2 := (x1 * 0x2)
|
||||
x3 := (arg1[2] * 0x2)
|
||||
@@ -201,8 +225,11 @@ fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Ele
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
@(optimization_mode="none")
|
||||
fe_cond_assign :: #force_no_inline proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: bool) {
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_assign :: #force_no_inline proc "contextless" (
|
||||
out1, arg1: ^Tight_Field_Element,
|
||||
arg2: bool,
|
||||
) {
|
||||
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
|
||||
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
|
||||
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
|
||||
@@ -325,34 +352,3 @@ fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_E
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
// The following routines were added by hand, and do not come from fiat-crypto.
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
}
|
||||
|
||||
fe_set :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
@(optimization_mode="none")
|
||||
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: bool) {
|
||||
mask := -u64(arg1)
|
||||
x := (out1[0] ~ out2[0]) & mask
|
||||
x1, y1 := out1[0] ~ x, out2[0] ~ x
|
||||
x = (out1[1] ~ out2[1]) & mask
|
||||
x2, y2 := out1[1] ~ x, out2[1] ~ x
|
||||
x = (out1[2] ~ out2[2]) & mask
|
||||
x3, y3 := out1[2] ~ x, out2[2] ~ x
|
||||
out1[0], out2[0] = x1, y1
|
||||
out1[1], out2[1] = x2, y2
|
||||
out1[2], out2[2] = x3, y3
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package field_scalar25519
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
@(private)
|
||||
_TWO_168 := Montgomery_Domain_Field_Element {
|
||||
0x5b8ab432eac74798,
|
||||
0x38afddd6de59d5d7,
|
||||
0xa2c131b399411b7c,
|
||||
0x6329a7ed9ce5a30,
|
||||
}
|
||||
@(private)
|
||||
_TWO_336 := Montgomery_Domain_Field_Element {
|
||||
0xbd3d108e2b35ecc5,
|
||||
0x5c3a3718bdf9c90b,
|
||||
0x63aa97a331b4f2ee,
|
||||
0x3d217f5be65cb5c,
|
||||
}
|
||||
|
||||
fe_clear :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) {
|
||||
mem.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
|
||||
}
|
||||
|
||||
fe_from_bytes :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^[32]byte,
|
||||
unsafe_assume_canonical := false,
|
||||
) -> bool {
|
||||
tmp := Non_Montgomery_Domain_Field_Element {
|
||||
endian.unchecked_get_u64le(arg1[0:]),
|
||||
endian.unchecked_get_u64le(arg1[8:]),
|
||||
endian.unchecked_get_u64le(arg1[16:]),
|
||||
endian.unchecked_get_u64le(arg1[24:]),
|
||||
}
|
||||
defer mem.zero_explicit(&tmp, size_of(tmp))
|
||||
|
||||
// Check that tmp is in the the range [0, ELL).
|
||||
if !unsafe_assume_canonical {
|
||||
_, borrow := bits.sub_u64(ELL[0] - 1, tmp[0], 0)
|
||||
_, borrow = bits.sub_u64(ELL[1], tmp[1], borrow)
|
||||
_, borrow = bits.sub_u64(ELL[2], tmp[2], borrow)
|
||||
_, borrow = bits.sub_u64(ELL[3], tmp[3], borrow)
|
||||
if borrow != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fe_to_montgomery(out1, &tmp)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fe_from_bytes_rfc8032 :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^[32]byte,
|
||||
) {
|
||||
tmp: [64]byte
|
||||
copy(tmp[:], arg1[:])
|
||||
|
||||
// Apply "clamping" as in RFC 8032.
|
||||
tmp[0] &= 248
|
||||
tmp[31] &= 127
|
||||
tmp[31] |= 64 // Sets the 254th bit, so the encoding is non-canonical.
|
||||
|
||||
fe_from_bytes_wide(out1, &tmp)
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
fe_from_bytes_wide :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^[64]byte,
|
||||
) {
|
||||
tmp: Montgomery_Domain_Field_Element
|
||||
// Use Frank Denis' trick, as documented by Filippo Valsorda
|
||||
// at https://words.filippo.io/dispatches/wide-reduction/
|
||||
//
|
||||
// x = c * 2^336 + b * 2^168 + a mod l
|
||||
_fe_from_bytes_short(out1, arg1[:21]) // a
|
||||
|
||||
_fe_from_bytes_short(&tmp, arg1[21:42]) // b
|
||||
fe_mul(&tmp, &tmp, &_TWO_168) // b * 2^168
|
||||
fe_add(out1, out1, &tmp) // a + b * 2^168
|
||||
|
||||
_fe_from_bytes_short(&tmp, arg1[42:]) // c
|
||||
fe_mul(&tmp, &tmp, &_TWO_336) // c * 2^336
|
||||
fe_add(out1, out1, &tmp) // a + b * 2^168 + c * 2^336
|
||||
|
||||
fe_clear(&tmp)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element, arg1: []byte) {
|
||||
// INVARIANT: len(arg1) < 32.
|
||||
if len(arg1) >= 32 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
tmp: [32]byte
|
||||
copy(tmp[:], arg1)
|
||||
|
||||
_ = fe_from_bytes(out1, &tmp, true)
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
if len(out1) != 32 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
|
||||
tmp: Non_Montgomery_Domain_Field_Element
|
||||
fe_from_montgomery(&tmp, arg1)
|
||||
|
||||
endian.unchecked_put_u64le(out1[0:], tmp[0])
|
||||
endian.unchecked_put_u64le(out1[8:], tmp[1])
|
||||
endian.unchecked_put_u64le(out1[16:], tmp[2])
|
||||
endian.unchecked_put_u64le(out1[24:], tmp[3])
|
||||
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
|
||||
fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> int {
|
||||
tmp: Montgomery_Domain_Field_Element
|
||||
fe_sub(&tmp, arg1, arg2)
|
||||
|
||||
// This will only underflow iff arg1 == arg2, and we return the borrow,
|
||||
// which will be 1.
|
||||
_, borrow := bits.sub_u64(fe_non_zero(&tmp), 1, 0)
|
||||
|
||||
fe_clear(&tmp)
|
||||
|
||||
return int(borrow)
|
||||
}
|
||||
|
||||
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0
|
||||
out1[1] = 0
|
||||
out1[2] = 0
|
||||
out1[3] = 0
|
||||
}
|
||||
|
||||
fe_set :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
x4 := arg1[3]
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
@@ -0,0 +1,535 @@
|
||||
// The BSD 1-Clause License (BSD-1-Clause)
|
||||
//
|
||||
// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design,
|
||||
// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package field_scalar25519
|
||||
|
||||
// The file provides arithmetic on the field Z/(2^252+27742317777372353535851937790883648493)
|
||||
// using a 64-bit Montgomery form internal representation. It is derived
|
||||
// primarily from the machine generated Golang output from the fiat-crypto
|
||||
// project.
|
||||
//
|
||||
// While the base implementation is provably correct, this implementation
|
||||
// makes no such claims as the port and optimizations were done by hand.
|
||||
|
||||
import fiat "core:crypto/_fiat"
|
||||
import "core:math/bits"
|
||||
|
||||
// ELL is the saturated representation of the field order, least-significant
|
||||
// limb first.
|
||||
ELL :: [4]u64{0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000}
|
||||
|
||||
Montgomery_Domain_Field_Element :: distinct [4]u64
|
||||
Non_Montgomery_Domain_Field_Element :: distinct [4]u64
|
||||
|
||||
fe_mul :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, arg2[3])
|
||||
x8, x7 := bits.mul_u64(x4, arg2[2])
|
||||
x10, x9 := bits.mul_u64(x4, arg2[1])
|
||||
x12, x11 := bits.mul_u64(x4, arg2[0])
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x19 := (u64(fiat.u1(x18)) + x6)
|
||||
_, x20 := bits.mul_u64(x11, 0xd2b51da312547e1b)
|
||||
x23, x22 := bits.mul_u64(x20, 0x1000000000000000)
|
||||
x25, x24 := bits.mul_u64(x20, 0x14def9dea2f79cd6)
|
||||
x27, x26 := bits.mul_u64(x20, 0x5812631a5cf5d3ed)
|
||||
x28, x29 := bits.add_u64(x27, x24, u64(0x0))
|
||||
x30 := (u64(fiat.u1(x29)) + x25)
|
||||
_, x32 := bits.add_u64(x11, x26, u64(0x0))
|
||||
x33, x34 := bits.add_u64(x13, x28, u64(fiat.u1(x32)))
|
||||
x35, x36 := bits.add_u64(x15, x30, u64(fiat.u1(x34)))
|
||||
x37, x38 := bits.add_u64(x17, x22, u64(fiat.u1(x36)))
|
||||
x39, x40 := bits.add_u64(x19, x23, u64(fiat.u1(x38)))
|
||||
x42, x41 := bits.mul_u64(x1, arg2[3])
|
||||
x44, x43 := bits.mul_u64(x1, arg2[2])
|
||||
x46, x45 := bits.mul_u64(x1, arg2[1])
|
||||
x48, x47 := bits.mul_u64(x1, arg2[0])
|
||||
x49, x50 := bits.add_u64(x48, x45, u64(0x0))
|
||||
x51, x52 := bits.add_u64(x46, x43, u64(fiat.u1(x50)))
|
||||
x53, x54 := bits.add_u64(x44, x41, u64(fiat.u1(x52)))
|
||||
x55 := (u64(fiat.u1(x54)) + x42)
|
||||
x56, x57 := bits.add_u64(x33, x47, u64(0x0))
|
||||
x58, x59 := bits.add_u64(x35, x49, u64(fiat.u1(x57)))
|
||||
x60, x61 := bits.add_u64(x37, x51, u64(fiat.u1(x59)))
|
||||
x62, x63 := bits.add_u64(x39, x53, u64(fiat.u1(x61)))
|
||||
x64, x65 := bits.add_u64(u64(fiat.u1(x40)), x55, u64(fiat.u1(x63)))
|
||||
_, x66 := bits.mul_u64(x56, 0xd2b51da312547e1b)
|
||||
x69, x68 := bits.mul_u64(x66, 0x1000000000000000)
|
||||
x71, x70 := bits.mul_u64(x66, 0x14def9dea2f79cd6)
|
||||
x73, x72 := bits.mul_u64(x66, 0x5812631a5cf5d3ed)
|
||||
x74, x75 := bits.add_u64(x73, x70, u64(0x0))
|
||||
x76 := (u64(fiat.u1(x75)) + x71)
|
||||
_, x78 := bits.add_u64(x56, x72, u64(0x0))
|
||||
x79, x80 := bits.add_u64(x58, x74, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x60, x76, u64(fiat.u1(x80)))
|
||||
x83, x84 := bits.add_u64(x62, x68, u64(fiat.u1(x82)))
|
||||
x85, x86 := bits.add_u64(x64, x69, u64(fiat.u1(x84)))
|
||||
x87 := (u64(fiat.u1(x86)) + u64(fiat.u1(x65)))
|
||||
x89, x88 := bits.mul_u64(x2, arg2[3])
|
||||
x91, x90 := bits.mul_u64(x2, arg2[2])
|
||||
x93, x92 := bits.mul_u64(x2, arg2[1])
|
||||
x95, x94 := bits.mul_u64(x2, arg2[0])
|
||||
x96, x97 := bits.add_u64(x95, x92, u64(0x0))
|
||||
x98, x99 := bits.add_u64(x93, x90, u64(fiat.u1(x97)))
|
||||
x100, x101 := bits.add_u64(x91, x88, u64(fiat.u1(x99)))
|
||||
x102 := (u64(fiat.u1(x101)) + x89)
|
||||
x103, x104 := bits.add_u64(x79, x94, u64(0x0))
|
||||
x105, x106 := bits.add_u64(x81, x96, u64(fiat.u1(x104)))
|
||||
x107, x108 := bits.add_u64(x83, x98, u64(fiat.u1(x106)))
|
||||
x109, x110 := bits.add_u64(x85, x100, u64(fiat.u1(x108)))
|
||||
x111, x112 := bits.add_u64(x87, x102, u64(fiat.u1(x110)))
|
||||
_, x113 := bits.mul_u64(x103, 0xd2b51da312547e1b)
|
||||
x116, x115 := bits.mul_u64(x113, 0x1000000000000000)
|
||||
x118, x117 := bits.mul_u64(x113, 0x14def9dea2f79cd6)
|
||||
x120, x119 := bits.mul_u64(x113, 0x5812631a5cf5d3ed)
|
||||
x121, x122 := bits.add_u64(x120, x117, u64(0x0))
|
||||
x123 := (u64(fiat.u1(x122)) + x118)
|
||||
_, x125 := bits.add_u64(x103, x119, u64(0x0))
|
||||
x126, x127 := bits.add_u64(x105, x121, u64(fiat.u1(x125)))
|
||||
x128, x129 := bits.add_u64(x107, x123, u64(fiat.u1(x127)))
|
||||
x130, x131 := bits.add_u64(x109, x115, u64(fiat.u1(x129)))
|
||||
x132, x133 := bits.add_u64(x111, x116, u64(fiat.u1(x131)))
|
||||
x134 := (u64(fiat.u1(x133)) + u64(fiat.u1(x112)))
|
||||
x136, x135 := bits.mul_u64(x3, arg2[3])
|
||||
x138, x137 := bits.mul_u64(x3, arg2[2])
|
||||
x140, x139 := bits.mul_u64(x3, arg2[1])
|
||||
x142, x141 := bits.mul_u64(x3, arg2[0])
|
||||
x143, x144 := bits.add_u64(x142, x139, u64(0x0))
|
||||
x145, x146 := bits.add_u64(x140, x137, u64(fiat.u1(x144)))
|
||||
x147, x148 := bits.add_u64(x138, x135, u64(fiat.u1(x146)))
|
||||
x149 := (u64(fiat.u1(x148)) + x136)
|
||||
x150, x151 := bits.add_u64(x126, x141, u64(0x0))
|
||||
x152, x153 := bits.add_u64(x128, x143, u64(fiat.u1(x151)))
|
||||
x154, x155 := bits.add_u64(x130, x145, u64(fiat.u1(x153)))
|
||||
x156, x157 := bits.add_u64(x132, x147, u64(fiat.u1(x155)))
|
||||
x158, x159 := bits.add_u64(x134, x149, u64(fiat.u1(x157)))
|
||||
_, x160 := bits.mul_u64(x150, 0xd2b51da312547e1b)
|
||||
x163, x162 := bits.mul_u64(x160, 0x1000000000000000)
|
||||
x165, x164 := bits.mul_u64(x160, 0x14def9dea2f79cd6)
|
||||
x167, x166 := bits.mul_u64(x160, 0x5812631a5cf5d3ed)
|
||||
x168, x169 := bits.add_u64(x167, x164, u64(0x0))
|
||||
x170 := (u64(fiat.u1(x169)) + x165)
|
||||
_, x172 := bits.add_u64(x150, x166, u64(0x0))
|
||||
x173, x174 := bits.add_u64(x152, x168, u64(fiat.u1(x172)))
|
||||
x175, x176 := bits.add_u64(x154, x170, u64(fiat.u1(x174)))
|
||||
x177, x178 := bits.add_u64(x156, x162, u64(fiat.u1(x176)))
|
||||
x179, x180 := bits.add_u64(x158, x163, u64(fiat.u1(x178)))
|
||||
x181 := (u64(fiat.u1(x180)) + u64(fiat.u1(x159)))
|
||||
x182, x183 := bits.sub_u64(x173, 0x5812631a5cf5d3ed, u64(0x0))
|
||||
x184, x185 := bits.sub_u64(x175, 0x14def9dea2f79cd6, u64(fiat.u1(x183)))
|
||||
x186, x187 := bits.sub_u64(x177, u64(0x0), u64(fiat.u1(x185)))
|
||||
x188, x189 := bits.sub_u64(x179, 0x1000000000000000, u64(fiat.u1(x187)))
|
||||
_, x191 := bits.sub_u64(x181, u64(0x0), u64(fiat.u1(x189)))
|
||||
x192 := fiat.cmovznz_u64(fiat.u1(x191), x182, x173)
|
||||
x193 := fiat.cmovznz_u64(fiat.u1(x191), x184, x175)
|
||||
x194 := fiat.cmovznz_u64(fiat.u1(x191), x186, x177)
|
||||
x195 := fiat.cmovznz_u64(fiat.u1(x191), x188, x179)
|
||||
out1[0] = x192
|
||||
out1[1] = x193
|
||||
out1[2] = x194
|
||||
out1[3] = x195
|
||||
}
|
||||
|
||||
fe_square :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, arg1[3])
|
||||
x8, x7 := bits.mul_u64(x4, arg1[2])
|
||||
x10, x9 := bits.mul_u64(x4, arg1[1])
|
||||
x12, x11 := bits.mul_u64(x4, arg1[0])
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
x19 := (u64(fiat.u1(x18)) + x6)
|
||||
_, x20 := bits.mul_u64(x11, 0xd2b51da312547e1b)
|
||||
x23, x22 := bits.mul_u64(x20, 0x1000000000000000)
|
||||
x25, x24 := bits.mul_u64(x20, 0x14def9dea2f79cd6)
|
||||
x27, x26 := bits.mul_u64(x20, 0x5812631a5cf5d3ed)
|
||||
x28, x29 := bits.add_u64(x27, x24, u64(0x0))
|
||||
x30 := (u64(fiat.u1(x29)) + x25)
|
||||
_, x32 := bits.add_u64(x11, x26, u64(0x0))
|
||||
x33, x34 := bits.add_u64(x13, x28, u64(fiat.u1(x32)))
|
||||
x35, x36 := bits.add_u64(x15, x30, u64(fiat.u1(x34)))
|
||||
x37, x38 := bits.add_u64(x17, x22, u64(fiat.u1(x36)))
|
||||
x39, x40 := bits.add_u64(x19, x23, u64(fiat.u1(x38)))
|
||||
x42, x41 := bits.mul_u64(x1, arg1[3])
|
||||
x44, x43 := bits.mul_u64(x1, arg1[2])
|
||||
x46, x45 := bits.mul_u64(x1, arg1[1])
|
||||
x48, x47 := bits.mul_u64(x1, arg1[0])
|
||||
x49, x50 := bits.add_u64(x48, x45, u64(0x0))
|
||||
x51, x52 := bits.add_u64(x46, x43, u64(fiat.u1(x50)))
|
||||
x53, x54 := bits.add_u64(x44, x41, u64(fiat.u1(x52)))
|
||||
x55 := (u64(fiat.u1(x54)) + x42)
|
||||
x56, x57 := bits.add_u64(x33, x47, u64(0x0))
|
||||
x58, x59 := bits.add_u64(x35, x49, u64(fiat.u1(x57)))
|
||||
x60, x61 := bits.add_u64(x37, x51, u64(fiat.u1(x59)))
|
||||
x62, x63 := bits.add_u64(x39, x53, u64(fiat.u1(x61)))
|
||||
x64, x65 := bits.add_u64(u64(fiat.u1(x40)), x55, u64(fiat.u1(x63)))
|
||||
_, x66 := bits.mul_u64(x56, 0xd2b51da312547e1b)
|
||||
x69, x68 := bits.mul_u64(x66, 0x1000000000000000)
|
||||
x71, x70 := bits.mul_u64(x66, 0x14def9dea2f79cd6)
|
||||
x73, x72 := bits.mul_u64(x66, 0x5812631a5cf5d3ed)
|
||||
x74, x75 := bits.add_u64(x73, x70, u64(0x0))
|
||||
x76 := (u64(fiat.u1(x75)) + x71)
|
||||
_, x78 := bits.add_u64(x56, x72, u64(0x0))
|
||||
x79, x80 := bits.add_u64(x58, x74, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x60, x76, u64(fiat.u1(x80)))
|
||||
x83, x84 := bits.add_u64(x62, x68, u64(fiat.u1(x82)))
|
||||
x85, x86 := bits.add_u64(x64, x69, u64(fiat.u1(x84)))
|
||||
x87 := (u64(fiat.u1(x86)) + u64(fiat.u1(x65)))
|
||||
x89, x88 := bits.mul_u64(x2, arg1[3])
|
||||
x91, x90 := bits.mul_u64(x2, arg1[2])
|
||||
x93, x92 := bits.mul_u64(x2, arg1[1])
|
||||
x95, x94 := bits.mul_u64(x2, arg1[0])
|
||||
x96, x97 := bits.add_u64(x95, x92, u64(0x0))
|
||||
x98, x99 := bits.add_u64(x93, x90, u64(fiat.u1(x97)))
|
||||
x100, x101 := bits.add_u64(x91, x88, u64(fiat.u1(x99)))
|
||||
x102 := (u64(fiat.u1(x101)) + x89)
|
||||
x103, x104 := bits.add_u64(x79, x94, u64(0x0))
|
||||
x105, x106 := bits.add_u64(x81, x96, u64(fiat.u1(x104)))
|
||||
x107, x108 := bits.add_u64(x83, x98, u64(fiat.u1(x106)))
|
||||
x109, x110 := bits.add_u64(x85, x100, u64(fiat.u1(x108)))
|
||||
x111, x112 := bits.add_u64(x87, x102, u64(fiat.u1(x110)))
|
||||
_, x113 := bits.mul_u64(x103, 0xd2b51da312547e1b)
|
||||
x116, x115 := bits.mul_u64(x113, 0x1000000000000000)
|
||||
x118, x117 := bits.mul_u64(x113, 0x14def9dea2f79cd6)
|
||||
x120, x119 := bits.mul_u64(x113, 0x5812631a5cf5d3ed)
|
||||
x121, x122 := bits.add_u64(x120, x117, u64(0x0))
|
||||
x123 := (u64(fiat.u1(x122)) + x118)
|
||||
_, x125 := bits.add_u64(x103, x119, u64(0x0))
|
||||
x126, x127 := bits.add_u64(x105, x121, u64(fiat.u1(x125)))
|
||||
x128, x129 := bits.add_u64(x107, x123, u64(fiat.u1(x127)))
|
||||
x130, x131 := bits.add_u64(x109, x115, u64(fiat.u1(x129)))
|
||||
x132, x133 := bits.add_u64(x111, x116, u64(fiat.u1(x131)))
|
||||
x134 := (u64(fiat.u1(x133)) + u64(fiat.u1(x112)))
|
||||
x136, x135 := bits.mul_u64(x3, arg1[3])
|
||||
x138, x137 := bits.mul_u64(x3, arg1[2])
|
||||
x140, x139 := bits.mul_u64(x3, arg1[1])
|
||||
x142, x141 := bits.mul_u64(x3, arg1[0])
|
||||
x143, x144 := bits.add_u64(x142, x139, u64(0x0))
|
||||
x145, x146 := bits.add_u64(x140, x137, u64(fiat.u1(x144)))
|
||||
x147, x148 := bits.add_u64(x138, x135, u64(fiat.u1(x146)))
|
||||
x149 := (u64(fiat.u1(x148)) + x136)
|
||||
x150, x151 := bits.add_u64(x126, x141, u64(0x0))
|
||||
x152, x153 := bits.add_u64(x128, x143, u64(fiat.u1(x151)))
|
||||
x154, x155 := bits.add_u64(x130, x145, u64(fiat.u1(x153)))
|
||||
x156, x157 := bits.add_u64(x132, x147, u64(fiat.u1(x155)))
|
||||
x158, x159 := bits.add_u64(x134, x149, u64(fiat.u1(x157)))
|
||||
_, x160 := bits.mul_u64(x150, 0xd2b51da312547e1b)
|
||||
x163, x162 := bits.mul_u64(x160, 0x1000000000000000)
|
||||
x165, x164 := bits.mul_u64(x160, 0x14def9dea2f79cd6)
|
||||
x167, x166 := bits.mul_u64(x160, 0x5812631a5cf5d3ed)
|
||||
x168, x169 := bits.add_u64(x167, x164, u64(0x0))
|
||||
x170 := (u64(fiat.u1(x169)) + x165)
|
||||
_, x172 := bits.add_u64(x150, x166, u64(0x0))
|
||||
x173, x174 := bits.add_u64(x152, x168, u64(fiat.u1(x172)))
|
||||
x175, x176 := bits.add_u64(x154, x170, u64(fiat.u1(x174)))
|
||||
x177, x178 := bits.add_u64(x156, x162, u64(fiat.u1(x176)))
|
||||
x179, x180 := bits.add_u64(x158, x163, u64(fiat.u1(x178)))
|
||||
x181 := (u64(fiat.u1(x180)) + u64(fiat.u1(x159)))
|
||||
x182, x183 := bits.sub_u64(x173, 0x5812631a5cf5d3ed, u64(0x0))
|
||||
x184, x185 := bits.sub_u64(x175, 0x14def9dea2f79cd6, u64(fiat.u1(x183)))
|
||||
x186, x187 := bits.sub_u64(x177, u64(0x0), u64(fiat.u1(x185)))
|
||||
x188, x189 := bits.sub_u64(x179, 0x1000000000000000, u64(fiat.u1(x187)))
|
||||
_, x191 := bits.sub_u64(x181, u64(0x0), u64(fiat.u1(x189)))
|
||||
x192 := fiat.cmovznz_u64(fiat.u1(x191), x182, x173)
|
||||
x193 := fiat.cmovznz_u64(fiat.u1(x191), x184, x175)
|
||||
x194 := fiat.cmovznz_u64(fiat.u1(x191), x186, x177)
|
||||
x195 := fiat.cmovznz_u64(fiat.u1(x191), x188, x179)
|
||||
out1[0] = x192
|
||||
out1[1] = x193
|
||||
out1[2] = x194
|
||||
out1[3] = x195
|
||||
}
|
||||
|
||||
fe_add :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.add_u64(arg1[0], arg2[0], u64(0x0))
|
||||
x3, x4 := bits.add_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.add_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.add_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
|
||||
x9, x10 := bits.sub_u64(x1, 0x5812631a5cf5d3ed, u64(0x0))
|
||||
x11, x12 := bits.sub_u64(x3, 0x14def9dea2f79cd6, u64(fiat.u1(x10)))
|
||||
x13, x14 := bits.sub_u64(x5, u64(0x0), u64(fiat.u1(x12)))
|
||||
x15, x16 := bits.sub_u64(x7, 0x1000000000000000, u64(fiat.u1(x14)))
|
||||
_, x18 := bits.sub_u64(u64(fiat.u1(x8)), u64(0x0), u64(fiat.u1(x16)))
|
||||
x19 := fiat.cmovznz_u64(fiat.u1(x18), x9, x1)
|
||||
x20 := fiat.cmovznz_u64(fiat.u1(x18), x11, x3)
|
||||
x21 := fiat.cmovznz_u64(fiat.u1(x18), x13, x5)
|
||||
x22 := fiat.cmovznz_u64(fiat.u1(x18), x15, x7)
|
||||
out1[0] = x19
|
||||
out1[1] = x20
|
||||
out1[2] = x21
|
||||
out1[3] = x22
|
||||
}
|
||||
|
||||
fe_sub :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.sub_u64(arg1[0], arg2[0], u64(0x0))
|
||||
x3, x4 := bits.sub_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.sub_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.sub_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
|
||||
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
|
||||
x10, x11 := bits.add_u64(x1, (x9 & 0x5812631a5cf5d3ed), u64(0x0))
|
||||
x12, x13 := bits.add_u64(x3, (x9 & 0x14def9dea2f79cd6), u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x5, u64(0x0), u64(fiat.u1(x13)))
|
||||
x16, _ := bits.add_u64(x7, (x9 & 0x1000000000000000), u64(fiat.u1(x15)))
|
||||
out1[0] = x10
|
||||
out1[1] = x12
|
||||
out1[2] = x14
|
||||
out1[3] = x16
|
||||
}
|
||||
|
||||
fe_opp :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
|
||||
x1, x2 := bits.sub_u64(u64(0x0), arg1[0], u64(0x0))
|
||||
x3, x4 := bits.sub_u64(u64(0x0), arg1[1], u64(fiat.u1(x2)))
|
||||
x5, x6 := bits.sub_u64(u64(0x0), arg1[2], u64(fiat.u1(x4)))
|
||||
x7, x8 := bits.sub_u64(u64(0x0), arg1[3], u64(fiat.u1(x6)))
|
||||
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
|
||||
x10, x11 := bits.add_u64(x1, (x9 & 0x5812631a5cf5d3ed), u64(0x0))
|
||||
x12, x13 := bits.add_u64(x3, (x9 & 0x14def9dea2f79cd6), u64(fiat.u1(x11)))
|
||||
x14, x15 := bits.add_u64(x5, u64(0x0), u64(fiat.u1(x13)))
|
||||
x16, _ := bits.add_u64(x7, (x9 & 0x1000000000000000), u64(fiat.u1(x15)))
|
||||
out1[0] = x10
|
||||
out1[1] = x12
|
||||
out1[2] = x14
|
||||
out1[3] = x16
|
||||
}
|
||||
|
||||
fe_one :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
|
||||
out1[0] = 0xd6ec31748d98951d
|
||||
out1[1] = 0xc6ef5bf4737dcf70
|
||||
out1[2] = 0xfffffffffffffffe
|
||||
out1[3] = 0xfffffffffffffff
|
||||
}
|
||||
|
||||
fe_non_zero :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> u64 {
|
||||
return arg1[0] | (arg1[1] | (arg1[2] | arg1[3]))
|
||||
}
|
||||
|
||||
@(optimization_mode = "none")
|
||||
fe_cond_assign :: #force_no_inline proc "contextless" (
|
||||
out1, arg1: ^Montgomery_Domain_Field_Element,
|
||||
arg2: int,
|
||||
) {
|
||||
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
|
||||
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
|
||||
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
|
||||
x4 := fiat.cmovznz_u64(fiat.u1(arg2), out1[3], arg1[3])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
}
|
||||
|
||||
fe_from_montgomery :: proc "contextless" (
|
||||
out1: ^Non_Montgomery_Domain_Field_Element,
|
||||
arg1: ^Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
x1 := arg1[0]
|
||||
_, x2 := bits.mul_u64(x1, 0xd2b51da312547e1b)
|
||||
x5, x4 := bits.mul_u64(x2, 0x1000000000000000)
|
||||
x7, x6 := bits.mul_u64(x2, 0x14def9dea2f79cd6)
|
||||
x9, x8 := bits.mul_u64(x2, 0x5812631a5cf5d3ed)
|
||||
x10, x11 := bits.add_u64(x9, x6, u64(0x0))
|
||||
_, x13 := bits.add_u64(x1, x8, u64(0x0))
|
||||
x14, x15 := bits.add_u64(u64(0x0), x10, u64(fiat.u1(x13)))
|
||||
x16, x17 := bits.add_u64(x14, arg1[1], u64(0x0))
|
||||
_, x18 := bits.mul_u64(x16, 0xd2b51da312547e1b)
|
||||
x21, x20 := bits.mul_u64(x18, 0x1000000000000000)
|
||||
x23, x22 := bits.mul_u64(x18, 0x14def9dea2f79cd6)
|
||||
x25, x24 := bits.mul_u64(x18, 0x5812631a5cf5d3ed)
|
||||
x26, x27 := bits.add_u64(x25, x22, u64(0x0))
|
||||
_, x29 := bits.add_u64(x16, x24, u64(0x0))
|
||||
x30, x31 := bits.add_u64(
|
||||
(u64(fiat.u1(x17)) + (u64(fiat.u1(x15)) + (u64(fiat.u1(x11)) + x7))),
|
||||
x26,
|
||||
u64(fiat.u1(x29)),
|
||||
)
|
||||
x32, x33 := bits.add_u64(x4, (u64(fiat.u1(x27)) + x23), u64(fiat.u1(x31)))
|
||||
x34, x35 := bits.add_u64(x5, x20, u64(fiat.u1(x33)))
|
||||
x36, x37 := bits.add_u64(x30, arg1[2], u64(0x0))
|
||||
x38, x39 := bits.add_u64(x32, u64(0x0), u64(fiat.u1(x37)))
|
||||
x40, x41 := bits.add_u64(x34, u64(0x0), u64(fiat.u1(x39)))
|
||||
_, x42 := bits.mul_u64(x36, 0xd2b51da312547e1b)
|
||||
x45, x44 := bits.mul_u64(x42, 0x1000000000000000)
|
||||
x47, x46 := bits.mul_u64(x42, 0x14def9dea2f79cd6)
|
||||
x49, x48 := bits.mul_u64(x42, 0x5812631a5cf5d3ed)
|
||||
x50, x51 := bits.add_u64(x49, x46, u64(0x0))
|
||||
_, x53 := bits.add_u64(x36, x48, u64(0x0))
|
||||
x54, x55 := bits.add_u64(x38, x50, u64(fiat.u1(x53)))
|
||||
x56, x57 := bits.add_u64(x40, (u64(fiat.u1(x51)) + x47), u64(fiat.u1(x55)))
|
||||
x58, x59 := bits.add_u64(
|
||||
(u64(fiat.u1(x41)) + (u64(fiat.u1(x35)) + x21)),
|
||||
x44,
|
||||
u64(fiat.u1(x57)),
|
||||
)
|
||||
x60, x61 := bits.add_u64(x54, arg1[3], u64(0x0))
|
||||
x62, x63 := bits.add_u64(x56, u64(0x0), u64(fiat.u1(x61)))
|
||||
x64, x65 := bits.add_u64(x58, u64(0x0), u64(fiat.u1(x63)))
|
||||
_, x66 := bits.mul_u64(x60, 0xd2b51da312547e1b)
|
||||
x69, x68 := bits.mul_u64(x66, 0x1000000000000000)
|
||||
x71, x70 := bits.mul_u64(x66, 0x14def9dea2f79cd6)
|
||||
x73, x72 := bits.mul_u64(x66, 0x5812631a5cf5d3ed)
|
||||
x74, x75 := bits.add_u64(x73, x70, u64(0x0))
|
||||
_, x77 := bits.add_u64(x60, x72, u64(0x0))
|
||||
x78, x79 := bits.add_u64(x62, x74, u64(fiat.u1(x77)))
|
||||
x80, x81 := bits.add_u64(x64, (u64(fiat.u1(x75)) + x71), u64(fiat.u1(x79)))
|
||||
x82, x83 := bits.add_u64(
|
||||
(u64(fiat.u1(x65)) + (u64(fiat.u1(x59)) + x45)),
|
||||
x68,
|
||||
u64(fiat.u1(x81)),
|
||||
)
|
||||
x84 := (u64(fiat.u1(x83)) + x69)
|
||||
x85, x86 := bits.sub_u64(x78, 0x5812631a5cf5d3ed, u64(0x0))
|
||||
x87, x88 := bits.sub_u64(x80, 0x14def9dea2f79cd6, u64(fiat.u1(x86)))
|
||||
x89, x90 := bits.sub_u64(x82, u64(0x0), u64(fiat.u1(x88)))
|
||||
x91, x92 := bits.sub_u64(x84, 0x1000000000000000, u64(fiat.u1(x90)))
|
||||
_, x94 := bits.sub_u64(u64(0x0), u64(0x0), u64(fiat.u1(x92)))
|
||||
x95 := fiat.cmovznz_u64(fiat.u1(x94), x85, x78)
|
||||
x96 := fiat.cmovznz_u64(fiat.u1(x94), x87, x80)
|
||||
x97 := fiat.cmovznz_u64(fiat.u1(x94), x89, x82)
|
||||
x98 := fiat.cmovznz_u64(fiat.u1(x94), x91, x84)
|
||||
out1[0] = x95
|
||||
out1[1] = x96
|
||||
out1[2] = x97
|
||||
out1[3] = x98
|
||||
}
|
||||
|
||||
fe_to_montgomery :: proc "contextless" (
|
||||
out1: ^Montgomery_Domain_Field_Element,
|
||||
arg1: ^Non_Montgomery_Domain_Field_Element,
|
||||
) {
|
||||
x1 := arg1[1]
|
||||
x2 := arg1[2]
|
||||
x3 := arg1[3]
|
||||
x4 := arg1[0]
|
||||
x6, x5 := bits.mul_u64(x4, 0x399411b7c309a3d)
|
||||
x8, x7 := bits.mul_u64(x4, 0xceec73d217f5be65)
|
||||
x10, x9 := bits.mul_u64(x4, 0xd00e1ba768859347)
|
||||
x12, x11 := bits.mul_u64(x4, 0xa40611e3449c0f01)
|
||||
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
|
||||
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
|
||||
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
|
||||
_, x19 := bits.mul_u64(x11, 0xd2b51da312547e1b)
|
||||
x22, x21 := bits.mul_u64(x19, 0x1000000000000000)
|
||||
x24, x23 := bits.mul_u64(x19, 0x14def9dea2f79cd6)
|
||||
x26, x25 := bits.mul_u64(x19, 0x5812631a5cf5d3ed)
|
||||
x27, x28 := bits.add_u64(x26, x23, u64(0x0))
|
||||
_, x30 := bits.add_u64(x11, x25, u64(0x0))
|
||||
x31, x32 := bits.add_u64(x13, x27, u64(fiat.u1(x30)))
|
||||
x33, x34 := bits.add_u64(x15, (u64(fiat.u1(x28)) + x24), u64(fiat.u1(x32)))
|
||||
x35, x36 := bits.add_u64(x17, x21, u64(fiat.u1(x34)))
|
||||
x38, x37 := bits.mul_u64(x1, 0x399411b7c309a3d)
|
||||
x40, x39 := bits.mul_u64(x1, 0xceec73d217f5be65)
|
||||
x42, x41 := bits.mul_u64(x1, 0xd00e1ba768859347)
|
||||
x44, x43 := bits.mul_u64(x1, 0xa40611e3449c0f01)
|
||||
x45, x46 := bits.add_u64(x44, x41, u64(0x0))
|
||||
x47, x48 := bits.add_u64(x42, x39, u64(fiat.u1(x46)))
|
||||
x49, x50 := bits.add_u64(x40, x37, u64(fiat.u1(x48)))
|
||||
x51, x52 := bits.add_u64(x31, x43, u64(0x0))
|
||||
x53, x54 := bits.add_u64(x33, x45, u64(fiat.u1(x52)))
|
||||
x55, x56 := bits.add_u64(x35, x47, u64(fiat.u1(x54)))
|
||||
x57, x58 := bits.add_u64(
|
||||
((u64(fiat.u1(x36)) + (u64(fiat.u1(x18)) + x6)) + x22),
|
||||
x49,
|
||||
u64(fiat.u1(x56)),
|
||||
)
|
||||
_, x59 := bits.mul_u64(x51, 0xd2b51da312547e1b)
|
||||
x62, x61 := bits.mul_u64(x59, 0x1000000000000000)
|
||||
x64, x63 := bits.mul_u64(x59, 0x14def9dea2f79cd6)
|
||||
x66, x65 := bits.mul_u64(x59, 0x5812631a5cf5d3ed)
|
||||
x67, x68 := bits.add_u64(x66, x63, u64(0x0))
|
||||
_, x70 := bits.add_u64(x51, x65, u64(0x0))
|
||||
x71, x72 := bits.add_u64(x53, x67, u64(fiat.u1(x70)))
|
||||
x73, x74 := bits.add_u64(x55, (u64(fiat.u1(x68)) + x64), u64(fiat.u1(x72)))
|
||||
x75, x76 := bits.add_u64(x57, x61, u64(fiat.u1(x74)))
|
||||
x78, x77 := bits.mul_u64(x2, 0x399411b7c309a3d)
|
||||
x80, x79 := bits.mul_u64(x2, 0xceec73d217f5be65)
|
||||
x82, x81 := bits.mul_u64(x2, 0xd00e1ba768859347)
|
||||
x84, x83 := bits.mul_u64(x2, 0xa40611e3449c0f01)
|
||||
x85, x86 := bits.add_u64(x84, x81, u64(0x0))
|
||||
x87, x88 := bits.add_u64(x82, x79, u64(fiat.u1(x86)))
|
||||
x89, x90 := bits.add_u64(x80, x77, u64(fiat.u1(x88)))
|
||||
x91, x92 := bits.add_u64(x71, x83, u64(0x0))
|
||||
x93, x94 := bits.add_u64(x73, x85, u64(fiat.u1(x92)))
|
||||
x95, x96 := bits.add_u64(x75, x87, u64(fiat.u1(x94)))
|
||||
x97, x98 := bits.add_u64(
|
||||
((u64(fiat.u1(x76)) + (u64(fiat.u1(x58)) + (u64(fiat.u1(x50)) + x38))) + x62),
|
||||
x89,
|
||||
u64(fiat.u1(x96)),
|
||||
)
|
||||
_, x99 := bits.mul_u64(x91, 0xd2b51da312547e1b)
|
||||
x102, x101 := bits.mul_u64(x99, 0x1000000000000000)
|
||||
x104, x103 := bits.mul_u64(x99, 0x14def9dea2f79cd6)
|
||||
x106, x105 := bits.mul_u64(x99, 0x5812631a5cf5d3ed)
|
||||
x107, x108 := bits.add_u64(x106, x103, u64(0x0))
|
||||
_, x110 := bits.add_u64(x91, x105, u64(0x0))
|
||||
x111, x112 := bits.add_u64(x93, x107, u64(fiat.u1(x110)))
|
||||
x113, x114 := bits.add_u64(x95, (u64(fiat.u1(x108)) + x104), u64(fiat.u1(x112)))
|
||||
x115, x116 := bits.add_u64(x97, x101, u64(fiat.u1(x114)))
|
||||
x118, x117 := bits.mul_u64(x3, 0x399411b7c309a3d)
|
||||
x120, x119 := bits.mul_u64(x3, 0xceec73d217f5be65)
|
||||
x122, x121 := bits.mul_u64(x3, 0xd00e1ba768859347)
|
||||
x124, x123 := bits.mul_u64(x3, 0xa40611e3449c0f01)
|
||||
x125, x126 := bits.add_u64(x124, x121, u64(0x0))
|
||||
x127, x128 := bits.add_u64(x122, x119, u64(fiat.u1(x126)))
|
||||
x129, x130 := bits.add_u64(x120, x117, u64(fiat.u1(x128)))
|
||||
x131, x132 := bits.add_u64(x111, x123, u64(0x0))
|
||||
x133, x134 := bits.add_u64(x113, x125, u64(fiat.u1(x132)))
|
||||
x135, x136 := bits.add_u64(x115, x127, u64(fiat.u1(x134)))
|
||||
x137, x138 := bits.add_u64(
|
||||
((u64(fiat.u1(x116)) + (u64(fiat.u1(x98)) + (u64(fiat.u1(x90)) + x78))) + x102),
|
||||
x129,
|
||||
u64(fiat.u1(x136)),
|
||||
)
|
||||
_, x139 := bits.mul_u64(x131, 0xd2b51da312547e1b)
|
||||
x142, x141 := bits.mul_u64(x139, 0x1000000000000000)
|
||||
x144, x143 := bits.mul_u64(x139, 0x14def9dea2f79cd6)
|
||||
x146, x145 := bits.mul_u64(x139, 0x5812631a5cf5d3ed)
|
||||
x147, x148 := bits.add_u64(x146, x143, u64(0x0))
|
||||
_, x150 := bits.add_u64(x131, x145, u64(0x0))
|
||||
x151, x152 := bits.add_u64(x133, x147, u64(fiat.u1(x150)))
|
||||
x153, x154 := bits.add_u64(x135, (u64(fiat.u1(x148)) + x144), u64(fiat.u1(x152)))
|
||||
x155, x156 := bits.add_u64(x137, x141, u64(fiat.u1(x154)))
|
||||
x157 := ((u64(fiat.u1(x156)) + (u64(fiat.u1(x138)) + (u64(fiat.u1(x130)) + x118))) + x142)
|
||||
x158, x159 := bits.sub_u64(x151, 0x5812631a5cf5d3ed, u64(0x0))
|
||||
x160, x161 := bits.sub_u64(x153, 0x14def9dea2f79cd6, u64(fiat.u1(x159)))
|
||||
x162, x163 := bits.sub_u64(x155, u64(0x0), u64(fiat.u1(x161)))
|
||||
x164, x165 := bits.sub_u64(x157, 0x1000000000000000, u64(fiat.u1(x163)))
|
||||
_, x167 := bits.sub_u64(u64(0x0), u64(0x0), u64(fiat.u1(x165)))
|
||||
x168 := fiat.cmovznz_u64(fiat.u1(x167), x158, x151)
|
||||
x169 := fiat.cmovznz_u64(fiat.u1(x167), x160, x153)
|
||||
x170 := fiat.cmovznz_u64(fiat.u1(x167), x162, x155)
|
||||
x171 := fiat.cmovznz_u64(fiat.u1(x167), x164, x157)
|
||||
out1[0] = x168
|
||||
out1[1] = x169
|
||||
out1[2] = x170
|
||||
out1[3] = x171
|
||||
}
|
||||
+179
-134
@@ -7,163 +7,208 @@ package _sha3
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the Keccak hashing algorithm, standardized as SHA3 in <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf>
|
||||
To use the original Keccak padding, set the is_keccak bool to true, otherwise it will use SHA3 padding.
|
||||
Implementation of the Keccak hashing algorithm, standardized as SHA3
|
||||
in <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf>.
|
||||
|
||||
As the only difference between the legacy Keccak and SHA3 is the domain
|
||||
separation byte, set dsbyte to the appropriate value to pick the desired
|
||||
algorithm.
|
||||
*/
|
||||
|
||||
import "../util"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
ROUNDS :: 24
|
||||
|
||||
Sha3_Context :: struct {
|
||||
st: struct #raw_union {
|
||||
b: [200]u8,
|
||||
q: [25]u64,
|
||||
},
|
||||
pt: int,
|
||||
rsiz: int,
|
||||
mdlen: int,
|
||||
is_keccak: bool,
|
||||
RATE_128 :: 1344 / 8 // ONLY for SHAKE128.
|
||||
RATE_224 :: 1152 / 8
|
||||
RATE_256 :: 1088 / 8
|
||||
RATE_384 :: 832 / 8
|
||||
RATE_512 :: 576 / 8
|
||||
|
||||
DS_KECCAK :: 0x01
|
||||
DS_SHA3 :: 0x06
|
||||
DS_SHAKE :: 0x1f
|
||||
DS_CSHAKE :: 0x04
|
||||
|
||||
Context :: struct {
|
||||
st: struct #raw_union {
|
||||
b: [200]u8,
|
||||
q: [25]u64,
|
||||
},
|
||||
pt: int,
|
||||
rsiz: int,
|
||||
mdlen: int,
|
||||
dsbyte: byte,
|
||||
is_initialized: bool,
|
||||
is_finalized: bool, // For SHAKE (unlimited squeeze is allowed)
|
||||
}
|
||||
|
||||
@(private)
|
||||
keccakf_rndc := [?]u64 {
|
||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
||||
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
||||
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
|
||||
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
||||
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
|
||||
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
|
||||
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
||||
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
||||
}
|
||||
|
||||
@(private)
|
||||
keccakf_rotc := [?]int {
|
||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
||||
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
||||
}
|
||||
|
||||
@(private)
|
||||
keccakf_piln := [?]i32 {
|
||||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
||||
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
||||
}
|
||||
|
||||
@(private)
|
||||
keccakf :: proc "contextless" (st: ^[25]u64) {
|
||||
keccakf_rndc := [?]u64 {
|
||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
||||
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
||||
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
|
||||
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
||||
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
|
||||
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
|
||||
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
||||
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
||||
}
|
||||
i, j, r: i32 = ---, ---, ---
|
||||
t: u64 = ---
|
||||
bc: [5]u64 = ---
|
||||
|
||||
keccakf_rotc := [?]i32 {
|
||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
||||
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
||||
}
|
||||
when ODIN_ENDIAN != .Little {
|
||||
for i = 0; i < 25; i += 1 {
|
||||
st[i] = bits.byte_swap(st[i])
|
||||
}
|
||||
}
|
||||
|
||||
keccakf_piln := [?]i32 {
|
||||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
||||
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
||||
}
|
||||
for r = 0; r < ROUNDS; r += 1 {
|
||||
// theta
|
||||
for i = 0; i < 5; i += 1 {
|
||||
bc[i] = st[i] ~ st[i + 5] ~ st[i + 10] ~ st[i + 15] ~ st[i + 20]
|
||||
}
|
||||
|
||||
i, j, r: i32 = ---, ---, ---
|
||||
t: u64 = ---
|
||||
bc: [5]u64 = ---
|
||||
for i = 0; i < 5; i += 1 {
|
||||
t = bc[(i + 4) % 5] ~ bits.rotate_left64(bc[(i + 1) % 5], 1)
|
||||
for j = 0; j < 25; j += 5 {
|
||||
st[j + i] ~= t
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_ENDIAN != .Little {
|
||||
v: uintptr = ---
|
||||
for i = 0; i < 25; i += 1 {
|
||||
v := uintptr(&st[i])
|
||||
st[i] = u64((^u8)(v + 0)^ << 0) | u64((^u8)(v + 1)^ << 8) |
|
||||
u64((^u8)(v + 2)^ << 16) | u64((^u8)(v + 3)^ << 24) |
|
||||
u64((^u8)(v + 4)^ << 32) | u64((^u8)(v + 5)^ << 40) |
|
||||
u64((^u8)(v + 6)^ << 48) | u64((^u8)(v + 7)^ << 56)
|
||||
}
|
||||
}
|
||||
// rho pi
|
||||
t = st[1]
|
||||
for i = 0; i < 24; i += 1 {
|
||||
j = keccakf_piln[i]
|
||||
bc[0] = st[j]
|
||||
st[j] = bits.rotate_left64(t, keccakf_rotc[i])
|
||||
t = bc[0]
|
||||
}
|
||||
|
||||
for r = 0; r < ROUNDS; r += 1 {
|
||||
// theta
|
||||
for i = 0; i < 5; i += 1 {
|
||||
bc[i] = st[i] ~ st[i + 5] ~ st[i + 10] ~ st[i + 15] ~ st[i + 20]
|
||||
}
|
||||
// chi
|
||||
for j = 0; j < 25; j += 5 {
|
||||
for i = 0; i < 5; i += 1 {
|
||||
bc[i] = st[j + i]
|
||||
}
|
||||
for i = 0; i < 5; i += 1 {
|
||||
st[j + i] ~= ~bc[(i + 1) % 5] & bc[(i + 2) % 5]
|
||||
}
|
||||
}
|
||||
|
||||
for i = 0; i < 5; i += 1 {
|
||||
t = bc[(i + 4) % 5] ~ util.ROTL64(bc[(i + 1) % 5], 1)
|
||||
for j = 0; j < 25; j += 5 {
|
||||
st[j + i] ~= t
|
||||
}
|
||||
}
|
||||
st[0] ~= keccakf_rndc[r]
|
||||
}
|
||||
|
||||
// rho pi
|
||||
t = st[1]
|
||||
for i = 0; i < 24; i += 1 {
|
||||
j = keccakf_piln[i]
|
||||
bc[0] = st[j]
|
||||
st[j] = util.ROTL64(t, u64(keccakf_rotc[i]))
|
||||
t = bc[0]
|
||||
}
|
||||
|
||||
// chi
|
||||
for j = 0; j < 25; j += 5 {
|
||||
for i = 0; i < 5; i += 1 {
|
||||
bc[i] = st[j + i]
|
||||
}
|
||||
for i = 0; i < 5; i += 1 {
|
||||
st[j + i] ~= ~bc[(i + 1) % 5] & bc[(i + 2) % 5]
|
||||
}
|
||||
}
|
||||
|
||||
st[0] ~= keccakf_rndc[r]
|
||||
}
|
||||
|
||||
when ODIN_ENDIAN != .Little {
|
||||
for i = 0; i < 25; i += 1 {
|
||||
v = uintptr(&st[i])
|
||||
t = st[i]
|
||||
(^u8)(v + 0)^ = (t >> 0) & 0xff
|
||||
(^u8)(v + 1)^ = (t >> 8) & 0xff
|
||||
(^u8)(v + 2)^ = (t >> 16) & 0xff
|
||||
(^u8)(v + 3)^ = (t >> 24) & 0xff
|
||||
(^u8)(v + 4)^ = (t >> 32) & 0xff
|
||||
(^u8)(v + 5)^ = (t >> 40) & 0xff
|
||||
(^u8)(v + 6)^ = (t >> 48) & 0xff
|
||||
(^u8)(v + 7)^ = (t >> 56) & 0xff
|
||||
}
|
||||
}
|
||||
when ODIN_ENDIAN != .Little {
|
||||
for i = 0; i < 25; i += 1 {
|
||||
st[i] = bits.byte_swap(st[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init :: proc "contextless" (c: ^Sha3_Context) {
|
||||
for i := 0; i < 25; i += 1 {
|
||||
c.st.q[i] = 0
|
||||
}
|
||||
c.rsiz = 200 - 2 * c.mdlen
|
||||
init :: proc(ctx: ^Context) {
|
||||
for i := 0; i < 25; i += 1 {
|
||||
ctx.st.q[i] = 0
|
||||
}
|
||||
ctx.rsiz = 200 - 2 * ctx.mdlen
|
||||
ctx.pt = 0
|
||||
|
||||
ctx.is_initialized = true
|
||||
ctx.is_finalized = false
|
||||
}
|
||||
|
||||
update :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
|
||||
j := c.pt
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
c.st.b[j] ~= data[i]
|
||||
j += 1
|
||||
if j >= c.rsiz {
|
||||
keccakf(&c.st.q)
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
c.pt = j
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx.is_initialized)
|
||||
assert(!ctx.is_finalized)
|
||||
|
||||
j := ctx.pt
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.st.b[j] ~= data[i]
|
||||
j += 1
|
||||
if j >= ctx.rsiz {
|
||||
keccakf(&ctx.st.q)
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
ctx.pt = j
|
||||
}
|
||||
|
||||
final :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
if c.is_keccak {
|
||||
c.st.b[c.pt] ~= 0x01
|
||||
} else {
|
||||
c.st.b[c.pt] ~= 0x06
|
||||
}
|
||||
|
||||
c.st.b[c.rsiz - 1] ~= 0x80
|
||||
keccakf(&c.st.q)
|
||||
for i := 0; i < c.mdlen; i += 1 {
|
||||
hash[i] = c.st.b[i]
|
||||
}
|
||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
if len(hash) < ctx.mdlen {
|
||||
panic("crypto/sha3: invalid destination digest size")
|
||||
}
|
||||
|
||||
ctx := ctx
|
||||
if finalize_clone {
|
||||
tmp_ctx: Context
|
||||
clone(&tmp_ctx, ctx)
|
||||
ctx = &tmp_ctx
|
||||
}
|
||||
defer (reset(ctx))
|
||||
|
||||
ctx.st.b[ctx.pt] ~= ctx.dsbyte
|
||||
|
||||
ctx.st.b[ctx.rsiz - 1] ~= 0x80
|
||||
keccakf(&ctx.st.q)
|
||||
for i := 0; i < ctx.mdlen; i += 1 {
|
||||
hash[i] = ctx.st.b[i]
|
||||
}
|
||||
}
|
||||
|
||||
shake_xof :: proc "contextless" (c: ^Sha3_Context) {
|
||||
c.st.b[c.pt] ~= 0x1F
|
||||
c.st.b[c.rsiz - 1] ~= 0x80
|
||||
keccakf(&c.st.q)
|
||||
c.pt = 0
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
ctx^ = other^
|
||||
}
|
||||
|
||||
shake_out :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
j := c.pt
|
||||
for i := 0; i < len(hash); i += 1 {
|
||||
if j >= c.rsiz {
|
||||
keccakf(&c.st.q)
|
||||
j = 0
|
||||
}
|
||||
hash[i] = c.st.b[j]
|
||||
j += 1
|
||||
}
|
||||
c.pt = j
|
||||
reset :: proc(ctx: ^Context) {
|
||||
if !ctx.is_initialized {
|
||||
return
|
||||
}
|
||||
|
||||
mem.zero_explicit(ctx, size_of(ctx^))
|
||||
}
|
||||
|
||||
shake_xof :: proc(ctx: ^Context) {
|
||||
assert(ctx.is_initialized)
|
||||
assert(!ctx.is_finalized)
|
||||
|
||||
ctx.st.b[ctx.pt] ~= ctx.dsbyte
|
||||
ctx.st.b[ctx.rsiz - 1] ~= 0x80
|
||||
keccakf(&ctx.st.q)
|
||||
ctx.pt = 0
|
||||
|
||||
ctx.is_finalized = true // No more absorb, unlimited squeeze.
|
||||
}
|
||||
|
||||
shake_out :: proc(ctx: ^Context, hash: []byte) {
|
||||
assert(ctx.is_initialized)
|
||||
assert(ctx.is_finalized)
|
||||
|
||||
j := ctx.pt
|
||||
for i := 0; i < len(hash); i += 1 {
|
||||
if j >= ctx.rsiz {
|
||||
keccakf(&ctx.st.q)
|
||||
j = 0
|
||||
}
|
||||
hash[i] = ctx.st.b[j]
|
||||
j += 1
|
||||
}
|
||||
ctx.pt = j
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package _sha3
|
||||
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
|
||||
init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
|
||||
ctx.mdlen = sec_strength / 8
|
||||
|
||||
// No domain separator is equivalent to vanilla SHAKE.
|
||||
if len(n) == 0 && len(s) == 0 {
|
||||
ctx.dsbyte = DS_SHAKE
|
||||
init(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.dsbyte = DS_CSHAKE
|
||||
init(ctx)
|
||||
bytepad(ctx, [][]byte{n, s}, rate_cshake(sec_strength))
|
||||
}
|
||||
|
||||
final_cshake :: proc(ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
|
||||
ctx := ctx
|
||||
if finalize_clone {
|
||||
tmp_ctx: Context
|
||||
clone(&tmp_ctx, ctx)
|
||||
ctx = &tmp_ctx
|
||||
}
|
||||
defer reset(ctx)
|
||||
|
||||
encode_byte_len(ctx, len(dst), false) // right_encode
|
||||
shake_xof(ctx)
|
||||
shake_out(ctx, dst)
|
||||
}
|
||||
|
||||
rate_cshake :: #force_inline proc(sec_strength: int) -> int {
|
||||
switch sec_strength {
|
||||
case 128:
|
||||
return RATE_128
|
||||
case 256:
|
||||
return RATE_256
|
||||
}
|
||||
|
||||
panic("crypto/sha3: invalid security strength")
|
||||
}
|
||||
|
||||
// right_encode and left_encode are defined to support 0 <= x < 2^2040
|
||||
// however, the largest value we will ever need to encode is `max(int) * 8`.
|
||||
//
|
||||
// This is unfortunate as the extreme upper edge is larger than
|
||||
// `max(u64)`. While such values are impractical at present,
|
||||
// they are possible (ie: https://arxiv.org/pdf/quant-ph/9908043.pdf).
|
||||
//
|
||||
// Thus we support 0 <= x < 2^128.
|
||||
|
||||
@(private)
|
||||
_PAD: [RATE_128]byte // Biggest possible value of w per spec.
|
||||
|
||||
bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
|
||||
// 1. z = left_encode(w) || X.
|
||||
z_hi: u64
|
||||
z_lo := left_right_encode(ctx, 0, u64(w), true)
|
||||
for x in x_strings {
|
||||
// All uses of bytepad in SP 800-185 use the output from
|
||||
// one or more encode_string values for `X`.
|
||||
hi, lo := encode_string(ctx, x)
|
||||
|
||||
carry: u64
|
||||
z_lo, carry = bits.add_u64(z_lo, lo, 0)
|
||||
z_hi, carry = bits.add_u64(z_hi, hi, carry)
|
||||
|
||||
// This isn't actually possible, at least with the currently
|
||||
// defined SP 800-185 routines.
|
||||
if carry != 0 {
|
||||
panic("crypto/sha3: bytepad input length overflow")
|
||||
}
|
||||
}
|
||||
|
||||
// We skip this step as we are doing a byte-oriented implementation
|
||||
// rather than a bit oriented one.
|
||||
//
|
||||
// 2. while len(z) mod 8 ≠ 0:
|
||||
// z = z || 0
|
||||
|
||||
// 3. while (len(z)/8) mod w ≠ 0:
|
||||
// z = z || 00000000
|
||||
z_len := u128(z_hi) << 64 | u128(z_lo)
|
||||
z_rem := int(z_len % u128(w))
|
||||
pad := _PAD[:w - z_rem]
|
||||
|
||||
// We just add the padding to the state, instead of returning z.
|
||||
//
|
||||
// 4. return z.
|
||||
update(ctx, pad)
|
||||
}
|
||||
|
||||
encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
|
||||
l := encode_byte_len(ctx, len(s), true) // left_encode
|
||||
update(ctx, s)
|
||||
|
||||
lo, hi := bits.add_u64(l, u64(len(s)), 0)
|
||||
|
||||
return hi, lo
|
||||
}
|
||||
|
||||
encode_byte_len :: #force_inline proc(ctx: ^Context, l: int, is_left: bool) -> u64 {
|
||||
hi, lo := bits.mul_u64(u64(l), 8)
|
||||
return left_right_encode(ctx, hi, lo, is_left)
|
||||
}
|
||||
|
||||
@(private)
|
||||
left_right_encode :: proc(ctx: ^Context, hi, lo: u64, is_left: bool) -> u64 {
|
||||
HI_OFFSET :: 1
|
||||
LO_OFFSET :: HI_OFFSET + 8
|
||||
RIGHT_OFFSET :: LO_OFFSET + 8
|
||||
BUF_LEN :: RIGHT_OFFSET + 1
|
||||
|
||||
buf: [BUF_LEN]byte // prefix + largest uint + postfix
|
||||
|
||||
endian.unchecked_put_u64be(buf[HI_OFFSET:], hi)
|
||||
endian.unchecked_put_u64be(buf[LO_OFFSET:], lo)
|
||||
|
||||
// 2. Strip leading `0x00` bytes.
|
||||
off: int
|
||||
for off = HI_OFFSET; off < RIGHT_OFFSET - 1; off = off + 1 {// Note: Minimum size is 1, not 0.
|
||||
if buf[off] != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
n := byte(RIGHT_OFFSET - off)
|
||||
|
||||
// 3. Prefix (left_encode) or postfix (right_encode) the length in bytes.
|
||||
b: []byte
|
||||
switch is_left {
|
||||
case true:
|
||||
buf[off - 1] = n // n | x
|
||||
b = buf[off - 1:RIGHT_OFFSET]
|
||||
case false:
|
||||
buf[RIGHT_OFFSET] = n // x | n
|
||||
b = buf[off:]
|
||||
}
|
||||
|
||||
update(ctx, b)
|
||||
|
||||
return u64(len(b))
|
||||
}
|
||||
@@ -1,410 +0,0 @@
|
||||
package _tiger
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the Tiger hashing algorithm, as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
||||
*/
|
||||
|
||||
import "../util"
|
||||
|
||||
T1 := [?]u64 {
|
||||
0x02aab17cf7e90c5e, 0xac424b03e243a8ec, 0x72cd5be30dd5fcd3, 0x6d019b93f6f97f3a,
|
||||
0xcd9978ffd21f9193, 0x7573a1c9708029e2, 0xb164326b922a83c3, 0x46883eee04915870,
|
||||
0xeaace3057103ece6, 0xc54169b808a3535c, 0x4ce754918ddec47c, 0x0aa2f4dfdc0df40c,
|
||||
0x10b76f18a74dbefa, 0xc6ccb6235ad1ab6a, 0x13726121572fe2ff, 0x1a488c6f199d921e,
|
||||
0x4bc9f9f4da0007ca, 0x26f5e6f6e85241c7, 0x859079dbea5947b6, 0x4f1885c5c99e8c92,
|
||||
0xd78e761ea96f864b, 0x8e36428c52b5c17d, 0x69cf6827373063c1, 0xb607c93d9bb4c56e,
|
||||
0x7d820e760e76b5ea, 0x645c9cc6f07fdc42, 0xbf38a078243342e0, 0x5f6b343c9d2e7d04,
|
||||
0xf2c28aeb600b0ec6, 0x6c0ed85f7254bcac, 0x71592281a4db4fe5, 0x1967fa69ce0fed9f,
|
||||
0xfd5293f8b96545db, 0xc879e9d7f2a7600b, 0x860248920193194e, 0xa4f9533b2d9cc0b3,
|
||||
0x9053836c15957613, 0xdb6dcf8afc357bf1, 0x18beea7a7a370f57, 0x037117ca50b99066,
|
||||
0x6ab30a9774424a35, 0xf4e92f02e325249b, 0x7739db07061ccae1, 0xd8f3b49ceca42a05,
|
||||
0xbd56be3f51382f73, 0x45faed5843b0bb28, 0x1c813d5c11bf1f83, 0x8af0e4b6d75fa169,
|
||||
0x33ee18a487ad9999, 0x3c26e8eab1c94410, 0xb510102bc0a822f9, 0x141eef310ce6123b,
|
||||
0xfc65b90059ddb154, 0xe0158640c5e0e607, 0x884e079826c3a3cf, 0x930d0d9523c535fd,
|
||||
0x35638d754e9a2b00, 0x4085fccf40469dd5, 0xc4b17ad28be23a4c, 0xcab2f0fc6a3e6a2e,
|
||||
0x2860971a6b943fcd, 0x3dde6ee212e30446, 0x6222f32ae01765ae, 0x5d550bb5478308fe,
|
||||
0xa9efa98da0eda22a, 0xc351a71686c40da7, 0x1105586d9c867c84, 0xdcffee85fda22853,
|
||||
0xccfbd0262c5eef76, 0xbaf294cb8990d201, 0xe69464f52afad975, 0x94b013afdf133e14,
|
||||
0x06a7d1a32823c958, 0x6f95fe5130f61119, 0xd92ab34e462c06c0, 0xed7bde33887c71d2,
|
||||
0x79746d6e6518393e, 0x5ba419385d713329, 0x7c1ba6b948a97564, 0x31987c197bfdac67,
|
||||
0xde6c23c44b053d02, 0x581c49fed002d64d, 0xdd474d6338261571, 0xaa4546c3e473d062,
|
||||
0x928fce349455f860, 0x48161bbacaab94d9, 0x63912430770e6f68, 0x6ec8a5e602c6641c,
|
||||
0x87282515337ddd2b, 0x2cda6b42034b701b, 0xb03d37c181cb096d, 0xe108438266c71c6f,
|
||||
0x2b3180c7eb51b255, 0xdf92b82f96c08bbc, 0x5c68c8c0a632f3ba, 0x5504cc861c3d0556,
|
||||
0xabbfa4e55fb26b8f, 0x41848b0ab3baceb4, 0xb334a273aa445d32, 0xbca696f0a85ad881,
|
||||
0x24f6ec65b528d56c, 0x0ce1512e90f4524a, 0x4e9dd79d5506d35a, 0x258905fac6ce9779,
|
||||
0x2019295b3e109b33, 0xf8a9478b73a054cc, 0x2924f2f934417eb0, 0x3993357d536d1bc4,
|
||||
0x38a81ac21db6ff8b, 0x47c4fbf17d6016bf, 0x1e0faadd7667e3f5, 0x7abcff62938beb96,
|
||||
0xa78dad948fc179c9, 0x8f1f98b72911e50d, 0x61e48eae27121a91, 0x4d62f7ad31859808,
|
||||
0xeceba345ef5ceaeb, 0xf5ceb25ebc9684ce, 0xf633e20cb7f76221, 0xa32cdf06ab8293e4,
|
||||
0x985a202ca5ee2ca4, 0xcf0b8447cc8a8fb1, 0x9f765244979859a3, 0xa8d516b1a1240017,
|
||||
0x0bd7ba3ebb5dc726, 0xe54bca55b86adb39, 0x1d7a3afd6c478063, 0x519ec608e7669edd,
|
||||
0x0e5715a2d149aa23, 0x177d4571848ff194, 0xeeb55f3241014c22, 0x0f5e5ca13a6e2ec2,
|
||||
0x8029927b75f5c361, 0xad139fabc3d6e436, 0x0d5df1a94ccf402f, 0x3e8bd948bea5dfc8,
|
||||
0xa5a0d357bd3ff77e, 0xa2d12e251f74f645, 0x66fd9e525e81a082, 0x2e0c90ce7f687a49,
|
||||
0xc2e8bcbeba973bc5, 0x000001bce509745f, 0x423777bbe6dab3d6, 0xd1661c7eaef06eb5,
|
||||
0xa1781f354daacfd8, 0x2d11284a2b16affc, 0xf1fc4f67fa891d1f, 0x73ecc25dcb920ada,
|
||||
0xae610c22c2a12651, 0x96e0a810d356b78a, 0x5a9a381f2fe7870f, 0xd5ad62ede94e5530,
|
||||
0xd225e5e8368d1427, 0x65977b70c7af4631, 0x99f889b2de39d74f, 0x233f30bf54e1d143,
|
||||
0x9a9675d3d9a63c97, 0x5470554ff334f9a8, 0x166acb744a4f5688, 0x70c74caab2e4aead,
|
||||
0xf0d091646f294d12, 0x57b82a89684031d1, 0xefd95a5a61be0b6b, 0x2fbd12e969f2f29a,
|
||||
0x9bd37013feff9fe8, 0x3f9b0404d6085a06, 0x4940c1f3166cfe15, 0x09542c4dcdf3defb,
|
||||
0xb4c5218385cd5ce3, 0xc935b7dc4462a641, 0x3417f8a68ed3b63f, 0xb80959295b215b40,
|
||||
0xf99cdaef3b8c8572, 0x018c0614f8fcb95d, 0x1b14accd1a3acdf3, 0x84d471f200bb732d,
|
||||
0xc1a3110e95e8da16, 0x430a7220bf1a82b8, 0xb77e090d39df210e, 0x5ef4bd9f3cd05e9d,
|
||||
0x9d4ff6da7e57a444, 0xda1d60e183d4a5f8, 0xb287c38417998e47, 0xfe3edc121bb31886,
|
||||
0xc7fe3ccc980ccbef, 0xe46fb590189bfd03, 0x3732fd469a4c57dc, 0x7ef700a07cf1ad65,
|
||||
0x59c64468a31d8859, 0x762fb0b4d45b61f6, 0x155baed099047718, 0x68755e4c3d50baa6,
|
||||
0xe9214e7f22d8b4df, 0x2addbf532eac95f4, 0x32ae3909b4bd0109, 0x834df537b08e3450,
|
||||
0xfa209da84220728d, 0x9e691d9b9efe23f7, 0x0446d288c4ae8d7f, 0x7b4cc524e169785b,
|
||||
0x21d87f0135ca1385, 0xcebb400f137b8aa5, 0x272e2b66580796be, 0x3612264125c2b0de,
|
||||
0x057702bdad1efbb2, 0xd4babb8eacf84be9, 0x91583139641bc67b, 0x8bdc2de08036e024,
|
||||
0x603c8156f49f68ed, 0xf7d236f7dbef5111, 0x9727c4598ad21e80, 0xa08a0896670a5fd7,
|
||||
0xcb4a8f4309eba9cb, 0x81af564b0f7036a1, 0xc0b99aa778199abd, 0x959f1ec83fc8e952,
|
||||
0x8c505077794a81b9, 0x3acaaf8f056338f0, 0x07b43f50627a6778, 0x4a44ab49f5eccc77,
|
||||
0x3bc3d6e4b679ee98, 0x9cc0d4d1cf14108c, 0x4406c00b206bc8a0, 0x82a18854c8d72d89,
|
||||
0x67e366b35c3c432c, 0xb923dd61102b37f2, 0x56ab2779d884271d, 0xbe83e1b0ff1525af,
|
||||
0xfb7c65d4217e49a9, 0x6bdbe0e76d48e7d4, 0x08df828745d9179e, 0x22ea6a9add53bd34,
|
||||
0xe36e141c5622200a, 0x7f805d1b8cb750ee, 0xafe5c7a59f58e837, 0xe27f996a4fb1c23c,
|
||||
0xd3867dfb0775f0d0, 0xd0e673de6e88891a, 0x123aeb9eafb86c25, 0x30f1d5d5c145b895,
|
||||
0xbb434a2dee7269e7, 0x78cb67ecf931fa38, 0xf33b0372323bbf9c, 0x52d66336fb279c74,
|
||||
0x505f33ac0afb4eaa, 0xe8a5cd99a2cce187, 0x534974801e2d30bb, 0x8d2d5711d5876d90,
|
||||
0x1f1a412891bc038e, 0xd6e2e71d82e56648, 0x74036c3a497732b7, 0x89b67ed96361f5ab,
|
||||
0xffed95d8f1ea02a2, 0xe72b3bd61464d43d, 0xa6300f170bdc4820, 0xebc18760ed78a77a,
|
||||
}
|
||||
|
||||
T2 := [?]u64 {
|
||||
0xe6a6be5a05a12138, 0xb5a122a5b4f87c98, 0x563c6089140b6990, 0x4c46cb2e391f5dd5,
|
||||
0xd932addbc9b79434, 0x08ea70e42015aff5, 0xd765a6673e478cf1, 0xc4fb757eab278d99,
|
||||
0xdf11c6862d6e0692, 0xddeb84f10d7f3b16, 0x6f2ef604a665ea04, 0x4a8e0f0ff0e0dfb3,
|
||||
0xa5edeef83dbcba51, 0xfc4f0a2a0ea4371e, 0xe83e1da85cb38429, 0xdc8ff882ba1b1ce2,
|
||||
0xcd45505e8353e80d, 0x18d19a00d4db0717, 0x34a0cfeda5f38101, 0x0be77e518887caf2,
|
||||
0x1e341438b3c45136, 0xe05797f49089ccf9, 0xffd23f9df2591d14, 0x543dda228595c5cd,
|
||||
0x661f81fd99052a33, 0x8736e641db0f7b76, 0x15227725418e5307, 0xe25f7f46162eb2fa,
|
||||
0x48a8b2126c13d9fe, 0xafdc541792e76eea, 0x03d912bfc6d1898f, 0x31b1aafa1b83f51b,
|
||||
0xf1ac2796e42ab7d9, 0x40a3a7d7fcd2ebac, 0x1056136d0afbbcc5, 0x7889e1dd9a6d0c85,
|
||||
0xd33525782a7974aa, 0xa7e25d09078ac09b, 0xbd4138b3eac6edd0, 0x920abfbe71eb9e70,
|
||||
0xa2a5d0f54fc2625c, 0xc054e36b0b1290a3, 0xf6dd59ff62fe932b, 0x3537354511a8ac7d,
|
||||
0xca845e9172fadcd4, 0x84f82b60329d20dc, 0x79c62ce1cd672f18, 0x8b09a2add124642c,
|
||||
0xd0c1e96a19d9e726, 0x5a786a9b4ba9500c, 0x0e020336634c43f3, 0xc17b474aeb66d822,
|
||||
0x6a731ae3ec9baac2, 0x8226667ae0840258, 0x67d4567691caeca5, 0x1d94155c4875adb5,
|
||||
0x6d00fd985b813fdf, 0x51286efcb774cd06, 0x5e8834471fa744af, 0xf72ca0aee761ae2e,
|
||||
0xbe40e4cdaee8e09a, 0xe9970bbb5118f665, 0x726e4beb33df1964, 0x703b000729199762,
|
||||
0x4631d816f5ef30a7, 0xb880b5b51504a6be, 0x641793c37ed84b6c, 0x7b21ed77f6e97d96,
|
||||
0x776306312ef96b73, 0xae528948e86ff3f4, 0x53dbd7f286a3f8f8, 0x16cadce74cfc1063,
|
||||
0x005c19bdfa52c6dd, 0x68868f5d64d46ad3, 0x3a9d512ccf1e186a, 0x367e62c2385660ae,
|
||||
0xe359e7ea77dcb1d7, 0x526c0773749abe6e, 0x735ae5f9d09f734b, 0x493fc7cc8a558ba8,
|
||||
0xb0b9c1533041ab45, 0x321958ba470a59bd, 0x852db00b5f46c393, 0x91209b2bd336b0e5,
|
||||
0x6e604f7d659ef19f, 0xb99a8ae2782ccb24, 0xccf52ab6c814c4c7, 0x4727d9afbe11727b,
|
||||
0x7e950d0c0121b34d, 0x756f435670ad471f, 0xf5add442615a6849, 0x4e87e09980b9957a,
|
||||
0x2acfa1df50aee355, 0xd898263afd2fd556, 0xc8f4924dd80c8fd6, 0xcf99ca3d754a173a,
|
||||
0xfe477bacaf91bf3c, 0xed5371f6d690c12d, 0x831a5c285e687094, 0xc5d3c90a3708a0a4,
|
||||
0x0f7f903717d06580, 0x19f9bb13b8fdf27f, 0xb1bd6f1b4d502843, 0x1c761ba38fff4012,
|
||||
0x0d1530c4e2e21f3b, 0x8943ce69a7372c8a, 0xe5184e11feb5ce66, 0x618bdb80bd736621,
|
||||
0x7d29bad68b574d0b, 0x81bb613e25e6fe5b, 0x071c9c10bc07913f, 0xc7beeb7909ac2d97,
|
||||
0xc3e58d353bc5d757, 0xeb017892f38f61e8, 0xd4effb9c9b1cc21a, 0x99727d26f494f7ab,
|
||||
0xa3e063a2956b3e03, 0x9d4a8b9a4aa09c30, 0x3f6ab7d500090fb4, 0x9cc0f2a057268ac0,
|
||||
0x3dee9d2dedbf42d1, 0x330f49c87960a972, 0xc6b2720287421b41, 0x0ac59ec07c00369c,
|
||||
0xef4eac49cb353425, 0xf450244eef0129d8, 0x8acc46e5caf4deb6, 0x2ffeab63989263f7,
|
||||
0x8f7cb9fe5d7a4578, 0x5bd8f7644e634635, 0x427a7315bf2dc900, 0x17d0c4aa2125261c,
|
||||
0x3992486c93518e50, 0xb4cbfee0a2d7d4c3, 0x7c75d6202c5ddd8d, 0xdbc295d8e35b6c61,
|
||||
0x60b369d302032b19, 0xce42685fdce44132, 0x06f3ddb9ddf65610, 0x8ea4d21db5e148f0,
|
||||
0x20b0fce62fcd496f, 0x2c1b912358b0ee31, 0xb28317b818f5a308, 0xa89c1e189ca6d2cf,
|
||||
0x0c6b18576aaadbc8, 0xb65deaa91299fae3, 0xfb2b794b7f1027e7, 0x04e4317f443b5beb,
|
||||
0x4b852d325939d0a6, 0xd5ae6beefb207ffc, 0x309682b281c7d374, 0xbae309a194c3b475,
|
||||
0x8cc3f97b13b49f05, 0x98a9422ff8293967, 0x244b16b01076ff7c, 0xf8bf571c663d67ee,
|
||||
0x1f0d6758eee30da1, 0xc9b611d97adeb9b7, 0xb7afd5887b6c57a2, 0x6290ae846b984fe1,
|
||||
0x94df4cdeacc1a5fd, 0x058a5bd1c5483aff, 0x63166cc142ba3c37, 0x8db8526eb2f76f40,
|
||||
0xe10880036f0d6d4e, 0x9e0523c9971d311d, 0x45ec2824cc7cd691, 0x575b8359e62382c9,
|
||||
0xfa9e400dc4889995, 0xd1823ecb45721568, 0xdafd983b8206082f, 0xaa7d29082386a8cb,
|
||||
0x269fcd4403b87588, 0x1b91f5f728bdd1e0, 0xe4669f39040201f6, 0x7a1d7c218cf04ade,
|
||||
0x65623c29d79ce5ce, 0x2368449096c00bb1, 0xab9bf1879da503ba, 0xbc23ecb1a458058e,
|
||||
0x9a58df01bb401ecc, 0xa070e868a85f143d, 0x4ff188307df2239e, 0x14d565b41a641183,
|
||||
0xee13337452701602, 0x950e3dcf3f285e09, 0x59930254b9c80953, 0x3bf299408930da6d,
|
||||
0xa955943f53691387, 0xa15edecaa9cb8784, 0x29142127352be9a0, 0x76f0371fff4e7afb,
|
||||
0x0239f450274f2228, 0xbb073af01d5e868b, 0xbfc80571c10e96c1, 0xd267088568222e23,
|
||||
0x9671a3d48e80b5b0, 0x55b5d38ae193bb81, 0x693ae2d0a18b04b8, 0x5c48b4ecadd5335f,
|
||||
0xfd743b194916a1ca, 0x2577018134be98c4, 0xe77987e83c54a4ad, 0x28e11014da33e1b9,
|
||||
0x270cc59e226aa213, 0x71495f756d1a5f60, 0x9be853fb60afef77, 0xadc786a7f7443dbf,
|
||||
0x0904456173b29a82, 0x58bc7a66c232bd5e, 0xf306558c673ac8b2, 0x41f639c6b6c9772a,
|
||||
0x216defe99fda35da, 0x11640cc71c7be615, 0x93c43694565c5527, 0xea038e6246777839,
|
||||
0xf9abf3ce5a3e2469, 0x741e768d0fd312d2, 0x0144b883ced652c6, 0xc20b5a5ba33f8552,
|
||||
0x1ae69633c3435a9d, 0x97a28ca4088cfdec, 0x8824a43c1e96f420, 0x37612fa66eeea746,
|
||||
0x6b4cb165f9cf0e5a, 0x43aa1c06a0abfb4a, 0x7f4dc26ff162796b, 0x6cbacc8e54ed9b0f,
|
||||
0xa6b7ffefd2bb253e, 0x2e25bc95b0a29d4f, 0x86d6a58bdef1388c, 0xded74ac576b6f054,
|
||||
0x8030bdbc2b45805d, 0x3c81af70e94d9289, 0x3eff6dda9e3100db, 0xb38dc39fdfcc8847,
|
||||
0x123885528d17b87e, 0xf2da0ed240b1b642, 0x44cefadcd54bf9a9, 0x1312200e433c7ee6,
|
||||
0x9ffcc84f3a78c748, 0xf0cd1f72248576bb, 0xec6974053638cfe4, 0x2ba7b67c0cec4e4c,
|
||||
0xac2f4df3e5ce32ed, 0xcb33d14326ea4c11, 0xa4e9044cc77e58bc, 0x5f513293d934fcef,
|
||||
0x5dc9645506e55444, 0x50de418f317de40a, 0x388cb31a69dde259, 0x2db4a83455820a86,
|
||||
0x9010a91e84711ae9, 0x4df7f0b7b1498371, 0xd62a2eabc0977179, 0x22fac097aa8d5c0e,
|
||||
}
|
||||
|
||||
T3 := [?]u64 {
|
||||
0xf49fcc2ff1daf39b, 0x487fd5c66ff29281, 0xe8a30667fcdca83f, 0x2c9b4be3d2fcce63,
|
||||
0xda3ff74b93fbbbc2, 0x2fa165d2fe70ba66, 0xa103e279970e93d4, 0xbecdec77b0e45e71,
|
||||
0xcfb41e723985e497, 0xb70aaa025ef75017, 0xd42309f03840b8e0, 0x8efc1ad035898579,
|
||||
0x96c6920be2b2abc5, 0x66af4163375a9172, 0x2174abdcca7127fb, 0xb33ccea64a72ff41,
|
||||
0xf04a4933083066a5, 0x8d970acdd7289af5, 0x8f96e8e031c8c25e, 0xf3fec02276875d47,
|
||||
0xec7bf310056190dd, 0xf5adb0aebb0f1491, 0x9b50f8850fd58892, 0x4975488358b74de8,
|
||||
0xa3354ff691531c61, 0x0702bbe481d2c6ee, 0x89fb24057deded98, 0xac3075138596e902,
|
||||
0x1d2d3580172772ed, 0xeb738fc28e6bc30d, 0x5854ef8f63044326, 0x9e5c52325add3bbe,
|
||||
0x90aa53cf325c4623, 0xc1d24d51349dd067, 0x2051cfeea69ea624, 0x13220f0a862e7e4f,
|
||||
0xce39399404e04864, 0xd9c42ca47086fcb7, 0x685ad2238a03e7cc, 0x066484b2ab2ff1db,
|
||||
0xfe9d5d70efbf79ec, 0x5b13b9dd9c481854, 0x15f0d475ed1509ad, 0x0bebcd060ec79851,
|
||||
0xd58c6791183ab7f8, 0xd1187c5052f3eee4, 0xc95d1192e54e82ff, 0x86eea14cb9ac6ca2,
|
||||
0x3485beb153677d5d, 0xdd191d781f8c492a, 0xf60866baa784ebf9, 0x518f643ba2d08c74,
|
||||
0x8852e956e1087c22, 0xa768cb8dc410ae8d, 0x38047726bfec8e1a, 0xa67738b4cd3b45aa,
|
||||
0xad16691cec0dde19, 0xc6d4319380462e07, 0xc5a5876d0ba61938, 0x16b9fa1fa58fd840,
|
||||
0x188ab1173ca74f18, 0xabda2f98c99c021f, 0x3e0580ab134ae816, 0x5f3b05b773645abb,
|
||||
0x2501a2be5575f2f6, 0x1b2f74004e7e8ba9, 0x1cd7580371e8d953, 0x7f6ed89562764e30,
|
||||
0xb15926ff596f003d, 0x9f65293da8c5d6b9, 0x6ecef04dd690f84c, 0x4782275fff33af88,
|
||||
0xe41433083f820801, 0xfd0dfe409a1af9b5, 0x4325a3342cdb396b, 0x8ae77e62b301b252,
|
||||
0xc36f9e9f6655615a, 0x85455a2d92d32c09, 0xf2c7dea949477485, 0x63cfb4c133a39eba,
|
||||
0x83b040cc6ebc5462, 0x3b9454c8fdb326b0, 0x56f56a9e87ffd78c, 0x2dc2940d99f42bc6,
|
||||
0x98f7df096b096e2d, 0x19a6e01e3ad852bf, 0x42a99ccbdbd4b40b, 0xa59998af45e9c559,
|
||||
0x366295e807d93186, 0x6b48181bfaa1f773, 0x1fec57e2157a0a1d, 0x4667446af6201ad5,
|
||||
0xe615ebcacfb0f075, 0xb8f31f4f68290778, 0x22713ed6ce22d11e, 0x3057c1a72ec3c93b,
|
||||
0xcb46acc37c3f1f2f, 0xdbb893fd02aaf50e, 0x331fd92e600b9fcf, 0xa498f96148ea3ad6,
|
||||
0xa8d8426e8b6a83ea, 0xa089b274b7735cdc, 0x87f6b3731e524a11, 0x118808e5cbc96749,
|
||||
0x9906e4c7b19bd394, 0xafed7f7e9b24a20c, 0x6509eadeeb3644a7, 0x6c1ef1d3e8ef0ede,
|
||||
0xb9c97d43e9798fb4, 0xa2f2d784740c28a3, 0x7b8496476197566f, 0x7a5be3e6b65f069d,
|
||||
0xf96330ed78be6f10, 0xeee60de77a076a15, 0x2b4bee4aa08b9bd0, 0x6a56a63ec7b8894e,
|
||||
0x02121359ba34fef4, 0x4cbf99f8283703fc, 0x398071350caf30c8, 0xd0a77a89f017687a,
|
||||
0xf1c1a9eb9e423569, 0x8c7976282dee8199, 0x5d1737a5dd1f7abd, 0x4f53433c09a9fa80,
|
||||
0xfa8b0c53df7ca1d9, 0x3fd9dcbc886ccb77, 0xc040917ca91b4720, 0x7dd00142f9d1dcdf,
|
||||
0x8476fc1d4f387b58, 0x23f8e7c5f3316503, 0x032a2244e7e37339, 0x5c87a5d750f5a74b,
|
||||
0x082b4cc43698992e, 0xdf917becb858f63c, 0x3270b8fc5bf86dda, 0x10ae72bb29b5dd76,
|
||||
0x576ac94e7700362b, 0x1ad112dac61efb8f, 0x691bc30ec5faa427, 0xff246311cc327143,
|
||||
0x3142368e30e53206, 0x71380e31e02ca396, 0x958d5c960aad76f1, 0xf8d6f430c16da536,
|
||||
0xc8ffd13f1be7e1d2, 0x7578ae66004ddbe1, 0x05833f01067be646, 0xbb34b5ad3bfe586d,
|
||||
0x095f34c9a12b97f0, 0x247ab64525d60ca8, 0xdcdbc6f3017477d1, 0x4a2e14d4decad24d,
|
||||
0xbdb5e6d9be0a1eeb, 0x2a7e70f7794301ab, 0xdef42d8a270540fd, 0x01078ec0a34c22c1,
|
||||
0xe5de511af4c16387, 0x7ebb3a52bd9a330a, 0x77697857aa7d6435, 0x004e831603ae4c32,
|
||||
0xe7a21020ad78e312, 0x9d41a70c6ab420f2, 0x28e06c18ea1141e6, 0xd2b28cbd984f6b28,
|
||||
0x26b75f6c446e9d83, 0xba47568c4d418d7f, 0xd80badbfe6183d8e, 0x0e206d7f5f166044,
|
||||
0xe258a43911cbca3e, 0x723a1746b21dc0bc, 0xc7caa854f5d7cdd3, 0x7cac32883d261d9c,
|
||||
0x7690c26423ba942c, 0x17e55524478042b8, 0xe0be477656a2389f, 0x4d289b5e67ab2da0,
|
||||
0x44862b9c8fbbfd31, 0xb47cc8049d141365, 0x822c1b362b91c793, 0x4eb14655fb13dfd8,
|
||||
0x1ecbba0714e2a97b, 0x6143459d5cde5f14, 0x53a8fbf1d5f0ac89, 0x97ea04d81c5e5b00,
|
||||
0x622181a8d4fdb3f3, 0xe9bcd341572a1208, 0x1411258643cce58a, 0x9144c5fea4c6e0a4,
|
||||
0x0d33d06565cf620f, 0x54a48d489f219ca1, 0xc43e5eac6d63c821, 0xa9728b3a72770daf,
|
||||
0xd7934e7b20df87ef, 0xe35503b61a3e86e5, 0xcae321fbc819d504, 0x129a50b3ac60bfa6,
|
||||
0xcd5e68ea7e9fb6c3, 0xb01c90199483b1c7, 0x3de93cd5c295376c, 0xaed52edf2ab9ad13,
|
||||
0x2e60f512c0a07884, 0xbc3d86a3e36210c9, 0x35269d9b163951ce, 0x0c7d6e2ad0cdb5fa,
|
||||
0x59e86297d87f5733, 0x298ef221898db0e7, 0x55000029d1a5aa7e, 0x8bc08ae1b5061b45,
|
||||
0xc2c31c2b6c92703a, 0x94cc596baf25ef42, 0x0a1d73db22540456, 0x04b6a0f9d9c4179a,
|
||||
0xeffdafa2ae3d3c60, 0xf7c8075bb49496c4, 0x9cc5c7141d1cd4e3, 0x78bd1638218e5534,
|
||||
0xb2f11568f850246a, 0xedfabcfa9502bc29, 0x796ce5f2da23051b, 0xaae128b0dc93537c,
|
||||
0x3a493da0ee4b29ae, 0xb5df6b2c416895d7, 0xfcabbd25122d7f37, 0x70810b58105dc4b1,
|
||||
0xe10fdd37f7882a90, 0x524dcab5518a3f5c, 0x3c9e85878451255b, 0x4029828119bd34e2,
|
||||
0x74a05b6f5d3ceccb, 0xb610021542e13eca, 0x0ff979d12f59e2ac, 0x6037da27e4f9cc50,
|
||||
0x5e92975a0df1847d, 0xd66de190d3e623fe, 0x5032d6b87b568048, 0x9a36b7ce8235216e,
|
||||
0x80272a7a24f64b4a, 0x93efed8b8c6916f7, 0x37ddbff44cce1555, 0x4b95db5d4b99bd25,
|
||||
0x92d3fda169812fc0, 0xfb1a4a9a90660bb6, 0x730c196946a4b9b2, 0x81e289aa7f49da68,
|
||||
0x64669a0f83b1a05f, 0x27b3ff7d9644f48b, 0xcc6b615c8db675b3, 0x674f20b9bcebbe95,
|
||||
0x6f31238275655982, 0x5ae488713e45cf05, 0xbf619f9954c21157, 0xeabac46040a8eae9,
|
||||
0x454c6fe9f2c0c1cd, 0x419cf6496412691c, 0xd3dc3bef265b0f70, 0x6d0e60f5c3578a9e,
|
||||
}
|
||||
|
||||
T4 := [?]u64 {
|
||||
0x5b0e608526323c55, 0x1a46c1a9fa1b59f5, 0xa9e245a17c4c8ffa, 0x65ca5159db2955d7,
|
||||
0x05db0a76ce35afc2, 0x81eac77ea9113d45, 0x528ef88ab6ac0a0d, 0xa09ea253597be3ff,
|
||||
0x430ddfb3ac48cd56, 0xc4b3a67af45ce46f, 0x4ececfd8fbe2d05e, 0x3ef56f10b39935f0,
|
||||
0x0b22d6829cd619c6, 0x17fd460a74df2069, 0x6cf8cc8e8510ed40, 0xd6c824bf3a6ecaa7,
|
||||
0x61243d581a817049, 0x048bacb6bbc163a2, 0xd9a38ac27d44cc32, 0x7fddff5baaf410ab,
|
||||
0xad6d495aa804824b, 0xe1a6a74f2d8c9f94, 0xd4f7851235dee8e3, 0xfd4b7f886540d893,
|
||||
0x247c20042aa4bfda, 0x096ea1c517d1327c, 0xd56966b4361a6685, 0x277da5c31221057d,
|
||||
0x94d59893a43acff7, 0x64f0c51ccdc02281, 0x3d33bcc4ff6189db, 0xe005cb184ce66af1,
|
||||
0xff5ccd1d1db99bea, 0xb0b854a7fe42980f, 0x7bd46a6a718d4b9f, 0xd10fa8cc22a5fd8c,
|
||||
0xd31484952be4bd31, 0xc7fa975fcb243847, 0x4886ed1e5846c407, 0x28cddb791eb70b04,
|
||||
0xc2b00be2f573417f, 0x5c9590452180f877, 0x7a6bddfff370eb00, 0xce509e38d6d9d6a4,
|
||||
0xebeb0f00647fa702, 0x1dcc06cf76606f06, 0xe4d9f28ba286ff0a, 0xd85a305dc918c262,
|
||||
0x475b1d8732225f54, 0x2d4fb51668ccb5fe, 0xa679b9d9d72bba20, 0x53841c0d912d43a5,
|
||||
0x3b7eaa48bf12a4e8, 0x781e0e47f22f1ddf, 0xeff20ce60ab50973, 0x20d261d19dffb742,
|
||||
0x16a12b03062a2e39, 0x1960eb2239650495, 0x251c16fed50eb8b8, 0x9ac0c330f826016e,
|
||||
0xed152665953e7671, 0x02d63194a6369570, 0x5074f08394b1c987, 0x70ba598c90b25ce1,
|
||||
0x794a15810b9742f6, 0x0d5925e9fcaf8c6c, 0x3067716cd868744e, 0x910ab077e8d7731b,
|
||||
0x6a61bbdb5ac42f61, 0x93513efbf0851567, 0xf494724b9e83e9d5, 0xe887e1985c09648d,
|
||||
0x34b1d3c675370cfd, 0xdc35e433bc0d255d, 0xd0aab84234131be0, 0x08042a50b48b7eaf,
|
||||
0x9997c4ee44a3ab35, 0x829a7b49201799d0, 0x263b8307b7c54441, 0x752f95f4fd6a6ca6,
|
||||
0x927217402c08c6e5, 0x2a8ab754a795d9ee, 0xa442f7552f72943d, 0x2c31334e19781208,
|
||||
0x4fa98d7ceaee6291, 0x55c3862f665db309, 0xbd0610175d53b1f3, 0x46fe6cb840413f27,
|
||||
0x3fe03792df0cfa59, 0xcfe700372eb85e8f, 0xa7be29e7adbce118, 0xe544ee5cde8431dd,
|
||||
0x8a781b1b41f1873e, 0xa5c94c78a0d2f0e7, 0x39412e2877b60728, 0xa1265ef3afc9a62c,
|
||||
0xbcc2770c6a2506c5, 0x3ab66dd5dce1ce12, 0xe65499d04a675b37, 0x7d8f523481bfd216,
|
||||
0x0f6f64fcec15f389, 0x74efbe618b5b13c8, 0xacdc82b714273e1d, 0xdd40bfe003199d17,
|
||||
0x37e99257e7e061f8, 0xfa52626904775aaa, 0x8bbbf63a463d56f9, 0xf0013f1543a26e64,
|
||||
0xa8307e9f879ec898, 0xcc4c27a4150177cc, 0x1b432f2cca1d3348, 0xde1d1f8f9f6fa013,
|
||||
0x606602a047a7ddd6, 0xd237ab64cc1cb2c7, 0x9b938e7225fcd1d3, 0xec4e03708e0ff476,
|
||||
0xfeb2fbda3d03c12d, 0xae0bced2ee43889a, 0x22cb8923ebfb4f43, 0x69360d013cf7396d,
|
||||
0x855e3602d2d4e022, 0x073805bad01f784c, 0x33e17a133852f546, 0xdf4874058ac7b638,
|
||||
0xba92b29c678aa14a, 0x0ce89fc76cfaadcd, 0x5f9d4e0908339e34, 0xf1afe9291f5923b9,
|
||||
0x6e3480f60f4a265f, 0xeebf3a2ab29b841c, 0xe21938a88f91b4ad, 0x57dfeff845c6d3c3,
|
||||
0x2f006b0bf62caaf2, 0x62f479ef6f75ee78, 0x11a55ad41c8916a9, 0xf229d29084fed453,
|
||||
0x42f1c27b16b000e6, 0x2b1f76749823c074, 0x4b76eca3c2745360, 0x8c98f463b91691bd,
|
||||
0x14bcc93cf1ade66a, 0x8885213e6d458397, 0x8e177df0274d4711, 0xb49b73b5503f2951,
|
||||
0x10168168c3f96b6b, 0x0e3d963b63cab0ae, 0x8dfc4b5655a1db14, 0xf789f1356e14de5c,
|
||||
0x683e68af4e51dac1, 0xc9a84f9d8d4b0fd9, 0x3691e03f52a0f9d1, 0x5ed86e46e1878e80,
|
||||
0x3c711a0e99d07150, 0x5a0865b20c4e9310, 0x56fbfc1fe4f0682e, 0xea8d5de3105edf9b,
|
||||
0x71abfdb12379187a, 0x2eb99de1bee77b9c, 0x21ecc0ea33cf4523, 0x59a4d7521805c7a1,
|
||||
0x3896f5eb56ae7c72, 0xaa638f3db18f75dc, 0x9f39358dabe9808e, 0xb7defa91c00b72ac,
|
||||
0x6b5541fd62492d92, 0x6dc6dee8f92e4d5b, 0x353f57abc4beea7e, 0x735769d6da5690ce,
|
||||
0x0a234aa642391484, 0xf6f9508028f80d9d, 0xb8e319a27ab3f215, 0x31ad9c1151341a4d,
|
||||
0x773c22a57bef5805, 0x45c7561a07968633, 0xf913da9e249dbe36, 0xda652d9b78a64c68,
|
||||
0x4c27a97f3bc334ef, 0x76621220e66b17f4, 0x967743899acd7d0b, 0xf3ee5bcae0ed6782,
|
||||
0x409f753600c879fc, 0x06d09a39b5926db6, 0x6f83aeb0317ac588, 0x01e6ca4a86381f21,
|
||||
0x66ff3462d19f3025, 0x72207c24ddfd3bfb, 0x4af6b6d3e2ece2eb, 0x9c994dbec7ea08de,
|
||||
0x49ace597b09a8bc4, 0xb38c4766cf0797ba, 0x131b9373c57c2a75, 0xb1822cce61931e58,
|
||||
0x9d7555b909ba1c0c, 0x127fafdd937d11d2, 0x29da3badc66d92e4, 0xa2c1d57154c2ecbc,
|
||||
0x58c5134d82f6fe24, 0x1c3ae3515b62274f, 0xe907c82e01cb8126, 0xf8ed091913e37fcb,
|
||||
0x3249d8f9c80046c9, 0x80cf9bede388fb63, 0x1881539a116cf19e, 0x5103f3f76bd52457,
|
||||
0x15b7e6f5ae47f7a8, 0xdbd7c6ded47e9ccf, 0x44e55c410228bb1a, 0xb647d4255edb4e99,
|
||||
0x5d11882bb8aafc30, 0xf5098bbb29d3212a, 0x8fb5ea14e90296b3, 0x677b942157dd025a,
|
||||
0xfb58e7c0a390acb5, 0x89d3674c83bd4a01, 0x9e2da4df4bf3b93b, 0xfcc41e328cab4829,
|
||||
0x03f38c96ba582c52, 0xcad1bdbd7fd85db2, 0xbbb442c16082ae83, 0xb95fe86ba5da9ab0,
|
||||
0xb22e04673771a93f, 0x845358c9493152d8, 0xbe2a488697b4541e, 0x95a2dc2dd38e6966,
|
||||
0xc02c11ac923c852b, 0x2388b1990df2a87b, 0x7c8008fa1b4f37be, 0x1f70d0c84d54e503,
|
||||
0x5490adec7ece57d4, 0x002b3c27d9063a3a, 0x7eaea3848030a2bf, 0xc602326ded2003c0,
|
||||
0x83a7287d69a94086, 0xc57a5fcb30f57a8a, 0xb56844e479ebe779, 0xa373b40f05dcbce9,
|
||||
0xd71a786e88570ee2, 0x879cbacdbde8f6a0, 0x976ad1bcc164a32f, 0xab21e25e9666d78b,
|
||||
0x901063aae5e5c33c, 0x9818b34448698d90, 0xe36487ae3e1e8abb, 0xafbdf931893bdcb4,
|
||||
0x6345a0dc5fbbd519, 0x8628fe269b9465ca, 0x1e5d01603f9c51ec, 0x4de44006a15049b7,
|
||||
0xbf6c70e5f776cbb1, 0x411218f2ef552bed, 0xcb0c0708705a36a3, 0xe74d14754f986044,
|
||||
0xcd56d9430ea8280e, 0xc12591d7535f5065, 0xc83223f1720aef96, 0xc3a0396f7363a51f,
|
||||
}
|
||||
|
||||
Tiger_Context :: struct {
|
||||
a: u64,
|
||||
b: u64,
|
||||
c: u64,
|
||||
x: [64]byte,
|
||||
nx: int,
|
||||
length: u64,
|
||||
ver: int,
|
||||
}
|
||||
|
||||
round :: #force_inline proc "contextless" (a, b, c, x, mul: u64) -> (u64, u64, u64) {
|
||||
a, b, c := a, b, c
|
||||
c ~= x
|
||||
a -= T1[c & 0xff] ~ T2[(c >> 16) & 0xff] ~ T3[(c >> 32) & 0xff] ~ T4[(c >> 48) & 0xff]
|
||||
b += T4[(c >> 8) & 0xff] ~ T3[(c >> 24) & 0xff] ~ T2[(c >> 40) & 0xff] ~ T1[(c >> 56) & 0xff]
|
||||
b *= mul
|
||||
return a, b, c
|
||||
}
|
||||
|
||||
pass :: #force_inline proc "contextless" (a, b, c: u64, d: []u64, mul: u64) -> (x, y, z: u64) {
|
||||
x, y, z = round(a, b, c, d[0], mul)
|
||||
y, z, x = round(y, z, x, d[1], mul)
|
||||
z, x, y = round(z, x, y, d[2], mul)
|
||||
x, y, z = round(x, y, z, d[3], mul)
|
||||
y, z, x = round(y, z, x, d[4], mul)
|
||||
z, x, y = round(z, x, y, d[5], mul)
|
||||
x, y, z = round(x, y, z, d[6], mul)
|
||||
y, z, x = round(y, z, x, d[7], mul)
|
||||
return
|
||||
}
|
||||
|
||||
key_schedule :: #force_inline proc "contextless" (x: []u64) {
|
||||
x[0] -= x[7] ~ 0xa5a5a5a5a5a5a5a5
|
||||
x[1] ~= x[0]
|
||||
x[2] += x[1]
|
||||
x[3] -= x[2] ~ ((~x[1]) << 19)
|
||||
x[4] ~= x[3]
|
||||
x[5] += x[4]
|
||||
x[6] -= x[5] ~ ((~x[4]) >> 23)
|
||||
x[7] ~= x[6]
|
||||
x[0] += x[7]
|
||||
x[1] -= x[0] ~ ((~x[7]) << 19)
|
||||
x[2] ~= x[1]
|
||||
x[3] += x[2]
|
||||
x[4] -= x[3] ~ ((~x[2]) >> 23)
|
||||
x[5] ~= x[4]
|
||||
x[6] += x[5]
|
||||
x[7] -= x[6] ~ 0x0123456789abcdef
|
||||
}
|
||||
|
||||
compress :: #force_inline proc "contextless" (ctx: ^Tiger_Context, data: []byte) {
|
||||
a := ctx.a
|
||||
b := ctx.b
|
||||
c := ctx.c
|
||||
x := util.cast_slice([]u64, data)
|
||||
ctx.a, ctx.b, ctx.c = pass(ctx.a, ctx.b, ctx.c, x, 5)
|
||||
key_schedule(x)
|
||||
ctx.c, ctx.a, ctx.b = pass(ctx.c, ctx.a, ctx.b, x, 7)
|
||||
key_schedule(x)
|
||||
ctx.b, ctx.c, ctx.a = pass(ctx.b, ctx.c, ctx.a, x, 9)
|
||||
ctx.a ~= a
|
||||
ctx.b -= b
|
||||
ctx.c += c
|
||||
}
|
||||
|
||||
init :: proc "contextless" (ctx: ^Tiger_Context) {
|
||||
ctx.a = 0x0123456789abcdef
|
||||
ctx.b = 0xfedcba9876543210
|
||||
ctx.c = 0xf096a5b4c3b2e187
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^Tiger_Context, input: []byte) {
|
||||
p := make([]byte, len(input))
|
||||
copy(p, input)
|
||||
|
||||
length := len(p)
|
||||
ctx.length += u64(length)
|
||||
if ctx.nx > 0 {
|
||||
n := len(p)
|
||||
if n > 64 - ctx.nx {
|
||||
n = 64 - ctx.nx
|
||||
}
|
||||
copy(ctx.x[ctx.nx:ctx.nx + n], p[:n])
|
||||
ctx.nx += n
|
||||
if ctx.nx == 64 {
|
||||
compress(ctx, ctx.x[:64 - 1])
|
||||
ctx.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
for len(p) >= 64 {
|
||||
compress(ctx, p[:64])
|
||||
p = p[64:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], p)
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Tiger_Context, hash: []byte) {
|
||||
length := ctx.length
|
||||
tmp: [64]byte
|
||||
if ctx.ver == 1 {
|
||||
tmp[0] = 0x01
|
||||
} else {
|
||||
tmp[0] = 0x80
|
||||
}
|
||||
|
||||
size := length & 0x3f
|
||||
if size < 56 {
|
||||
update(ctx, tmp[:56 - size])
|
||||
} else {
|
||||
update(ctx, tmp[:64 + 56 - size])
|
||||
}
|
||||
|
||||
length <<= 3
|
||||
for i := uint(0); i < 8; i += 1 {
|
||||
tmp[i] = byte(length >> (8 * i))
|
||||
}
|
||||
update(ctx, tmp[:8])
|
||||
|
||||
for i := uint(0); i < 8; i += 1 {
|
||||
tmp[i] = byte(ctx.a >> (8 * i))
|
||||
tmp[i + 8] = byte(ctx.b >> (8 * i))
|
||||
tmp[i + 16] = byte(ctx.c >> (8 * i))
|
||||
}
|
||||
copy(hash[:], tmp[:len(hash)])
|
||||
}
|
||||
@@ -1,726 +0,0 @@
|
||||
package blake
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the BLAKE hashing algorithm, as defined in <https://web.archive.org/web/20190915215948/https://131002.net/blake>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_224 :: 28
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_384 :: 48
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc "contextless" (data: string) -> [DIGEST_SIZE_224]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_224]byte {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = true
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_224 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_224(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_224(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_224]byte{}, false
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
hash_bytes_to_buffer_224,
|
||||
hash_string_to_buffer_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc "contextless" (data: string) -> [DIGEST_SIZE_256]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = false
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_256 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_256(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_256(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_256]byte{}, false
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
hash_bytes_to_buffer_256,
|
||||
hash_string_to_buffer_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc "contextless" (data: string) -> [DIGEST_SIZE_384]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_384]byte {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = true
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_384 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_384(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_384(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_384]byte{}, false
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
hash_bytes_to_buffer_384,
|
||||
hash_string_to_buffer_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc "contextless" (data: string) -> [DIGEST_SIZE_512]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = false
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_512 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_512(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_512(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_512]byte{}, false
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc "contextless" (ctx: ^$T) {
|
||||
when T == Blake256_Context {
|
||||
if ctx.is224 {
|
||||
ctx.h[0] = 0xc1059ed8
|
||||
ctx.h[1] = 0x367cd507
|
||||
ctx.h[2] = 0x3070dd17
|
||||
ctx.h[3] = 0xf70e5939
|
||||
ctx.h[4] = 0xffc00b31
|
||||
ctx.h[5] = 0x68581511
|
||||
ctx.h[6] = 0x64f98fa7
|
||||
ctx.h[7] = 0xbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667
|
||||
ctx.h[1] = 0xbb67ae85
|
||||
ctx.h[2] = 0x3c6ef372
|
||||
ctx.h[3] = 0xa54ff53a
|
||||
ctx.h[4] = 0x510e527f
|
||||
ctx.h[5] = 0x9b05688c
|
||||
ctx.h[6] = 0x1f83d9ab
|
||||
ctx.h[7] = 0x5be0cd19
|
||||
}
|
||||
} else when T == Blake512_Context {
|
||||
if ctx.is384 {
|
||||
ctx.h[0] = 0xcbbb9d5dc1059ed8
|
||||
ctx.h[1] = 0x629a292a367cd507
|
||||
ctx.h[2] = 0x9159015a3070dd17
|
||||
ctx.h[3] = 0x152fecd8f70e5939
|
||||
ctx.h[4] = 0x67332667ffc00b31
|
||||
ctx.h[5] = 0x8eb44a8768581511
|
||||
ctx.h[6] = 0xdb0c2e0d64f98fa7
|
||||
ctx.h[7] = 0x47b5481dbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667f3bcc908
|
||||
ctx.h[1] = 0xbb67ae8584caa73b
|
||||
ctx.h[2] = 0x3c6ef372fe94f82b
|
||||
ctx.h[3] = 0xa54ff53a5f1d36f1
|
||||
ctx.h[4] = 0x510e527fade682d1
|
||||
ctx.h[5] = 0x9b05688c2b3e6c1f
|
||||
ctx.h[6] = 0x1f83d9abfb41bd6b
|
||||
ctx.h[7] = 0x5be0cd19137e2179
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update :: proc "contextless" (ctx: ^$T, data: []byte) {
|
||||
data := data
|
||||
when T == Blake256_Context {
|
||||
if ctx.nx > 0 {
|
||||
n := copy(ctx.x[ctx.nx:], data)
|
||||
ctx.nx += n
|
||||
if ctx.nx == BLOCKSIZE_256 {
|
||||
block256(ctx, ctx.x[:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) >= BLOCKSIZE_256 {
|
||||
n := len(data) &~ (BLOCKSIZE_256 - 1)
|
||||
block256(ctx, data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], data)
|
||||
}
|
||||
} else when T == Blake512_Context {
|
||||
if ctx.nx > 0 {
|
||||
n := copy(ctx.x[ctx.nx:], data)
|
||||
ctx.nx += n
|
||||
if ctx.nx == BLOCKSIZE_512 {
|
||||
block512(ctx, ctx.x[:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) >= BLOCKSIZE_512 {
|
||||
n := len(data) &~ (BLOCKSIZE_512 - 1)
|
||||
block512(ctx, data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc "contextless" (ctx: ^$T, hash: []byte) {
|
||||
when T == Blake256_Context {
|
||||
tmp: [65]byte
|
||||
} else when T == Blake512_Context {
|
||||
tmp: [129]byte
|
||||
}
|
||||
nx := u64(ctx.nx)
|
||||
tmp[0] = 0x80
|
||||
length := (ctx.t + nx) << 3
|
||||
|
||||
when T == Blake256_Context {
|
||||
if nx == 55 {
|
||||
if ctx.is224 {
|
||||
write_additional(ctx, {0x80})
|
||||
} else {
|
||||
write_additional(ctx, {0x81})
|
||||
}
|
||||
} else {
|
||||
if nx < 55 {
|
||||
if nx == 0 {
|
||||
ctx.nullt = true
|
||||
}
|
||||
write_additional(ctx, tmp[0 : 55 - nx])
|
||||
} else {
|
||||
write_additional(ctx, tmp[0 : 64 - nx])
|
||||
write_additional(ctx, tmp[1:56])
|
||||
ctx.nullt = true
|
||||
}
|
||||
if ctx.is224 {
|
||||
write_additional(ctx, {0x00})
|
||||
} else {
|
||||
write_additional(ctx, {0x01})
|
||||
}
|
||||
}
|
||||
|
||||
for i : uint = 0; i < 8; i += 1 {
|
||||
tmp[i] = byte(length >> (56 - 8 * i))
|
||||
}
|
||||
write_additional(ctx, tmp[0:8])
|
||||
|
||||
h := ctx.h[:]
|
||||
if ctx.is224 {
|
||||
h = h[0:7]
|
||||
}
|
||||
for s, i in h {
|
||||
hash[i * 4] = byte(s >> 24)
|
||||
hash[i * 4 + 1] = byte(s >> 16)
|
||||
hash[i * 4 + 2] = byte(s >> 8)
|
||||
hash[i * 4 + 3] = byte(s)
|
||||
}
|
||||
} else when T == Blake512_Context {
|
||||
if nx == 111 {
|
||||
if ctx.is384 {
|
||||
write_additional(ctx, {0x80})
|
||||
} else {
|
||||
write_additional(ctx, {0x81})
|
||||
}
|
||||
} else {
|
||||
if nx < 111 {
|
||||
if nx == 0 {
|
||||
ctx.nullt = true
|
||||
}
|
||||
write_additional(ctx, tmp[0 : 111 - nx])
|
||||
} else {
|
||||
write_additional(ctx, tmp[0 : 128 - nx])
|
||||
write_additional(ctx, tmp[1:112])
|
||||
ctx.nullt = true
|
||||
}
|
||||
if ctx.is384 {
|
||||
write_additional(ctx, {0x00})
|
||||
} else {
|
||||
write_additional(ctx, {0x01})
|
||||
}
|
||||
}
|
||||
|
||||
for i : uint = 0; i < 16; i += 1 {
|
||||
tmp[i] = byte(length >> (120 - 8 * i))
|
||||
}
|
||||
write_additional(ctx, tmp[0:16])
|
||||
|
||||
h := ctx.h[:]
|
||||
if ctx.is384 {
|
||||
h = h[0:6]
|
||||
}
|
||||
for s, i in h {
|
||||
hash[i * 8] = byte(s >> 56)
|
||||
hash[i * 8 + 1] = byte(s >> 48)
|
||||
hash[i * 8 + 2] = byte(s >> 40)
|
||||
hash[i * 8 + 3] = byte(s >> 32)
|
||||
hash[i * 8 + 4] = byte(s >> 24)
|
||||
hash[i * 8 + 5] = byte(s >> 16)
|
||||
hash[i * 8 + 6] = byte(s >> 8)
|
||||
hash[i * 8 + 7] = byte(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SIZE_224 :: 28
|
||||
SIZE_256 :: 32
|
||||
SIZE_384 :: 48
|
||||
SIZE_512 :: 64
|
||||
BLOCKSIZE_256 :: 64
|
||||
BLOCKSIZE_512 :: 128
|
||||
|
||||
Blake256_Context :: struct {
|
||||
h: [8]u32,
|
||||
s: [4]u32,
|
||||
t: u64,
|
||||
x: [64]byte,
|
||||
nx: int,
|
||||
is224: bool,
|
||||
nullt: bool,
|
||||
}
|
||||
|
||||
Blake512_Context :: struct {
|
||||
h: [8]u64,
|
||||
s: [4]u64,
|
||||
t: u64,
|
||||
x: [128]byte,
|
||||
nx: int,
|
||||
is384: bool,
|
||||
nullt: bool,
|
||||
}
|
||||
|
||||
SIGMA := [?]int {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
|
||||
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
|
||||
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
|
||||
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
|
||||
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
|
||||
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
|
||||
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
|
||||
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
|
||||
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
|
||||
}
|
||||
|
||||
U256 := [16]u32 {
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
||||
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
|
||||
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
||||
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
||||
}
|
||||
|
||||
U512 := [16]u64 {
|
||||
0x243f6a8885a308d3, 0x13198a2e03707344, 0xa4093822299f31d0, 0x082efa98ec4e6c89,
|
||||
0x452821e638d01377, 0xbe5466cf34e90c6c, 0xc0ac29b7c97c50dd, 0x3f84d5b5b5470917,
|
||||
0x9216d5d98979fb1b, 0xd1310ba698dfb5ac, 0x2ffd72dbd01adfb7, 0xb8e1afed6a267e96,
|
||||
0xba7c9045f12c7f99, 0x24a19947b3916cf7, 0x0801f2e2858efc16, 0x636920d871574e69,
|
||||
}
|
||||
|
||||
G256 :: #force_inline proc "contextless" (a, b, c, d: u32, m: [16]u32, i, j: int) -> (u32, u32, u32, u32) {
|
||||
a, b, c, d := a, b, c, d
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j)]] ~ U256[SIGMA[(i % 10) * 16 + (2 * j + 1)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (32 - 16) | d >> 16
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (32 - 12) | b >> 12
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j + 1)]] ~ U256[SIGMA[(i % 10) * 16 + (2 * j)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (32 - 8) | d >> 8
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (32 - 7) | b >> 7
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
G512 :: #force_inline proc "contextless" (a, b, c, d: u64, m: [16]u64, i, j: int) -> (u64, u64, u64, u64) {
|
||||
a, b, c, d := a, b, c, d
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j)]] ~ U512[SIGMA[(i % 10) * 16 + (2 * j + 1)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (64 - 32) | d >> 32
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (64 - 25) | b >> 25
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j + 1)]] ~ U512[SIGMA[(i % 10) * 16 + (2 * j)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (64 - 16) | d >> 16
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (64 - 11) | b >> 11
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) #no_bounds_check {
|
||||
i, j: int = ---, ---
|
||||
v, m: [16]u32 = ---, ---
|
||||
p := p
|
||||
for len(p) >= BLOCKSIZE_256 {
|
||||
v[0] = ctx.h[0]
|
||||
v[1] = ctx.h[1]
|
||||
v[2] = ctx.h[2]
|
||||
v[3] = ctx.h[3]
|
||||
v[4] = ctx.h[4]
|
||||
v[5] = ctx.h[5]
|
||||
v[6] = ctx.h[6]
|
||||
v[7] = ctx.h[7]
|
||||
v[8] = ctx.s[0] ~ U256[0]
|
||||
v[9] = ctx.s[1] ~ U256[1]
|
||||
v[10] = ctx.s[2] ~ U256[2]
|
||||
v[11] = ctx.s[3] ~ U256[3]
|
||||
v[12] = U256[4]
|
||||
v[13] = U256[5]
|
||||
v[14] = U256[6]
|
||||
v[15] = U256[7]
|
||||
|
||||
ctx.t += 512
|
||||
if !ctx.nullt {
|
||||
v[12] ~= u32(ctx.t)
|
||||
v[13] ~= u32(ctx.t)
|
||||
v[14] ~= u32(ctx.t >> 32)
|
||||
v[15] ~= u32(ctx.t >> 32)
|
||||
}
|
||||
|
||||
for i, j = 0, 0; i < 16; i, j = i+1, j+4 {
|
||||
m[i] = u32(p[j]) << 24 | u32(p[j + 1]) << 16 | u32(p[j + 2]) << 8 | u32(p[j + 3])
|
||||
}
|
||||
|
||||
for i = 0; i < 14; i += 1 {
|
||||
v[0], v[4], v[8], v[12] = G256(v[0], v[4], v[8], v[12], m, i, 0)
|
||||
v[1], v[5], v[9], v[13] = G256(v[1], v[5], v[9], v[13], m, i, 1)
|
||||
v[2], v[6], v[10], v[14] = G256(v[2], v[6], v[10], v[14], m, i, 2)
|
||||
v[3], v[7], v[11], v[15] = G256(v[3], v[7], v[11], v[15], m, i, 3)
|
||||
v[0], v[5], v[10], v[15] = G256(v[0], v[5], v[10], v[15], m, i, 4)
|
||||
v[1], v[6], v[11], v[12] = G256(v[1], v[6], v[11], v[12], m, i, 5)
|
||||
v[2], v[7], v[8], v[13] = G256(v[2], v[7], v[8], v[13], m, i, 6)
|
||||
v[3], v[4], v[9], v[14] = G256(v[3], v[4], v[9], v[14], m, i, 7)
|
||||
}
|
||||
|
||||
for i = 0; i < 8; i += 1 {
|
||||
ctx.h[i] ~= ctx.s[i % 4] ~ v[i] ~ v[i + 8]
|
||||
}
|
||||
p = p[BLOCKSIZE_256:]
|
||||
}
|
||||
}
|
||||
|
||||
block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_check {
|
||||
i, j: int = ---, ---
|
||||
v, m: [16]u64 = ---, ---
|
||||
p := p
|
||||
for len(p) >= BLOCKSIZE_512 {
|
||||
v[0] = ctx.h[0]
|
||||
v[1] = ctx.h[1]
|
||||
v[2] = ctx.h[2]
|
||||
v[3] = ctx.h[3]
|
||||
v[4] = ctx.h[4]
|
||||
v[5] = ctx.h[5]
|
||||
v[6] = ctx.h[6]
|
||||
v[7] = ctx.h[7]
|
||||
v[8] = ctx.s[0] ~ U512[0]
|
||||
v[9] = ctx.s[1] ~ U512[1]
|
||||
v[10] = ctx.s[2] ~ U512[2]
|
||||
v[11] = ctx.s[3] ~ U512[3]
|
||||
v[12] = U512[4]
|
||||
v[13] = U512[5]
|
||||
v[14] = U512[6]
|
||||
v[15] = U512[7]
|
||||
|
||||
ctx.t += 1024
|
||||
if !ctx.nullt {
|
||||
v[12] ~= ctx.t
|
||||
v[13] ~= ctx.t
|
||||
v[14] ~= 0
|
||||
v[15] ~= 0
|
||||
}
|
||||
|
||||
for i, j = 0, 0; i < 16; i, j = i + 1, j + 8 {
|
||||
m[i] = u64(p[j]) << 56 | u64(p[j + 1]) << 48 | u64(p[j + 2]) << 40 | u64(p[j + 3]) << 32 |
|
||||
u64(p[j + 4]) << 24 | u64(p[j + 5]) << 16 | u64(p[j + 6]) << 8 | u64(p[j + 7])
|
||||
}
|
||||
for i = 0; i < 16; i += 1 {
|
||||
v[0], v[4], v[8], v[12] = G512(v[0], v[4], v[8], v[12], m, i, 0)
|
||||
v[1], v[5], v[9], v[13] = G512(v[1], v[5], v[9], v[13], m, i, 1)
|
||||
v[2], v[6], v[10], v[14] = G512(v[2], v[6], v[10], v[14], m, i, 2)
|
||||
v[3], v[7], v[11], v[15] = G512(v[3], v[7], v[11], v[15], m, i, 3)
|
||||
v[0], v[5], v[10], v[15] = G512(v[0], v[5], v[10], v[15], m, i, 4)
|
||||
v[1], v[6], v[11], v[12] = G512(v[1], v[6], v[11], v[12], m, i, 5)
|
||||
v[2], v[7], v[8], v[13] = G512(v[2], v[7], v[8], v[13], m, i, 6)
|
||||
v[3], v[4], v[9], v[14] = G512(v[3], v[4], v[9], v[14], m, i, 7)
|
||||
}
|
||||
|
||||
for i = 0; i < 8; i += 1 {
|
||||
ctx.h[i] ~= ctx.s[i % 4] ~ v[i] ~ v[i + 8]
|
||||
}
|
||||
p = p[BLOCKSIZE_512:]
|
||||
}
|
||||
}
|
||||
|
||||
write_additional :: proc "contextless" (ctx: ^$T, data: []byte) {
|
||||
ctx.t -= u64(len(data)) << 3
|
||||
update(ctx, data)
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
package blake2b implements the BLAKE2b hash algorithm.
|
||||
|
||||
See:
|
||||
- https://datatracker.ietf.org/doc/html/rfc7693
|
||||
- https://www.blake2.net
|
||||
*/
|
||||
package blake2b
|
||||
|
||||
/*
|
||||
@@ -6,122 +13,47 @@ package blake2b
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Interface for the BLAKE2B hashing algorithm.
|
||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_blake2"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// DIGEST_SIZE is the BLAKE2b digest size in bytes.
|
||||
DIGEST_SIZE :: 64
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
// BLOCK_SIZE is the BLAKE2b block size in bytes.
|
||||
BLOCK_SIZE :: _blake2.BLAKE2B_BLOCK_SIZE
|
||||
|
||||
// Context is a BLAKE2b instance.
|
||||
Context :: _blake2.Blake2b_Context
|
||||
|
||||
// init initializes a Context with the default BLAKE2b config.
|
||||
init :: proc(ctx: ^Context) {
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
_blake2.init(ctx, &cfg)
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: _blake2.Blake2b_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
_blake2.update(&ctx, data)
|
||||
_blake2.final(&ctx, hash[:])
|
||||
return hash
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
// final finalizes the Context, writes the digest to hash, and calls
|
||||
// reset on the Context.
|
||||
//
|
||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||
// which is useful for for calculating rolling digests.
|
||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||
_blake2.final(ctx, hash, finalize_clone)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _blake2.Blake2b_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
_blake2.update(&ctx, data)
|
||||
_blake2.final(&ctx, hash)
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_blake2.clone(ctx, other)
|
||||
}
|
||||
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: _blake2.Blake2b_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
_blake2.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_blake2.final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE]byte{}, false
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
Blake2b_Context :: _blake2.Blake2b_Context
|
||||
|
||||
init :: proc(ctx: ^_blake2.Blake2b_Context) {
|
||||
_blake2.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, hash: []byte) {
|
||||
_blake2.final(ctx, hash)
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
_blake2.reset(ctx)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
package blake2s implements the BLAKE2s hash algorithm.
|
||||
|
||||
See:
|
||||
- https://datatracker.ietf.org/doc/html/rfc7693
|
||||
- https://www.blake2.net/
|
||||
*/
|
||||
package blake2s
|
||||
|
||||
/*
|
||||
@@ -6,122 +13,47 @@ package blake2s
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Interface for the BLAKE2S hashing algorithm.
|
||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_blake2"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// DIGEST_SIZE is the BLAKE2s digest size in bytes.
|
||||
DIGEST_SIZE :: 32
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
// BLOCK_SIZE is the BLAKE2s block size in bytes.
|
||||
BLOCK_SIZE :: _blake2.BLAKE2S_BLOCK_SIZE
|
||||
|
||||
// Context is a BLAKE2s instance.
|
||||
Context :: _blake2.Blake2s_Context
|
||||
|
||||
// init initializes a Context with the default BLAKE2s config.
|
||||
init :: proc(ctx: ^Context) {
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
_blake2.init(ctx, &cfg)
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: _blake2.Blake2s_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
_blake2.update(&ctx, data)
|
||||
_blake2.final(&ctx, hash[:])
|
||||
return hash
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
// final finalizes the Context, writes the digest to hash, and calls
|
||||
// reset on the Context.
|
||||
//
|
||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||
// which is useful for for calculating rolling digests.
|
||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||
_blake2.final(ctx, hash, finalize_clone)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _blake2.Blake2s_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
_blake2.update(&ctx, data)
|
||||
_blake2.final(&ctx, hash)
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_blake2.clone(ctx, other)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: _blake2.Blake2s_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
_blake2.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_blake2.final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE]byte{}, false
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
Blake2s_Context :: _blake2.Blake2b_Context
|
||||
|
||||
init :: proc(ctx: ^_blake2.Blake2s_Context) {
|
||||
_blake2.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, hash: []byte) {
|
||||
_blake2.final(ctx, hash)
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
_blake2.reset(ctx)
|
||||
}
|
||||
|
||||
+168
-195
@@ -1,11 +1,21 @@
|
||||
/*
|
||||
package chacha20 implements the ChaCha20 and XChaCha20 stream ciphers.
|
||||
|
||||
See:
|
||||
- https://datatracker.ietf.org/doc/html/rfc8439
|
||||
- https://datatracker.ietf.org/doc/draft-irtf-cfrg-xchacha/03/
|
||||
*/
|
||||
package chacha20
|
||||
|
||||
import "core:crypto/util"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
// KEY_SIZE is the (X)ChaCha20 key size in bytes.
|
||||
KEY_SIZE :: 32
|
||||
// NONCE_SIZE is the ChaCha20 nonce size in bytes.
|
||||
NONCE_SIZE :: 12
|
||||
// XNONCE_SIZE is the XChaCha20 nonce size in bytes.
|
||||
XNONCE_SIZE :: 24
|
||||
|
||||
@(private)
|
||||
@@ -19,25 +29,26 @@ _STATE_SIZE_U32 :: 16
|
||||
_ROUNDS :: 20
|
||||
|
||||
@(private)
|
||||
_SIGMA_0 : u32 : 0x61707865
|
||||
_SIGMA_0: u32 : 0x61707865
|
||||
@(private)
|
||||
_SIGMA_1 : u32 : 0x3320646e
|
||||
_SIGMA_1: u32 : 0x3320646e
|
||||
@(private)
|
||||
_SIGMA_2 : u32 : 0x79622d32
|
||||
_SIGMA_2: u32 : 0x79622d32
|
||||
@(private)
|
||||
_SIGMA_3 : u32 : 0x6b206574
|
||||
_SIGMA_3: u32 : 0x6b206574
|
||||
|
||||
// Context is a ChaCha20 or XChaCha20 instance.
|
||||
Context :: struct {
|
||||
_s: [_STATE_SIZE_U32]u32,
|
||||
|
||||
_buffer: [_BLOCK_SIZE]byte,
|
||||
_off: int,
|
||||
|
||||
_s: [_STATE_SIZE_U32]u32,
|
||||
_buffer: [_BLOCK_SIZE]byte,
|
||||
_off: int,
|
||||
_is_ietf_flavor: bool,
|
||||
_is_initialized: bool,
|
||||
}
|
||||
|
||||
init :: proc (ctx: ^Context, key, nonce: []byte) {
|
||||
// init inititializes a Context for ChaCha20 or XChaCha20 with the provided
|
||||
// key and nonce.
|
||||
init :: proc(ctx: ^Context, key, nonce: []byte) {
|
||||
if len(key) != KEY_SIZE {
|
||||
panic("crypto/chacha20: invalid ChaCha20 key size")
|
||||
}
|
||||
@@ -60,23 +71,23 @@ init :: proc (ctx: ^Context, key, nonce: []byte) {
|
||||
ctx._s[1] = _SIGMA_1
|
||||
ctx._s[2] = _SIGMA_2
|
||||
ctx._s[3] = _SIGMA_3
|
||||
ctx._s[4] = util.U32_LE(k[0:4])
|
||||
ctx._s[5] = util.U32_LE(k[4:8])
|
||||
ctx._s[6] = util.U32_LE(k[8:12])
|
||||
ctx._s[7] = util.U32_LE(k[12:16])
|
||||
ctx._s[8] = util.U32_LE(k[16:20])
|
||||
ctx._s[9] = util.U32_LE(k[20:24])
|
||||
ctx._s[10] = util.U32_LE(k[24:28])
|
||||
ctx._s[11] = util.U32_LE(k[28:32])
|
||||
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] = util.U32_LE(n[0:4])
|
||||
ctx._s[14] = util.U32_LE(n[4:8])
|
||||
ctx._s[15] = util.U32_LE(n[8:12])
|
||||
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] = util.U32_LE(n[0:4])
|
||||
ctx._s[15] = util.U32_LE(n[4:8])
|
||||
ctx._s[14] = endian.unchecked_get_u32le(n[0:4])
|
||||
ctx._s[15] = endian.unchecked_get_u32le(n[4:8])
|
||||
|
||||
// The sub-key is stored in the keystream buffer. While
|
||||
// this will be overwritten in most circumstances, explicitly
|
||||
@@ -89,7 +100,8 @@ init :: proc (ctx: ^Context, key, nonce: []byte) {
|
||||
ctx._is_initialized = true
|
||||
}
|
||||
|
||||
seek :: proc (ctx: ^Context, block_nr: u64) {
|
||||
// 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 {
|
||||
@@ -103,7 +115,10 @@ seek :: proc (ctx: ^Context, block_nr: u64) {
|
||||
ctx._off = _BLOCK_SIZE
|
||||
}
|
||||
|
||||
xor_bytes :: proc (ctx: ^Context, dst, src: []byte) {
|
||||
// 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
|
||||
// alias exactly or not at all.
|
||||
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
// TODO: Enforcing that dst and src alias exactly or not at all
|
||||
@@ -147,7 +162,8 @@ xor_bytes :: proc (ctx: ^Context, dst, src: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
keystream_bytes :: proc (ctx: ^Context, dst: []byte) {
|
||||
// keystream_bytes fills dst with the raw (X)ChaCha20 keystream output.
|
||||
keystream_bytes :: proc(ctx: ^Context, dst: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
dst := dst
|
||||
@@ -180,7 +196,9 @@ keystream_bytes :: proc (ctx: ^Context, dst: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
reset :: proc (ctx: ^Context) {
|
||||
// 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))
|
||||
|
||||
@@ -188,7 +206,7 @@ reset :: proc (ctx: ^Context) {
|
||||
}
|
||||
|
||||
@(private)
|
||||
_do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
||||
_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
|
||||
@@ -221,114 +239,114 @@ _do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
||||
// quarterround(x, 0, 4, 8, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = util.ROTL32(x12, 16)
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
|
||||
// quarterround(x, 1, 5, 9, 13)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 2, 6, 10, 14)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 3, 7, 11, 15)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 0, 5, 10, 15)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 1, 6, 11, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 16)
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 2, 7, 8, 13)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 3, 4, 9, 14)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
}
|
||||
|
||||
x0 += _SIGMA_0
|
||||
@@ -352,93 +370,48 @@ _do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
||||
// this is "use vector operations", support for that is currently
|
||||
// a work in progress/to be designed.
|
||||
//
|
||||
// Until dedicated assembly can be written leverage the fact that
|
||||
// the callers of this routine ensure that src/dst are valid.
|
||||
// In the meantime:
|
||||
// - The caller(s) ensure that src/dst are valid.
|
||||
// - The compiler knows if the target is picky about alignment.
|
||||
|
||||
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||
// util.PUT_U32_LE/util.U32_LE are not required on little-endian
|
||||
// systems that also happen to not be strict about aligned
|
||||
// memory access.
|
||||
|
||||
dst_p := transmute(^[16]u32)(&dst[0])
|
||||
#no_bounds_check {
|
||||
if src != nil {
|
||||
src_p := transmute(^[16]u32)(&src[0])
|
||||
dst_p[0] = src_p[0] ~ x0
|
||||
dst_p[1] = src_p[1] ~ x1
|
||||
dst_p[2] = src_p[2] ~ x2
|
||||
dst_p[3] = src_p[3] ~ x3
|
||||
dst_p[4] = src_p[4] ~ x4
|
||||
dst_p[5] = src_p[5] ~ x5
|
||||
dst_p[6] = src_p[6] ~ x6
|
||||
dst_p[7] = src_p[7] ~ x7
|
||||
dst_p[8] = src_p[8] ~ x8
|
||||
dst_p[9] = src_p[9] ~ x9
|
||||
dst_p[10] = src_p[10] ~ x10
|
||||
dst_p[11] = src_p[11] ~ x11
|
||||
dst_p[12] = src_p[12] ~ x12
|
||||
dst_p[13] = src_p[13] ~ x13
|
||||
dst_p[14] = src_p[14] ~ x14
|
||||
dst_p[15] = src_p[15] ~ x15
|
||||
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 {
|
||||
dst_p[0] = x0
|
||||
dst_p[1] = x1
|
||||
dst_p[2] = x2
|
||||
dst_p[3] = x3
|
||||
dst_p[4] = x4
|
||||
dst_p[5] = x5
|
||||
dst_p[6] = x6
|
||||
dst_p[7] = x7
|
||||
dst_p[8] = x8
|
||||
dst_p[9] = x9
|
||||
dst_p[10] = x10
|
||||
dst_p[11] = x11
|
||||
dst_p[12] = x12
|
||||
dst_p[13] = x13
|
||||
dst_p[14] = x14
|
||||
dst_p[15] = x15
|
||||
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:]
|
||||
} else {
|
||||
#no_bounds_check {
|
||||
if src != nil {
|
||||
util.PUT_U32_LE(dst[0:4], util.U32_LE(src[0:4]) ~ x0)
|
||||
util.PUT_U32_LE(dst[4:8], util.U32_LE(src[4:8]) ~ x1)
|
||||
util.PUT_U32_LE(dst[8:12], util.U32_LE(src[8:12]) ~ x2)
|
||||
util.PUT_U32_LE(dst[12:16], util.U32_LE(src[12:16]) ~ x3)
|
||||
util.PUT_U32_LE(dst[16:20], util.U32_LE(src[16:20]) ~ x4)
|
||||
util.PUT_U32_LE(dst[20:24], util.U32_LE(src[20:24]) ~ x5)
|
||||
util.PUT_U32_LE(dst[24:28], util.U32_LE(src[24:28]) ~ x6)
|
||||
util.PUT_U32_LE(dst[28:32], util.U32_LE(src[28:32]) ~ x7)
|
||||
util.PUT_U32_LE(dst[32:36], util.U32_LE(src[32:36]) ~ x8)
|
||||
util.PUT_U32_LE(dst[36:40], util.U32_LE(src[36:40]) ~ x9)
|
||||
util.PUT_U32_LE(dst[40:44], util.U32_LE(src[40:44]) ~ x10)
|
||||
util.PUT_U32_LE(dst[44:48], util.U32_LE(src[44:48]) ~ x11)
|
||||
util.PUT_U32_LE(dst[48:52], util.U32_LE(src[48:52]) ~ x12)
|
||||
util.PUT_U32_LE(dst[52:56], util.U32_LE(src[52:56]) ~ x13)
|
||||
util.PUT_U32_LE(dst[56:60], util.U32_LE(src[56:60]) ~ x14)
|
||||
util.PUT_U32_LE(dst[60:64], util.U32_LE(src[60:64]) ~ x15)
|
||||
src = src[_BLOCK_SIZE:]
|
||||
} else {
|
||||
util.PUT_U32_LE(dst[0:4], x0)
|
||||
util.PUT_U32_LE(dst[4:8], x1)
|
||||
util.PUT_U32_LE(dst[8:12], x2)
|
||||
util.PUT_U32_LE(dst[12:16], x3)
|
||||
util.PUT_U32_LE(dst[16:20], x4)
|
||||
util.PUT_U32_LE(dst[20:24], x5)
|
||||
util.PUT_U32_LE(dst[24:28], x6)
|
||||
util.PUT_U32_LE(dst[28:32], x7)
|
||||
util.PUT_U32_LE(dst[32:36], x8)
|
||||
util.PUT_U32_LE(dst[36:40], x9)
|
||||
util.PUT_U32_LE(dst[40:44], x10)
|
||||
util.PUT_U32_LE(dst[44:48], x11)
|
||||
util.PUT_U32_LE(dst[48:52], x12)
|
||||
util.PUT_U32_LE(dst[52:56], x13)
|
||||
util.PUT_U32_LE(dst[56:60], x14)
|
||||
util.PUT_U32_LE(dst[60:64], x15)
|
||||
}
|
||||
dst = dst[_BLOCK_SIZE:]
|
||||
}
|
||||
}
|
||||
|
||||
// Increment the counter. Overflow checking is done upon
|
||||
@@ -451,141 +424,141 @@ _do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
||||
}
|
||||
|
||||
@(private)
|
||||
_hchacha20 :: proc (dst, key, nonce: []byte) {
|
||||
_hchacha20 :: proc "contextless" (dst, key, nonce: []byte) {
|
||||
x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
|
||||
x4 := util.U32_LE(key[0:4])
|
||||
x5 := util.U32_LE(key[4:8])
|
||||
x6 := util.U32_LE(key[8:12])
|
||||
x7 := util.U32_LE(key[12:16])
|
||||
x8 := util.U32_LE(key[16:20])
|
||||
x9 := util.U32_LE(key[20:24])
|
||||
x10 := util.U32_LE(key[24:28])
|
||||
x11 := util.U32_LE(key[28:32])
|
||||
x12 := util.U32_LE(nonce[0:4])
|
||||
x13 := util.U32_LE(nonce[4:8])
|
||||
x14 := util.U32_LE(nonce[8:12])
|
||||
x15 := util.U32_LE(nonce[12:16])
|
||||
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 = util.ROTL32(x12, 16)
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
|
||||
// quarterround(x, 1, 5, 9, 13)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 2, 6, 10, 14)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 3, 7, 11, 15)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 0, 5, 10, 15)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x15 = bits.rotate_left32(x15, 16)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x5 = bits.rotate_left32(x5, 12)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x15 = bits.rotate_left32(x15, 8)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
x5 = bits.rotate_left32(x5, 7)
|
||||
|
||||
// quarterround(x, 1, 6, 11, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 16)
|
||||
x12 = bits.rotate_left32(x12, 16)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x6 = bits.rotate_left32(x6, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x12 = bits.rotate_left32(x12, 8)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
x6 = bits.rotate_left32(x6, 7)
|
||||
|
||||
// quarterround(x, 2, 7, 8, 13)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x13 = bits.rotate_left32(x13, 16)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x7 = bits.rotate_left32(x7, 12)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x13 = bits.rotate_left32(x13, 8)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
x7 = bits.rotate_left32(x7, 7)
|
||||
|
||||
// quarterround(x, 3, 4, 9, 14)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x14 = bits.rotate_left32(x14, 16)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x4 = bits.rotate_left32(x4, 12)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x14 = bits.rotate_left32(x14, 8)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
x4 = bits.rotate_left32(x4, 7)
|
||||
}
|
||||
|
||||
util.PUT_U32_LE(dst[0:4], x0)
|
||||
util.PUT_U32_LE(dst[4:8], x1)
|
||||
util.PUT_U32_LE(dst[8:12], x2)
|
||||
util.PUT_U32_LE(dst[12:16], x3)
|
||||
util.PUT_U32_LE(dst[16:20], x12)
|
||||
util.PUT_U32_LE(dst[20:24], x13)
|
||||
util.PUT_U32_LE(dst[24:28], x14)
|
||||
util.PUT_U32_LE(dst[28:32], x15)
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
/*
|
||||
package chacha20poly1305 implements the AEAD_CHACHA20_POLY1305 Authenticated
|
||||
Encryption with Additional Data algorithm.
|
||||
|
||||
See:
|
||||
- https://www.rfc-editor.org/rfc/rfc8439
|
||||
*/
|
||||
package chacha20poly1305
|
||||
|
||||
import "core:crypto"
|
||||
import "core:crypto/chacha20"
|
||||
import "core:crypto/poly1305"
|
||||
import "core:crypto/util"
|
||||
import "core:encoding/endian"
|
||||
import "core:mem"
|
||||
|
||||
// KEY_SIZE is the chacha20poly1305 key size in bytes.
|
||||
KEY_SIZE :: chacha20.KEY_SIZE
|
||||
// NONCE_SIZE is the chacha20poly1305 nonce size in bytes.
|
||||
NONCE_SIZE :: chacha20.NONCE_SIZE
|
||||
// TAG_SIZE is the chacha20poly1305 tag size in bytes.
|
||||
TAG_SIZE :: poly1305.TAG_SIZE
|
||||
|
||||
@(private)
|
||||
@@ -49,6 +59,8 @@ _update_mac_pad16 :: #force_inline proc (ctx: ^poly1305.Context, x_len: int) {
|
||||
}
|
||||
}
|
||||
|
||||
// encrypt encrypts the plaintext and authenticates the aad and ciphertext,
|
||||
// with the provided key and nonce, stores the output in ciphertext and tag.
|
||||
encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
|
||||
_validate_common_slice_sizes(tag, key, nonce, aad, plaintext)
|
||||
if len(ciphertext) != len(plaintext) {
|
||||
@@ -87,14 +99,19 @@ encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
|
||||
// mac_data |= num_to_8_le_bytes(aad.length)
|
||||
// mac_data |= num_to_8_le_bytes(ciphertext.length)
|
||||
l_buf := otk[0:16] // Reuse the scratch buffer.
|
||||
util.PUT_U64_LE(l_buf[0:8], u64(aad_len))
|
||||
util.PUT_U64_LE(l_buf[8:16], u64(ciphertext_len))
|
||||
endian.unchecked_put_u64le(l_buf[0:8], u64(aad_len))
|
||||
endian.unchecked_put_u64le(l_buf[8:16], u64(ciphertext_len))
|
||||
poly1305.update(&mac_ctx, l_buf)
|
||||
|
||||
// tag = poly1305_mac(mac_data, otk)
|
||||
poly1305.final(&mac_ctx, tag) // Implicitly sanitizes context.
|
||||
}
|
||||
|
||||
// decrypt authenticates the aad and ciphertext, and decrypts the ciphertext,
|
||||
// with the provided key, nonce, and tag, and stores the output in plaintext,
|
||||
// returning true iff the authentication was successful.
|
||||
//
|
||||
// If authentication fails, the destination plaintext buffer will be zeroed.
|
||||
decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool {
|
||||
_validate_common_slice_sizes(tag, key, nonce, aad, ciphertext)
|
||||
if len(ciphertext) != len(plaintext) {
|
||||
@@ -128,8 +145,8 @@ decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool {
|
||||
poly1305.update(&mac_ctx, ciphertext)
|
||||
_update_mac_pad16(&mac_ctx, ciphertext_len)
|
||||
l_buf := otk[0:16] // Reuse the scratch buffer.
|
||||
util.PUT_U64_LE(l_buf[0:8], u64(aad_len))
|
||||
util.PUT_U64_LE(l_buf[8:16], u64(ciphertext_len))
|
||||
endian.unchecked_put_u64le(l_buf[0:8], u64(aad_len))
|
||||
endian.unchecked_put_u64le(l_buf[8:16], u64(ciphertext_len))
|
||||
poly1305.update(&mac_ctx, l_buf)
|
||||
|
||||
// tag = poly1305_mac(mac_data, otk)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/*
|
||||
package crypto implements a selection of cryptography algorithms and useful
|
||||
helper routines.
|
||||
*/
|
||||
package crypto
|
||||
|
||||
import "core:mem"
|
||||
@@ -51,3 +55,9 @@ rand_bytes :: proc (dst: []byte) {
|
||||
|
||||
_rand_bytes(dst)
|
||||
}
|
||||
|
||||
// has_rand_bytes returns true iff the target has support for accessing the
|
||||
// system entropty source.
|
||||
has_rand_bytes :: proc () -> bool {
|
||||
return _has_rand_bytes()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
package ed25519 implements the Ed25519 EdDSA signature algorithm.
|
||||
|
||||
See:
|
||||
- https://datatracker.ietf.org/doc/html/rfc8032
|
||||
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
|
||||
- https://eprint.iacr.org/2020/1244.pdf
|
||||
*/
|
||||
package ed25519
|
||||
|
||||
import "core:crypto"
|
||||
import grp "core:crypto/_edwards25519"
|
||||
import "core:crypto/sha2"
|
||||
import "core:mem"
|
||||
|
||||
// PRIVATE_KEY_SIZE is the byte-encoded private key size.
|
||||
PRIVATE_KEY_SIZE :: 32
|
||||
// PUBLIC_KEY_SIZE is the byte-encoded public key size.
|
||||
PUBLIC_KEY_SIZE :: 32
|
||||
// SIGNATURE_SIZE is the byte-encoded signature size.
|
||||
SIGNATURE_SIZE :: 64
|
||||
|
||||
@(private)
|
||||
NONCE_SIZE :: 32
|
||||
|
||||
// Private_Key is an Ed25519 private key.
|
||||
Private_Key :: struct {
|
||||
// WARNING: All of the members are to be treated as internal (ie:
|
||||
// the Private_Key structure is intended to be opaque). There are
|
||||
// subtle vulnerabilities that can be introduced if the internal
|
||||
// values are allowed to be altered.
|
||||
//
|
||||
// See: https://github.com/MystenLabs/ed25519-unsafe-libs
|
||||
_b: [PRIVATE_KEY_SIZE]byte,
|
||||
_s: grp.Scalar,
|
||||
_nonce: [NONCE_SIZE]byte,
|
||||
_pub_key: Public_Key,
|
||||
_is_initialized: bool,
|
||||
}
|
||||
|
||||
// Public_Key is an Ed25519 public key.
|
||||
Public_Key :: struct {
|
||||
// WARNING: All of the members are to be treated as internal (ie:
|
||||
// the Public_Key structure is intended to be opaque).
|
||||
_b: [PUBLIC_KEY_SIZE]byte,
|
||||
_neg_A: grp.Group_Element,
|
||||
_is_valid: bool,
|
||||
_is_initialized: bool,
|
||||
}
|
||||
|
||||
// private_key_set_bytes decodes a byte-encoded private key, and returns
|
||||
// true iff the operation was successful.
|
||||
private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool {
|
||||
if len(b) != PRIVATE_KEY_SIZE {
|
||||
return false
|
||||
}
|
||||
|
||||
// Derive the private key.
|
||||
ctx: sha2.Context_512 = ---
|
||||
h_bytes: [sha2.DIGEST_SIZE_512]byte = ---
|
||||
sha2.init_512(&ctx)
|
||||
sha2.update(&ctx, b)
|
||||
sha2.final(&ctx, h_bytes[:])
|
||||
|
||||
copy(priv_key._b[:], b)
|
||||
copy(priv_key._nonce[:], h_bytes[32:])
|
||||
grp.sc_set_bytes_rfc8032(&priv_key._s, h_bytes[:32])
|
||||
|
||||
// Derive the corresponding public key.
|
||||
A: grp.Group_Element = ---
|
||||
grp.ge_scalarmult_basepoint(&A, &priv_key._s)
|
||||
grp.ge_bytes(&A, priv_key._pub_key._b[:])
|
||||
grp.ge_negate(&priv_key._pub_key._neg_A, &A)
|
||||
priv_key._pub_key._is_valid = !grp.ge_is_small_order(&A)
|
||||
priv_key._pub_key._is_initialized = true
|
||||
|
||||
priv_key._is_initialized = true
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// private_key_bytes sets dst to byte-encoding of priv_key.
|
||||
private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
|
||||
if !priv_key._is_initialized {
|
||||
panic("crypto/ed25519: uninitialized private key")
|
||||
}
|
||||
if len(dst) != PRIVATE_KEY_SIZE {
|
||||
panic("crypto/ed25519: invalid destination size")
|
||||
}
|
||||
|
||||
copy(dst, priv_key._b[:])
|
||||
}
|
||||
|
||||
// private_key_clear clears priv_key to the uninitialized state.
|
||||
private_key_clear :: proc "contextless" (priv_key: ^Private_Key) {
|
||||
mem.zero_explicit(priv_key, size_of(Private_Key))
|
||||
}
|
||||
|
||||
// sign writes the signature by priv_key over msg to sig.
|
||||
sign :: proc(priv_key: ^Private_Key, msg, sig: []byte) {
|
||||
if !priv_key._is_initialized {
|
||||
panic("crypto/ed25519: uninitialized private key")
|
||||
}
|
||||
if len(sig) != SIGNATURE_SIZE {
|
||||
panic("crypto/ed25519: invalid destination size")
|
||||
}
|
||||
|
||||
// 1. Compute the hash of the private key d, H(d) = (h_0, h_1, ..., h_2b-1)
|
||||
// using SHA-512 for Ed25519. H(d) may be precomputed.
|
||||
//
|
||||
// 2. Using the second half of the digest hdigest2 = hb || ... || h2b-1,
|
||||
// define:
|
||||
//
|
||||
// 2.1 For Ed25519, r = SHA-512(hdigest2 || M); Interpret r as a
|
||||
// 64-octet little-endian integer.
|
||||
ctx: sha2.Context_512 = ---
|
||||
digest_bytes: [sha2.DIGEST_SIZE_512]byte = ---
|
||||
sha2.init_512(&ctx)
|
||||
sha2.update(&ctx, priv_key._nonce[:])
|
||||
sha2.update(&ctx, msg)
|
||||
sha2.final(&ctx, digest_bytes[:])
|
||||
|
||||
r: grp.Scalar = ---
|
||||
grp.sc_set_bytes_wide(&r, &digest_bytes)
|
||||
|
||||
// 3. Compute the point [r]G. The octet string R is the encoding of
|
||||
// the point [r]G.
|
||||
R: grp.Group_Element = ---
|
||||
R_bytes := sig[:32]
|
||||
grp.ge_scalarmult_basepoint(&R, &r)
|
||||
grp.ge_bytes(&R, R_bytes)
|
||||
|
||||
// 4. Derive s from H(d) as in the key pair generation algorithm.
|
||||
// Use octet strings R, Q, and M to define:
|
||||
//
|
||||
// 4.1 For Ed25519, digest = SHA-512(R || Q || M).
|
||||
// Interpret digest as a little-endian integer.
|
||||
sha2.init_512(&ctx)
|
||||
sha2.update(&ctx, R_bytes)
|
||||
sha2.update(&ctx, priv_key._pub_key._b[:]) // Q in NIST terminology.
|
||||
sha2.update(&ctx, msg)
|
||||
sha2.final(&ctx, digest_bytes[:])
|
||||
|
||||
sc: grp.Scalar = --- // `digest` in NIST terminology.
|
||||
grp.sc_set_bytes_wide(&sc, &digest_bytes)
|
||||
|
||||
// 5. Compute S = (r + digest × s) mod n. The octet string S is the
|
||||
// encoding of the resultant integer.
|
||||
grp.sc_mul(&sc, &sc, &priv_key._s)
|
||||
grp.sc_add(&sc, &sc, &r)
|
||||
|
||||
// 6. Form the signature as the concatenation of the octet strings
|
||||
// R and S.
|
||||
grp.sc_bytes(sig[32:], &sc)
|
||||
|
||||
grp.sc_clear(&r)
|
||||
}
|
||||
|
||||
// public_key_set_bytes decodes a byte-encoded public key, and returns
|
||||
// true iff the operation was successful.
|
||||
public_key_set_bytes :: proc "contextless" (pub_key: ^Public_Key, b: []byte) -> bool {
|
||||
if len(b) != PUBLIC_KEY_SIZE {
|
||||
return false
|
||||
}
|
||||
|
||||
A: grp.Group_Element = ---
|
||||
if !grp.ge_set_bytes(&A, b) {
|
||||
return false
|
||||
}
|
||||
|
||||
copy(pub_key._b[:], b)
|
||||
grp.ge_negate(&pub_key._neg_A, &A)
|
||||
pub_key._is_valid = !grp.ge_is_small_order(&A)
|
||||
pub_key._is_initialized = true
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// public_key_set_priv sets pub_key to the public component of priv_key.
|
||||
public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
|
||||
if !priv_key._is_initialized {
|
||||
panic("crypto/ed25519: uninitialized public key")
|
||||
}
|
||||
|
||||
src := &priv_key._pub_key
|
||||
copy(pub_key._b[:], src._b[:])
|
||||
grp.ge_set(&pub_key._neg_A, &src._neg_A)
|
||||
pub_key._is_valid = src._is_valid
|
||||
pub_key._is_initialized = src._is_initialized
|
||||
}
|
||||
|
||||
// public_key_bytes sets dst to byte-encoding of pub_key.
|
||||
public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
|
||||
if !pub_key._is_initialized {
|
||||
panic("crypto/ed25519: uninitialized public key")
|
||||
}
|
||||
if len(dst) != PUBLIC_KEY_SIZE {
|
||||
panic("crypto/ed25519: invalid destination size")
|
||||
}
|
||||
|
||||
copy(dst, pub_key._b[:])
|
||||
}
|
||||
|
||||
// public_key_equal returns true iff pub_key is equal to other.
|
||||
public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
|
||||
if !pub_key._is_initialized || !other._is_initialized {
|
||||
panic("crypto/ed25519: uninitialized public key")
|
||||
}
|
||||
|
||||
return crypto.compare_constant_time(pub_key._b[:], other._b[:]) == 1
|
||||
}
|
||||
|
||||
// verify returns true iff sig is a valid signature by pub_key over msg.
|
||||
//
|
||||
// The optional `allow_small_order_A` parameter will make this
|
||||
// implementation strictly compatible with FIPS 186-5, at the expense of
|
||||
// SBS-security. Doing so is NOT recommended, and the disallowed
|
||||
// public keys all have a known discrete-log.
|
||||
verify :: proc(pub_key: ^Public_Key, msg, sig: []byte, allow_small_order_A := false) -> bool {
|
||||
switch {
|
||||
case !pub_key._is_initialized:
|
||||
return false
|
||||
case len(sig) != SIGNATURE_SIZE:
|
||||
return false
|
||||
}
|
||||
|
||||
// TLDR: Just use ristretto255.
|
||||
//
|
||||
// While there are two "standards" for EdDSA, existing implementations
|
||||
// diverge (sometimes dramatically). This implementation opts for
|
||||
// "Algorithm 2" from "Taming the Many EdDSAs", which provides the
|
||||
// strongest notion of security (SUF-CMA + SBS).
|
||||
//
|
||||
// The relevant properties are:
|
||||
// - Reject non-canonical S.
|
||||
// - Reject non-canonical A/R.
|
||||
// - Reject small-order A (Extra non-standard check).
|
||||
// - Cofactored verification equation.
|
||||
//
|
||||
// There are 19 possible non-canonical group element encodings of
|
||||
// which:
|
||||
// - 2 are small order
|
||||
// - 10 are mixed order
|
||||
// - 7 are not on the curve
|
||||
//
|
||||
// While historical implementations have been lax about enforcing
|
||||
// that A/R are canonically encoded, that behavior is mandated by
|
||||
// both the RFC and FIPS specification. No valid key generation
|
||||
// or sign implementation will ever produce non-canonically encoded
|
||||
// public keys or signatures.
|
||||
//
|
||||
// There are 8 small-order group elements, 1 which is in the
|
||||
// prime-order sub-group, and thus the probability that a properly
|
||||
// generated A is small-order is cryptographically insignificant.
|
||||
//
|
||||
// While both the RFC and FIPS standard allow for either the
|
||||
// cofactored or non-cofactored equation. It is possible to
|
||||
// artificially produce signatures that are valid for the former
|
||||
// but not the latter. This will NEVER occur with a valid sign
|
||||
// implementation. The choice of the latter is to be compatible
|
||||
// with ABGLSV-Pornin, batch verification, and FROST (among other
|
||||
// things).
|
||||
|
||||
s_bytes, r_bytes := sig[32:], sig[:32]
|
||||
|
||||
// 1. Reject the signature if S is not in the range [0, L).
|
||||
s: grp.Scalar = ---
|
||||
if !grp.sc_set_bytes(&s, s_bytes) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 2. Reject the signature if the public key A is one of 8 small
|
||||
// order points.
|
||||
//
|
||||
// As this check is optional and not part of the standard, we allow
|
||||
// the caller to bypass it if desired. Disabling the check makes
|
||||
// the scheme NOT SBS-secure.
|
||||
if !pub_key._is_valid && !allow_small_order_A {
|
||||
return false
|
||||
}
|
||||
|
||||
// 3. Reject the signature if A or R are non-canonical.
|
||||
//
|
||||
// Note: All initialized public keys are guaranteed to be canonical.
|
||||
neg_R: grp.Group_Element = ---
|
||||
if !grp.ge_set_bytes(&neg_R, r_bytes) {
|
||||
return false
|
||||
}
|
||||
grp.ge_negate(&neg_R, &neg_R)
|
||||
|
||||
// 4. Compute the hash SHA512(R||A||M) and reduce it mod L to get a
|
||||
// scalar h.
|
||||
ctx: sha2.Context_512 = ---
|
||||
h_bytes: [sha2.DIGEST_SIZE_512]byte = ---
|
||||
sha2.init_512(&ctx)
|
||||
sha2.update(&ctx, r_bytes)
|
||||
sha2.update(&ctx, pub_key._b[:])
|
||||
sha2.update(&ctx, msg)
|
||||
sha2.final(&ctx, h_bytes[:])
|
||||
|
||||
h: grp.Scalar = ---
|
||||
grp.sc_set_bytes_wide(&h, &h_bytes)
|
||||
|
||||
// 5. Accept if 8(s * G) - 8R - 8(h * A) = 0
|
||||
//
|
||||
// > first compute V = SB − R − hA and then accept if V is one of
|
||||
// > 8 small order points (or alternatively compute 8V with 3
|
||||
// > doublings and check against the neutral element)
|
||||
V: grp.Group_Element = ---
|
||||
grp.ge_double_scalarmult_basepoint_vartime(&V, &h, &pub_key._neg_A, &s)
|
||||
grp.ge_add(&V, &V, &neg_R)
|
||||
|
||||
return grp.ge_is_small_order(&V)
|
||||
}
|
||||
@@ -1,382 +0,0 @@
|
||||
package gost
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the GOST hashing algorithm, as defined in RFC 5831 <https://datatracker.ietf.org/doc/html/rfc5831>
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 32
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Gost_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Gost_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Gost_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE]byte{}, false
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc "contextless" (ctx: ^Gost_Context) {
|
||||
sbox: [8][16]u32 = {
|
||||
{ 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15 },
|
||||
{ 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8 },
|
||||
{ 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13 },
|
||||
{ 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3 },
|
||||
{ 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5 },
|
||||
{ 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3 },
|
||||
{ 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11 },
|
||||
{ 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 },
|
||||
}
|
||||
|
||||
i := 0
|
||||
for a := 0; a < 16; a += 1 {
|
||||
ax := sbox[1][a] << 15
|
||||
bx := sbox[3][a] << 23
|
||||
cx := sbox[5][a]
|
||||
cx = (cx >> 1) | (cx << 31)
|
||||
dx := sbox[7][a] << 7
|
||||
for b := 0; b < 16; b, i = b + 1, i + 1 {
|
||||
SBOX_1[i] = ax | (sbox[0][b] << 11)
|
||||
SBOX_2[i] = bx | (sbox[2][b] << 19)
|
||||
SBOX_3[i] = cx | (sbox[4][b] << 27)
|
||||
SBOX_4[i] = dx | (sbox[6][b] << 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^Gost_Context, data: []byte) {
|
||||
length := byte(len(data))
|
||||
j: byte
|
||||
|
||||
i := ctx.partial_bytes
|
||||
for i < 32 && j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
|
||||
if i < 32 {
|
||||
ctx.partial_bytes = i
|
||||
return
|
||||
}
|
||||
bytes(ctx, ctx.partial[:], 256)
|
||||
|
||||
for (j + 32) < length {
|
||||
bytes(ctx, data[j:], 256)
|
||||
j += 32
|
||||
}
|
||||
|
||||
i = 0
|
||||
for j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
ctx.partial_bytes = i
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Gost_Context, hash: []byte) {
|
||||
if ctx.partial_bytes > 0 {
|
||||
mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
|
||||
bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
|
||||
}
|
||||
|
||||
compress(ctx.hash[:], ctx.len[:])
|
||||
compress(ctx.hash[:], ctx.sum[:])
|
||||
|
||||
for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 {
|
||||
hash[j] = byte(ctx.hash[i])
|
||||
hash[j + 1] = byte(ctx.hash[i] >> 8)
|
||||
hash[j + 2] = byte(ctx.hash[i] >> 16)
|
||||
hash[j + 3] = byte(ctx.hash[i] >> 24)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GOST implementation
|
||||
*/
|
||||
|
||||
Gost_Context :: struct {
|
||||
sum: [8]u32,
|
||||
hash: [8]u32,
|
||||
len: [8]u32,
|
||||
partial: [32]byte,
|
||||
partial_bytes: byte,
|
||||
}
|
||||
|
||||
SBOX_1: [256]u32
|
||||
SBOX_2: [256]u32
|
||||
SBOX_3: [256]u32
|
||||
SBOX_4: [256]u32
|
||||
|
||||
ENCRYPT_ROUND :: #force_inline proc "contextless" (l, r, t, k1, k2: u32) -> (u32, u32, u32) {
|
||||
l, r, t := l, r, t
|
||||
t = (k1) + r
|
||||
l ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
|
||||
t = (k2) + l
|
||||
r ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
|
||||
return l, r, t
|
||||
}
|
||||
|
||||
ENCRYPT :: #force_inline proc "contextless" (a, b, c: u32, key: []u32) -> (l, r, t: u32) {
|
||||
l, r, t = ENCRYPT_ROUND(a, b, c, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[7], key[6])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[5], key[4])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[3], key[2])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[1], key[0])
|
||||
t = r
|
||||
r = l
|
||||
l = t
|
||||
return
|
||||
}
|
||||
|
||||
bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
|
||||
a, c: u32
|
||||
m: [8]u32
|
||||
|
||||
for i, j := 0, 0; i < 8; i += 1 {
|
||||
a = u32(buf[j]) | u32(buf[j + 1]) << 8 | u32(buf[j + 2]) << 16 | u32(buf[j + 3]) << 24
|
||||
j += 4
|
||||
m[i] = a
|
||||
c = a + c + ctx.sum[i]
|
||||
ctx.sum[i] = c
|
||||
c = c < a ? 1 : 0
|
||||
}
|
||||
|
||||
compress(ctx.hash[:], m[:])
|
||||
ctx.len[0] += bits
|
||||
if ctx.len[0] < bits {
|
||||
ctx.len[1] += 1
|
||||
}
|
||||
}
|
||||
|
||||
compress :: proc(h, m: []u32) {
|
||||
key, u, v, w, s: [8]u32
|
||||
|
||||
copy(u[:], h)
|
||||
copy(v[:], m)
|
||||
|
||||
for i := 0; i < 8; i += 2 {
|
||||
w[0] = u[0] ~ v[0]
|
||||
w[1] = u[1] ~ v[1]
|
||||
w[2] = u[2] ~ v[2]
|
||||
w[3] = u[3] ~ v[3]
|
||||
w[4] = u[4] ~ v[4]
|
||||
w[5] = u[5] ~ v[5]
|
||||
w[6] = u[6] ~ v[6]
|
||||
w[7] = u[7] ~ v[7]
|
||||
|
||||
key[0] = (w[0] & 0x000000ff) | (w[2] & 0x000000ff) << 8 | (w[4] & 0x000000ff) << 16 | (w[6] & 0x000000ff) << 24
|
||||
key[1] = (w[0] & 0x0000ff00) >> 8 | (w[2] & 0x0000ff00) | (w[4] & 0x0000ff00) << 8 | (w[6] & 0x0000ff00) << 16
|
||||
key[2] = (w[0] & 0x00ff0000) >> 16 | (w[2] & 0x00ff0000) >> 8 | (w[4] & 0x00ff0000) | (w[6] & 0x00ff0000) << 8
|
||||
key[3] = (w[0] & 0xff000000) >> 24 | (w[2] & 0xff000000) >> 16 | (w[4] & 0xff000000) >> 8 | (w[6] & 0xff000000)
|
||||
key[4] = (w[1] & 0x000000ff) | (w[3] & 0x000000ff) << 8 | (w[5] & 0x000000ff) << 16 | (w[7] & 0x000000ff) << 24
|
||||
key[5] = (w[1] & 0x0000ff00) >> 8 | (w[3] & 0x0000ff00) | (w[5] & 0x0000ff00) << 8 | (w[7] & 0x0000ff00) << 16
|
||||
key[6] = (w[1] & 0x00ff0000) >> 16 | (w[3] & 0x00ff0000) >> 8 | (w[5] & 0x00ff0000) | (w[7] & 0x00ff0000) << 8
|
||||
key[7] = (w[1] & 0xff000000) >> 24 | (w[3] & 0xff000000) >> 16 | (w[5] & 0xff000000) >> 8 | (w[7] & 0xff000000)
|
||||
|
||||
r := h[i]
|
||||
l := h[i + 1]
|
||||
t: u32
|
||||
l, r, t = ENCRYPT(l, r, 0, key[:])
|
||||
|
||||
s[i] = r
|
||||
s[i + 1] = l
|
||||
|
||||
if i == 6 {
|
||||
break
|
||||
}
|
||||
|
||||
l = u[0] ~ u[2]
|
||||
r = u[1] ~ u[3]
|
||||
u[0] = u[2]
|
||||
u[1] = u[3]
|
||||
u[2] = u[4]
|
||||
u[3] = u[5]
|
||||
u[4] = u[6]
|
||||
u[5] = u[7]
|
||||
u[6] = l
|
||||
u[7] = r
|
||||
|
||||
if i == 2 {
|
||||
u[0] ~= 0xff00ff00
|
||||
u[1] ~= 0xff00ff00
|
||||
u[2] ~= 0x00ff00ff
|
||||
u[3] ~= 0x00ff00ff
|
||||
u[4] ~= 0x00ffff00
|
||||
u[5] ~= 0xff0000ff
|
||||
u[6] ~= 0x000000ff
|
||||
u[7] ~= 0xff00ffff
|
||||
}
|
||||
|
||||
l = v[0]
|
||||
r = v[2]
|
||||
v[0] = v[4]
|
||||
v[2] = v[6]
|
||||
v[4] = l ~ r
|
||||
v[6] = v[0] ~ r
|
||||
l = v[1]
|
||||
r = v[3]
|
||||
v[1] = v[5]
|
||||
v[3] = v[7]
|
||||
v[5] = l ~ r
|
||||
v[7] = v[1] ~ r
|
||||
}
|
||||
|
||||
u[0] = m[0] ~ s[6]
|
||||
u[1] = m[1] ~ s[7]
|
||||
u[2] = m[2] ~ (s[0] << 16) ~ (s[0] >> 16) ~ (s[0] & 0xffff) ~
|
||||
(s[1] & 0xffff) ~ (s[1] >> 16) ~ (s[2] << 16) ~ s[6] ~ (s[6] << 16) ~
|
||||
(s[7] & 0xffff0000) ~ (s[7] >> 16)
|
||||
u[3] = m[3] ~ (s[0] & 0xffff) ~ (s[0] << 16) ~ (s[1] & 0xffff) ~
|
||||
(s[1] << 16) ~ (s[1] >> 16) ~ (s[2] << 16) ~ (s[2] >> 16) ~
|
||||
(s[3] << 16) ~ s[6] ~ (s[6] << 16) ~ (s[6] >> 16) ~ (s[7] & 0xffff) ~
|
||||
(s[7] << 16) ~ (s[7] >> 16)
|
||||
u[4] = m[4] ~
|
||||
(s[0] & 0xffff0000) ~ (s[0] << 16) ~ (s[0] >> 16) ~
|
||||
(s[1] & 0xffff0000) ~ (s[1] >> 16) ~ (s[2] << 16) ~ (s[2] >> 16) ~
|
||||
(s[3] << 16) ~ (s[3] >> 16) ~ (s[4] << 16) ~ (s[6] << 16) ~
|
||||
(s[6] >> 16) ~(s[7] & 0xffff) ~ (s[7] << 16) ~ (s[7] >> 16)
|
||||
u[5] = m[5] ~ (s[0] << 16) ~ (s[0] >> 16) ~ (s[0] & 0xffff0000) ~
|
||||
(s[1] & 0xffff) ~ s[2] ~ (s[2] >> 16) ~ (s[3] << 16) ~ (s[3] >> 16) ~
|
||||
(s[4] << 16) ~ (s[4] >> 16) ~ (s[5] << 16) ~ (s[6] << 16) ~
|
||||
(s[6] >> 16) ~ (s[7] & 0xffff0000) ~ (s[7] << 16) ~ (s[7] >> 16)
|
||||
u[6] = m[6] ~ s[0] ~ (s[1] >> 16) ~ (s[2] << 16) ~ s[3] ~ (s[3] >> 16) ~
|
||||
(s[4] << 16) ~ (s[4] >> 16) ~ (s[5] << 16) ~ (s[5] >> 16) ~ s[6] ~
|
||||
(s[6] << 16) ~ (s[6] >> 16) ~ (s[7] << 16)
|
||||
u[7] = m[7] ~ (s[0] & 0xffff0000) ~ (s[0] << 16) ~ (s[1] & 0xffff) ~
|
||||
(s[1] << 16) ~ (s[2] >> 16) ~ (s[3] << 16) ~ s[4] ~ (s[4] >> 16) ~
|
||||
(s[5] << 16) ~ (s[5] >> 16) ~ (s[6] >> 16) ~ (s[7] & 0xffff) ~
|
||||
(s[7] << 16) ~ (s[7] >> 16)
|
||||
|
||||
v[0] = h[0] ~ (u[1] << 16) ~ (u[0] >> 16)
|
||||
v[1] = h[1] ~ (u[2] << 16) ~ (u[1] >> 16)
|
||||
v[2] = h[2] ~ (u[3] << 16) ~ (u[2] >> 16)
|
||||
v[3] = h[3] ~ (u[4] << 16) ~ (u[3] >> 16)
|
||||
v[4] = h[4] ~ (u[5] << 16) ~ (u[4] >> 16)
|
||||
v[5] = h[5] ~ (u[6] << 16) ~ (u[5] >> 16)
|
||||
v[6] = h[6] ~ (u[7] << 16) ~ (u[6] >> 16)
|
||||
v[7] = h[7] ~ (u[0] & 0xffff0000) ~ (u[0] << 16) ~ (u[7] >> 16) ~ (u[1] & 0xffff0000) ~ (u[1] << 16) ~ (u[6] << 16) ~ (u[7] & 0xffff0000)
|
||||
|
||||
h[0] = (v[0] & 0xffff0000) ~ (v[0] << 16) ~ (v[0] >> 16) ~ (v[1] >> 16) ~
|
||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ (v[3] >> 16) ~ (v[4] << 16) ~
|
||||
(v[5] >> 16) ~ v[5] ~ (v[6] >> 16) ~ (v[7] << 16) ~ (v[7] >> 16) ~
|
||||
(v[7] & 0xffff)
|
||||
h[1] = (v[0] << 16) ~ (v[0] >> 16) ~ (v[0] & 0xffff0000) ~ (v[1] & 0xffff) ~
|
||||
v[2] ~ (v[2] >> 16) ~ (v[3] << 16) ~ (v[4] >> 16) ~ (v[5] << 16) ~
|
||||
(v[6] << 16) ~ v[6] ~ (v[7] & 0xffff0000) ~ (v[7] >> 16)
|
||||
h[2] = (v[0] & 0xffff) ~ (v[0] << 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~
|
||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ (v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~
|
||||
(v[5] >> 16) ~ v[6] ~ (v[6] >> 16) ~ (v[7] & 0xffff) ~ (v[7] << 16) ~
|
||||
(v[7] >> 16)
|
||||
h[3] = (v[0] << 16) ~ (v[0] >> 16) ~ (v[0] & 0xffff0000) ~
|
||||
(v[1] & 0xffff0000) ~ (v[1] >> 16) ~ (v[2] << 16) ~ (v[2] >> 16) ~ v[2] ~
|
||||
(v[3] << 16) ~ (v[4] >> 16) ~ v[4] ~ (v[5] << 16) ~ (v[6] << 16) ~
|
||||
(v[7] & 0xffff) ~ (v[7] >> 16)
|
||||
h[4] = (v[0] >> 16) ~ (v[1] << 16) ~ v[1] ~ (v[2] >> 16) ~ v[2] ~
|
||||
(v[3] << 16) ~ (v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ (v[5] >> 16) ~
|
||||
v[5] ~ (v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16)
|
||||
h[5] = (v[0] << 16) ~ (v[0] & 0xffff0000) ~ (v[1] << 16) ~ (v[1] >> 16) ~
|
||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ v[2] ~ (v[3] >> 16) ~ v[3] ~
|
||||
(v[4] << 16) ~ (v[4] >> 16) ~ v[4] ~ (v[5] << 16) ~ (v[6] << 16) ~
|
||||
(v[6] >> 16) ~ v[6] ~ (v[7] << 16) ~ (v[7] >> 16) ~ (v[7] & 0xffff0000)
|
||||
h[6] = v[0] ~ v[2] ~ (v[2] >> 16) ~ v[3] ~ (v[3] << 16) ~ v[4] ~
|
||||
(v[4] >> 16) ~ (v[5] << 16) ~ (v[5] >> 16) ~ v[5] ~ (v[6] << 16) ~
|
||||
(v[6] >> 16) ~ v[6] ~ (v[7] << 16) ~ v[7]
|
||||
h[7] = v[0] ~ (v[0] >> 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~ (v[2] << 16) ~
|
||||
(v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ v[4] ~ (v[5] >> 16) ~ v[5] ~
|
||||
(v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16) ~ v[7]
|
||||
}
|
||||
@@ -1,653 +0,0 @@
|
||||
package groestl
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the GROESTL hashing algorithm, as defined in <http://www.groestl.info/Groestl.zip>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_224 :: 28
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_384 :: 48
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_224 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_224(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_224(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_224]byte{}, false
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
hash_bytes_to_buffer_224,
|
||||
hash_string_to_buffer_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_256 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_256(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_256(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_256]byte{}, false
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
hash_bytes_to_buffer_256,
|
||||
hash_string_to_buffer_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_384 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_384(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_384(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_384]byte{}, false
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
hash_bytes_to_buffer_384,
|
||||
hash_string_to_buffer_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_512 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_512(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_512(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_512]byte{}, false
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^Groestl_Context) {
|
||||
assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
|
||||
if ctx.hashbitlen <= 256 {
|
||||
ctx.rounds = 10
|
||||
ctx.columns = 8
|
||||
ctx.statesize = 64
|
||||
} else {
|
||||
ctx.rounds = 14
|
||||
ctx.columns = 16
|
||||
ctx.statesize = 128
|
||||
}
|
||||
for i := 8 - size_of(i32); i < 8; i += 1 {
|
||||
ctx.chaining[i][ctx.columns - 1] = byte(ctx.hashbitlen >> (8 * (7 - uint(i))))
|
||||
}
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^Groestl_Context, data: []byte) {
|
||||
databitlen := len(data) * 8
|
||||
msglen := databitlen / 8
|
||||
rem := databitlen % 8
|
||||
|
||||
i: int
|
||||
assert(ctx.bits_in_last_byte == 0)
|
||||
|
||||
if ctx.buf_ptr != 0 {
|
||||
for i = 0; ctx.buf_ptr < ctx.statesize && i < msglen; i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1 {
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
}
|
||||
|
||||
if ctx.buf_ptr < ctx.statesize {
|
||||
if rem != 0 {
|
||||
ctx.bits_in_last_byte = rem
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.buf_ptr = 0
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
}
|
||||
|
||||
transform(ctx, data[i:], u32(msglen - i))
|
||||
i += ((msglen - i) / ctx.statesize) * ctx.statesize
|
||||
for i < msglen {
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1
|
||||
}
|
||||
|
||||
if rem != 0 {
|
||||
ctx.bits_in_last_byte = rem
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Groestl_Context, hash: []byte) {
|
||||
hashbytelen := ctx.hashbitlen / 8
|
||||
|
||||
if ctx.bits_in_last_byte != 0 {
|
||||
ctx.buffer[ctx.buf_ptr - 1] &= ((1 << uint(ctx.bits_in_last_byte)) - 1) << (8 - uint(ctx.bits_in_last_byte))
|
||||
ctx.buffer[ctx.buf_ptr - 1] ~= 0x1 << (7 - uint(ctx.bits_in_last_byte))
|
||||
} else {
|
||||
ctx.buffer[ctx.buf_ptr] = 0x80
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
|
||||
if ctx.buf_ptr > ctx.statesize - 8 {
|
||||
for ctx.buf_ptr < ctx.statesize {
|
||||
ctx.buffer[ctx.buf_ptr] = 0
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
ctx.buf_ptr = 0
|
||||
}
|
||||
|
||||
for ctx.buf_ptr < ctx.statesize - 8 {
|
||||
ctx.buffer[ctx.buf_ptr] = 0
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
|
||||
ctx.block_counter += 1
|
||||
ctx.buf_ptr = ctx.statesize
|
||||
|
||||
for ctx.buf_ptr > ctx.statesize - 8 {
|
||||
ctx.buf_ptr -= 1
|
||||
ctx.buffer[ctx.buf_ptr] = byte(ctx.block_counter)
|
||||
ctx.block_counter >>= 8
|
||||
}
|
||||
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
output_transformation(ctx)
|
||||
|
||||
for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 {
|
||||
hash[j] = ctx.chaining[i % 8][i / 8]
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GROESTL implementation
|
||||
*/
|
||||
|
||||
SBOX := [256]byte {
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
|
||||
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
||||
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
|
||||
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
|
||||
0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
||||
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
|
||||
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
|
||||
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
||||
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
|
||||
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
|
||||
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
||||
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
|
||||
0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
|
||||
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
||||
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
|
||||
0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
|
||||
0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
|
||||
}
|
||||
|
||||
SHIFT := [2][2][8]int {
|
||||
{{0, 1, 2, 3, 4, 5, 6, 7}, {1, 3, 5, 7, 0, 2, 4, 6}},
|
||||
{{0, 1, 2, 3, 4, 5, 6, 11}, {1, 3, 5, 11, 0, 2, 4, 6}},
|
||||
}
|
||||
|
||||
Groestl_Context :: struct {
|
||||
chaining: [8][16]byte,
|
||||
block_counter: u64,
|
||||
hashbitlen: int,
|
||||
buffer: [128]byte,
|
||||
buf_ptr: int,
|
||||
bits_in_last_byte: int,
|
||||
columns: int,
|
||||
rounds: int,
|
||||
statesize: int,
|
||||
}
|
||||
|
||||
Groestl_Variant :: enum {
|
||||
P512 = 0,
|
||||
Q512 = 1,
|
||||
P1024 = 2,
|
||||
Q1024 = 3,
|
||||
}
|
||||
|
||||
MUL2 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return (b >> 7) != 0 ? (b << 1) ~ 0x1b : (b << 1)
|
||||
}
|
||||
|
||||
MUL3 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL2(b) ~ b
|
||||
}
|
||||
|
||||
MUL4 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL2(MUL2(b))
|
||||
}
|
||||
|
||||
MUL5 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL4(b) ~ b
|
||||
}
|
||||
|
||||
MUL6 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL4(b) ~ MUL2(b)
|
||||
}
|
||||
|
||||
MUL7 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL4(b) ~ MUL2(b) ~ b
|
||||
}
|
||||
|
||||
sub_bytes :: #force_inline proc (x: [][16]byte, columns: int) {
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < columns; j += 1 {
|
||||
x[i][j] = SBOX[x[i][j]]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shift_bytes :: #force_inline proc (x: [][16]byte, columns: int, v: Groestl_Variant) {
|
||||
temp: [16]byte
|
||||
R := &SHIFT[int(v) / 2][int(v) & 1]
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < columns; j += 1 {
|
||||
temp[j] = x[i][(j + R[i]) % columns]
|
||||
}
|
||||
for j := 0; j < columns; j += 1 {
|
||||
x[i][j] = temp[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mix_bytes :: #force_inline proc (x: [][16]byte, columns: int) {
|
||||
temp: [8]byte
|
||||
|
||||
for i := 0; i < columns; i += 1 {
|
||||
for j := 0; j < 8; j += 1 {
|
||||
temp[j] = MUL2(x[(j + 0) % 8][i]) ~
|
||||
MUL2(x[(j + 1) % 8][i]) ~
|
||||
MUL3(x[(j + 2) % 8][i]) ~
|
||||
MUL4(x[(j + 3) % 8][i]) ~
|
||||
MUL5(x[(j + 4) % 8][i]) ~
|
||||
MUL3(x[(j + 5) % 8][i]) ~
|
||||
MUL5(x[(j + 6) % 8][i]) ~
|
||||
MUL7(x[(j + 7) % 8][i])
|
||||
}
|
||||
for j := 0; j < 8; j += 1 {
|
||||
x[j][i] = temp[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p :: #force_inline proc (ctx: ^Groestl_Context, x: [][16]byte) {
|
||||
v := ctx.columns == 8 ? Groestl_Variant.P512 : Groestl_Variant.P1024
|
||||
for i := 0; i < ctx.rounds; i += 1 {
|
||||
add_roundconstant(x, ctx.columns, byte(i), v)
|
||||
sub_bytes(x, ctx.columns)
|
||||
shift_bytes(x, ctx.columns, v)
|
||||
mix_bytes(x, ctx.columns)
|
||||
}
|
||||
}
|
||||
|
||||
q :: #force_inline proc (ctx: ^Groestl_Context, x: [][16]byte) {
|
||||
v := ctx.columns == 8 ? Groestl_Variant.Q512 : Groestl_Variant.Q1024
|
||||
for i := 0; i < ctx.rounds; i += 1 {
|
||||
add_roundconstant(x, ctx.columns, byte(i), v)
|
||||
sub_bytes(x, ctx.columns)
|
||||
shift_bytes(x, ctx.columns, v)
|
||||
mix_bytes(x, ctx.columns)
|
||||
}
|
||||
}
|
||||
|
||||
transform :: proc(ctx: ^Groestl_Context, input: []byte, msglen: u32) {
|
||||
tmp1, tmp2: [8][16]byte
|
||||
input, msglen := input, msglen
|
||||
|
||||
for msglen >= u32(ctx.statesize) {
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
tmp1[i][j] = ctx.chaining[i][j] ~ input[j * 8 + i]
|
||||
tmp2[i][j] = input[j * 8 + i]
|
||||
}
|
||||
}
|
||||
|
||||
p(ctx, tmp1[:])
|
||||
q(ctx, tmp2[:])
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
ctx.chaining[i][j] ~= tmp1[i][j] ~ tmp2[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
ctx.block_counter += 1
|
||||
msglen -= u32(ctx.statesize)
|
||||
input = input[ctx.statesize:]
|
||||
}
|
||||
}
|
||||
|
||||
output_transformation :: proc(ctx: ^Groestl_Context) {
|
||||
temp: [8][16]byte
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
temp[i][j] = ctx.chaining[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
p(ctx, temp[:])
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
ctx.chaining[i][j] ~= temp[i][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_roundconstant :: proc(x: [][16]byte, columns: int, round: byte, v: Groestl_Variant) {
|
||||
switch (i32(v) & 1) {
|
||||
case 0:
|
||||
for i := 0; i < columns; i += 1 {
|
||||
x[0][i] ~= byte(i << 4) ~ round
|
||||
}
|
||||
case 1:
|
||||
for i := 0; i < columns; i += 1 {
|
||||
for j := 0; j < 7; j += 1 {
|
||||
x[j][i] ~= 0xff
|
||||
}
|
||||
}
|
||||
for i := 0; i < columns; i += 1 {
|
||||
x[7][i] ~= byte(i << 4) ~ 0xff ~ round
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user