mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-06 03:31:38 -07:00
Compare commits
689 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 | |||
| 301b9eef31 | |||
| 36ed8fe55d | |||
| 7b9ea9eca0 | |||
| 89404147f6 | |||
| 29d4bdc80b | |||
| 594078cc1d | |||
| ab68e4c6c4 | |||
| 4956f9dad3 | |||
| fd090c6672 | |||
| b46b3010ec | |||
| f93f2dfd5c | |||
| 9ac619f4a2 | |||
| 239d4e1076 | |||
| 99825a28d7 | |||
| 144504a752 | |||
| 59aa05170d | |||
| 0b2f357bbe | |||
| 9a1c4dc56d | |||
| 7eabeda870 |
+52
-11
@@ -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
|
||||
@@ -92,13 +99,47 @@ 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
|
||||
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 check examples/all for Linux arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
|
||||
- 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
|
||||
run: |
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
build_windows:
|
||||
name: Windows Build, Check, and Test
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
@@ -7,6 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build_windows:
|
||||
name: Windows Build
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
@@ -40,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
|
||||
@@ -61,27 +67,28 @@ jobs:
|
||||
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
|
||||
@@ -91,14 +98,53 @@ jobs:
|
||||
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
|
||||
@@ -129,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:
|
||||
@@ -143,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"
|
||||
|
||||
+3
-1
@@ -28,6 +28,7 @@ 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
|
||||
@@ -39,7 +40,7 @@ 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_hash
|
||||
tests/core/test_crypto
|
||||
tests/core/test_hash
|
||||
tests/core/test_hxa
|
||||
tests/core/test_json
|
||||
@@ -49,6 +50,7 @@ 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/
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -38,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))) ---
|
||||
|
||||
@@ -293,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)
|
||||
|
||||
+27
-4
@@ -177,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,
|
||||
@@ -223,6 +235,7 @@ Type_Info :: struct {
|
||||
Type_Info_Relative_Multi_Pointer,
|
||||
Type_Info_Matrix,
|
||||
Type_Info_Soa_Pointer,
|
||||
Type_Info_Bit_Field,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -256,6 +269,7 @@ Typeid_Kind :: enum u8 {
|
||||
Relative_Multi_Pointer,
|
||||
Matrix,
|
||||
Soa_Pointer,
|
||||
Bit_Field,
|
||||
}
|
||||
#assert(len(Typeid_Kind) < 32)
|
||||
|
||||
@@ -270,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
|
||||
|
||||
@@ -296,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
|
||||
@@ -575,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
|
||||
}
|
||||
}
|
||||
@@ -591,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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -447,12 +447,12 @@ _append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero:
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: 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, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -496,12 +496,12 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
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, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -556,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
|
||||
}
|
||||
@@ -574,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
|
||||
}
|
||||
@@ -740,6 +740,9 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int,
|
||||
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
|
||||
}
|
||||
@@ -824,9 +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))
|
||||
}
|
||||
|
||||
// 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
|
||||
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 {
|
||||
|
||||
@@ -86,7 +86,6 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
|
||||
return
|
||||
}
|
||||
|
||||
array.allocator = allocator
|
||||
footer := raw_soa_footer(&array)
|
||||
if size_of(E) == 0 {
|
||||
footer.len = length
|
||||
@@ -426,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
|
||||
}
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
//+private
|
||||
//+build linux, darwin, freebsd, openbsd
|
||||
//+build linux, darwin, freebsd, openbsd, haiku
|
||||
//+no-instrumentation
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+build linux, darwin, freebsd, openbsd
|
||||
//+build linux, darwin, freebsd, openbsd, haiku
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
@@ -35,4 +35,4 @@ _heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
|
||||
_heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,7 +22,7 @@ byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byt
|
||||
return ([^]byte)(data)[:max(len, 0)]
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -40,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
|
||||
}
|
||||
@@ -962,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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1034,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
|
||||
}
|
||||
}
|
||||
@@ -2,25 +2,14 @@
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
foreign import libc "system:System.framework"
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__stderrp")
|
||||
_stderr: rawptr
|
||||
|
||||
@(link_name="fwrite")
|
||||
_fwrite :: proc(ptr: rawptr, size: uint, nmemb: uint, stream: rawptr) -> uint ---
|
||||
|
||||
@(link_name="__error")
|
||||
_get_errno :: proc() -> ^i32 ---
|
||||
}
|
||||
import "base:intrinsics"
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
ret := _fwrite(raw_data(data), 1, len(data), _stderr)
|
||||
if ret < len(data) {
|
||||
err := _get_errno()
|
||||
return int(ret), _OS_Errno(err^ if err != nil else 0)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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[")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
+8
-4
@@ -56,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)"
|
||||
@@ -83,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"
|
||||
;;
|
||||
@@ -97,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
|
||||
|
||||
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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,6 +4,8 @@ package libc
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:c"
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="none")
|
||||
foreign _ {
|
||||
@@ -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)
|
||||
|
||||
@@ -163,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
+16
-70
@@ -1,84 +1,30 @@
|
||||
# crypto
|
||||
|
||||
A cryptography 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 some of the options.
|
||||
|
||||
## Hashing algorithms
|
||||
|
||||
| Algorithm | |
|
||||
|:-------------------------------------------------------------------------------------------------------------|:-----------------|
|
||||
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
||||
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
||||
| [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) | ✔️ |
|
||||
| legacy/[Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| legacy/[MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ |
|
||||
| legacy/[SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | ✔️ |
|
||||
|
||||
#### 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)
|
||||
|
||||
\* On some algorithms there is another part to the name, since they might offer control about additional parameters.
|
||||
For instance, `SHA-2` offers different sizes.
|
||||
Computing a 512-bit hash is therefore achieved by calling `sha2.hash_512(...)`.
|
||||
|
||||
#### Low level API
|
||||
|
||||
The above mentioned procedures internally call three procedures: `init`, `update` and `final`.
|
||||
You may also directly call them, if you wish.
|
||||
|
||||
#### Example
|
||||
|
||||
```odin
|
||||
package crypto_example
|
||||
|
||||
// Import the desired package
|
||||
import "core:crypto/blake2b"
|
||||
|
||||
main :: proc() {
|
||||
input := "foo"
|
||||
|
||||
// Compute the hash, using the high level API
|
||||
computed_hash := blake2b.hash(input)
|
||||
|
||||
// Variant that takes a destination buffer, instead of returning the computed hash
|
||||
hash := make([]byte, sha2.DIGEST_SIZE) // @note: Destination buffer has to be at least as big as the digest size of the hash
|
||||
blake2b.hash(input, hash[:])
|
||||
|
||||
// Compute the hash, using the low level API
|
||||
ctx: blake2b.Context
|
||||
computed_hash_low: [blake2b.DIGEST_SIZE]byte
|
||||
blake2b.init(&ctx)
|
||||
blake2b.update(&ctx, transmute([]byte)input)
|
||||
blake2b.final(&ctx, computed_hash_low[:])
|
||||
}
|
||||
```
|
||||
For example uses of all available algorithms, please see the tests within `tests/core/crypto`.
|
||||
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.
|
||||
|
||||
## Implementation considerations
|
||||
|
||||
- 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
|
||||
architectures. Architectures that are known to be unreasonable include
|
||||
but are not limited to i386, i486, and WebAssembly.
|
||||
- Some but not all of the packages attempt to santize sensitive data,
|
||||
however this is not done consistently through the library at the moment.
|
||||
As Thomas Pornin puts it "In general, such memory cleansing is a fool's
|
||||
quest."
|
||||
- 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.
|
||||
|
||||
## License
|
||||
|
||||
@@ -11,6 +11,7 @@ package _blake2
|
||||
*/
|
||||
|
||||
import "core:encoding/endian"
|
||||
import "core:mem"
|
||||
|
||||
BLAKE2S_BLOCK_SIZE :: 64
|
||||
BLAKE2S_SIZE :: 32
|
||||
@@ -28,7 +29,6 @@ Blake2s_Context :: struct {
|
||||
is_keyed: bool,
|
||||
size: byte,
|
||||
is_last_node: bool,
|
||||
cfg: Blake2_Config,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
@@ -44,7 +44,6 @@ Blake2b_Context :: struct {
|
||||
is_keyed: bool,
|
||||
size: byte,
|
||||
is_last_node: bool,
|
||||
cfg: Blake2_Config,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
@@ -83,62 +82,61 @@ 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
|
||||
}
|
||||
|
||||
if ctx.cfg.size > max_size {
|
||||
if cfg.size > max_size {
|
||||
panic("blake2: requested output size exceeeds algorithm max")
|
||||
}
|
||||
|
||||
p := make([]byte, block_size)
|
||||
defer delete(p)
|
||||
// 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[:]
|
||||
|
||||
p[0] = ctx.cfg.size
|
||||
p[1] = byte(len(ctx.cfg.key))
|
||||
p[0] = cfg.size
|
||||
p[1] = byte(len(cfg.key))
|
||||
|
||||
if ctx.cfg.salt != nil {
|
||||
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
|
||||
endian.unchecked_put_u32le(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 {
|
||||
endian.unchecked_put_u64le(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] ~ endian.unchecked_get_u32le(p[i * 4:])
|
||||
@@ -147,11 +145,14 @@ init :: proc(ctx: ^$T) {
|
||||
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
|
||||
}
|
||||
@@ -194,22 +195,40 @@ update :: proc(ctx: ^$T, p: []byte) {
|
||||
ctx.nx += copy(ctx.x[ctx.nx:], p)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^$T, hash: []byte) {
|
||||
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
|
||||
}
|
||||
defer(reset(ctx))
|
||||
|
||||
when T == Blake2s_Context {
|
||||
if len(hash) < int(ctx.cfg.size) {
|
||||
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.cfg.size) {
|
||||
if len(hash) < int(ctx.size) {
|
||||
panic("crypto/blake2b: invalid destination digest size")
|
||||
}
|
||||
blake2b_final(ctx, hash)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.is_initialized = false
|
||||
clone :: proc(ctx, other: ^$T) {
|
||||
ctx^ = other^
|
||||
}
|
||||
|
||||
reset :: proc(ctx: ^$T) {
|
||||
if !ctx.is_initialized {
|
||||
return
|
||||
}
|
||||
|
||||
mem.zero_explicit(ctx, size_of(ctx^))
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
@@ -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 "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) {
|
||||
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,7 +29,9 @@ 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)
|
||||
if len(arg1) != 16 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
|
||||
// While it may be unwise to do deserialization here on our
|
||||
// own when fiat-crypto provides equivalent functionality,
|
||||
@@ -51,3 +62,35 @@ fe_from_u64s :: proc "contextless" (out1: ^Tight_Field_Element, lo, hi: u64) {
|
||||
// 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
|
||||
}
|
||||
+109
-78
@@ -7,50 +7,69 @@ 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 "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
ROUNDS :: 24
|
||||
|
||||
Sha3_Context :: struct {
|
||||
st: struct #raw_union {
|
||||
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,
|
||||
is_keccak: bool,
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
i, j, r: i32 = ---, ---, ---
|
||||
t: u64 = ---
|
||||
bc: [5]u64 = ---
|
||||
@@ -103,81 +122,93 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
|
||||
}
|
||||
}
|
||||
|
||||
init :: proc(c: ^Sha3_Context) {
|
||||
init :: proc(ctx: ^Context) {
|
||||
for i := 0; i < 25; i += 1 {
|
||||
c.st.q[i] = 0
|
||||
ctx.st.q[i] = 0
|
||||
}
|
||||
c.rsiz = 200 - 2 * c.mdlen
|
||||
c.pt = 0
|
||||
ctx.rsiz = 200 - 2 * ctx.mdlen
|
||||
ctx.pt = 0
|
||||
|
||||
c.is_initialized = true
|
||||
c.is_finalized = false
|
||||
ctx.is_initialized = true
|
||||
ctx.is_finalized = false
|
||||
}
|
||||
|
||||
update :: proc(c: ^Sha3_Context, data: []byte) {
|
||||
assert(c.is_initialized)
|
||||
assert(!c.is_finalized)
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx.is_initialized)
|
||||
assert(!ctx.is_finalized)
|
||||
|
||||
j := c.pt
|
||||
j := ctx.pt
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
c.st.b[j] ~= data[i]
|
||||
ctx.st.b[j] ~= data[i]
|
||||
j += 1
|
||||
if j >= c.rsiz {
|
||||
keccakf(&c.st.q)
|
||||
if j >= ctx.rsiz {
|
||||
keccakf(&ctx.st.q)
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
c.pt = j
|
||||
ctx.pt = j
|
||||
}
|
||||
|
||||
final :: proc(c: ^Sha3_Context, hash: []byte) {
|
||||
assert(c.is_initialized)
|
||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
if len(hash) < c.mdlen {
|
||||
if c.is_keccak {
|
||||
panic("crypto/keccac: invalid destination digest size")
|
||||
}
|
||||
if len(hash) < ctx.mdlen {
|
||||
panic("crypto/sha3: invalid destination digest size")
|
||||
}
|
||||
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]
|
||||
ctx := ctx
|
||||
if finalize_clone {
|
||||
tmp_ctx: Context
|
||||
clone(&tmp_ctx, ctx)
|
||||
ctx = &tmp_ctx
|
||||
}
|
||||
defer (reset(ctx))
|
||||
|
||||
c.is_initialized = false // No more absorb, no more squeeze.
|
||||
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(c: ^Sha3_Context) {
|
||||
assert(c.is_initialized)
|
||||
assert(!c.is_finalized)
|
||||
|
||||
c.st.b[c.pt] ~= 0x1F
|
||||
c.st.b[c.rsiz - 1] ~= 0x80
|
||||
keccakf(&c.st.q)
|
||||
c.pt = 0
|
||||
|
||||
c.is_finalized = true // No more absorb, unlimited squeeze.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
ctx^ = other^
|
||||
}
|
||||
|
||||
shake_out :: proc(c: ^Sha3_Context, hash: []byte) {
|
||||
assert(c.is_initialized)
|
||||
assert(c.is_finalized)
|
||||
reset :: proc(ctx: ^Context) {
|
||||
if !ctx.is_initialized {
|
||||
return
|
||||
}
|
||||
|
||||
j := c.pt
|
||||
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 >= c.rsiz {
|
||||
keccakf(&c.st.q)
|
||||
if j >= ctx.rsiz {
|
||||
keccakf(&ctx.st.q)
|
||||
j = 0
|
||||
}
|
||||
hash[i] = c.st.b[j]
|
||||
hash[i] = ctx.st.b[j]
|
||||
j += 1
|
||||
}
|
||||
c.pt = j
|
||||
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,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 BLAKE2s share the implementation in the _blake2 package.
|
||||
*/
|
||||
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
// 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: Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
ctx.cfg = cfg
|
||||
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) {
|
||||
ctx: Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
ctx.cfg = cfg
|
||||
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: Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
ctx.cfg = cfg
|
||||
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
|
||||
*/
|
||||
// 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) {
|
||||
_blake2.init(ctx)
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
_blake2.init(ctx, &cfg)
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Context, hash: []byte) {
|
||||
_blake2.final(ctx, 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)
|
||||
}
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_blake2.clone(ctx, other)
|
||||
}
|
||||
|
||||
// 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.
|
||||
BLAKE2s and BLAKE2b share the implementation in the _blake2 package.
|
||||
*/
|
||||
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
// 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: Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
ctx.cfg = cfg
|
||||
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) {
|
||||
ctx: Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
ctx.cfg = cfg
|
||||
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: Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
ctx.cfg = cfg
|
||||
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
|
||||
*/
|
||||
// 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) {
|
||||
_blake2.init(ctx)
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
_blake2.init(ctx, &cfg)
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Context, hash: []byte) {
|
||||
_blake2.final(ctx, 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)
|
||||
}
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_blake2.clone(ctx, other)
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
_blake2.reset(ctx)
|
||||
}
|
||||
|
||||
@@ -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: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")
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
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"
|
||||
@@ -6,8 +13,11 @@ import "core:crypto/poly1305"
|
||||
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) {
|
||||
@@ -95,6 +107,11 @@ encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
|
||||
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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
package hash provides a generic interface to the supported hash algorithms.
|
||||
|
||||
A high-level convenience procedure group `hash` is provided to easily
|
||||
accomplish common tasks.
|
||||
- `hash_string` - Hash a given string and return the digest.
|
||||
- `hash_bytes` - Hash a given byte slice and return the digest.
|
||||
- `hash_string_to_buffer` - Hash a given string and put the digest in
|
||||
the third parameter. It requires that the destination buffer
|
||||
is at least as big as the digest size.
|
||||
- `hash_bytes_to_buffer` - Hash a given string and put the computed
|
||||
digest in the third parameter. It requires that the destination
|
||||
buffer is at least as big as the digest size.
|
||||
- `hash_stream` - Incrementally fully consume a `io.Stream`, and return
|
||||
the computed digest.
|
||||
- `hash_file` - Takes a file handle and returns the computed digest.
|
||||
A third optional boolean parameter controls if the file is streamed
|
||||
(default), or or read at once.
|
||||
|
||||
```odin
|
||||
package hash_example
|
||||
|
||||
import "core:crypto/hash"
|
||||
|
||||
main :: proc() {
|
||||
input := "Feed the fire."
|
||||
|
||||
// Compute the digest, using the high level API.
|
||||
returned_digest := hash.hash(hash.Algorithm.SHA512_256, input)
|
||||
defer delete(returned_digest)
|
||||
|
||||
// Variant that takes a destination buffer, instead of returning
|
||||
// the digest.
|
||||
digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.BLAKE2B]) // @note: Destination buffer has to be at least as big as the digest size of the hash.
|
||||
defer delete(digest)
|
||||
hash.hash(hash.Algorithm.BLAKE2B, input, digest)
|
||||
}
|
||||
```
|
||||
|
||||
A generic low level API is provided supporting the init/update/final interface
|
||||
that is typical with cryptographic hash function implementations.
|
||||
|
||||
```odin
|
||||
package hash_example
|
||||
|
||||
import "core:crypto/hash"
|
||||
|
||||
main :: proc() {
|
||||
input := "Let the cinders burn."
|
||||
|
||||
// Compute the digest, using the low level API.
|
||||
ctx: hash.Context
|
||||
digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.SHA3_512])
|
||||
defer delete(digest)
|
||||
|
||||
hash.init(&ctx, hash.Algorithm.SHA3_512)
|
||||
hash.update(&ctx, transmute([]byte)input)
|
||||
hash.final(&ctx, digest)
|
||||
}
|
||||
```
|
||||
*/
|
||||
package crypto_hash
|
||||
@@ -0,0 +1,116 @@
|
||||
package crypto_hash
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
*/
|
||||
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
|
||||
// hash_bytes will hash the given input and return the computed digest
|
||||
// in a newly allocated slice.
|
||||
hash_string :: proc(algorithm: Algorithm, data: string, allocator := context.allocator) -> []byte {
|
||||
return hash_bytes(algorithm, transmute([]byte)(data), allocator)
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the computed digest
|
||||
// in a newly allocated slice.
|
||||
hash_bytes :: proc(algorithm: Algorithm, data: []byte, allocator := context.allocator) -> []byte {
|
||||
dst := make([]byte, DIGEST_SIZES[algorithm], allocator)
|
||||
hash_bytes_to_buffer(algorithm, data, dst)
|
||||
return dst
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed digest to the third parameter. It requires that the
|
||||
// destination buffer is at least as big as the digest size.
|
||||
hash_string_to_buffer :: proc(algorithm: Algorithm, data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(algorithm, transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed digest into the third parameter. It requires that the
|
||||
// destination buffer is at least as big as the digest size.
|
||||
hash_bytes_to_buffer :: proc(algorithm: Algorithm, data, hash: []byte) {
|
||||
ctx: Context
|
||||
|
||||
init(&ctx, algorithm)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream will incrementally fully consume a stream, and return the
|
||||
// computed digest in a newly allocated slice.
|
||||
hash_stream :: proc(
|
||||
algorithm: Algorithm,
|
||||
s: io.Stream,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
[]byte,
|
||||
io.Error,
|
||||
) {
|
||||
ctx: Context
|
||||
|
||||
buf: [MAX_BLOCK_SIZE * 4]byte
|
||||
defer mem.zero_explicit(&buf, size_of(buf))
|
||||
|
||||
init(&ctx, algorithm)
|
||||
|
||||
loop: for {
|
||||
n, err := io.read(s, buf[:])
|
||||
if n > 0 {
|
||||
// XXX/yawning: Can io.read return n > 0 and EOF?
|
||||
update(&ctx, buf[:n])
|
||||
}
|
||||
#partial switch err {
|
||||
case .None:
|
||||
case .EOF:
|
||||
break loop
|
||||
case:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
dst := make([]byte, DIGEST_SIZES[algorithm], allocator)
|
||||
final(&ctx, dst)
|
||||
|
||||
return dst, io.Error.None
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle and return the
|
||||
// computed digest in a newly allocated slice.
|
||||
hash_file :: proc(
|
||||
algorithm: Algorithm,
|
||||
hd: os.Handle,
|
||||
load_at_once := false,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
[]byte,
|
||||
io.Error,
|
||||
) {
|
||||
if !load_at_once {
|
||||
return hash_stream(algorithm, os.stream_from_handle(hd), allocator)
|
||||
}
|
||||
|
||||
buf, ok := os.read_entire_file(hd, allocator)
|
||||
if !ok {
|
||||
return nil, io.Error.Unknown
|
||||
}
|
||||
defer delete(buf, allocator)
|
||||
|
||||
return hash_bytes(algorithm, buf, allocator), io.Error.None
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
package crypto_hash
|
||||
|
||||
import "core:crypto/blake2b"
|
||||
import "core:crypto/blake2s"
|
||||
import "core:crypto/sha2"
|
||||
import "core:crypto/sha3"
|
||||
import "core:crypto/sm3"
|
||||
import "core:crypto/legacy/keccak"
|
||||
import "core:crypto/legacy/md5"
|
||||
import "core:crypto/legacy/sha1"
|
||||
|
||||
import "core:reflect"
|
||||
|
||||
// MAX_DIGEST_SIZE is the maximum size digest that can be returned by any
|
||||
// of the Algorithms supported via this package.
|
||||
MAX_DIGEST_SIZE :: 64
|
||||
// MAX_BLOCK_SIZE is the maximum block size used by any of Algorithms
|
||||
// supported by this package.
|
||||
MAX_BLOCK_SIZE :: sha3.BLOCK_SIZE_224
|
||||
|
||||
// Algorithm is the algorithm identifier associated with a given Context.
|
||||
Algorithm :: enum {
|
||||
Invalid,
|
||||
BLAKE2B,
|
||||
BLAKE2S,
|
||||
SHA224,
|
||||
SHA256,
|
||||
SHA384,
|
||||
SHA512,
|
||||
SHA512_256,
|
||||
SHA3_224,
|
||||
SHA3_256,
|
||||
SHA3_384,
|
||||
SHA3_512,
|
||||
SM3,
|
||||
Legacy_KECCAK_224,
|
||||
Legacy_KECCAK_256,
|
||||
Legacy_KECCAK_384,
|
||||
Legacy_KECCAK_512,
|
||||
Insecure_MD5,
|
||||
Insecure_SHA1,
|
||||
}
|
||||
|
||||
// ALGORITHM_NAMES is the Algorithm to algorithm name string.
|
||||
ALGORITHM_NAMES := [Algorithm]string {
|
||||
.Invalid = "Invalid",
|
||||
.BLAKE2B = "BLAKE2b",
|
||||
.BLAKE2S = "BLAKE2s",
|
||||
.SHA224 = "SHA-224",
|
||||
.SHA256 = "SHA-256",
|
||||
.SHA384 = "SHA-384",
|
||||
.SHA512 = "SHA-512",
|
||||
.SHA512_256 = "SHA-512/256",
|
||||
.SHA3_224 = "SHA3-224",
|
||||
.SHA3_256 = "SHA3-256",
|
||||
.SHA3_384 = "SHA3-384",
|
||||
.SHA3_512 = "SHA3-512",
|
||||
.SM3 = "SM3",
|
||||
.Legacy_KECCAK_224 = "Keccak-224",
|
||||
.Legacy_KECCAK_256 = "Keccak-256",
|
||||
.Legacy_KECCAK_384 = "Keccak-384",
|
||||
.Legacy_KECCAK_512 = "Keccak-512",
|
||||
.Insecure_MD5 = "MD5",
|
||||
.Insecure_SHA1 = "SHA-1",
|
||||
}
|
||||
|
||||
// DIGEST_SIZES is the Algorithm to digest size in bytes.
|
||||
DIGEST_SIZES := [Algorithm]int {
|
||||
.Invalid = 0,
|
||||
.BLAKE2B = blake2b.DIGEST_SIZE,
|
||||
.BLAKE2S = blake2s.DIGEST_SIZE,
|
||||
.SHA224 = sha2.DIGEST_SIZE_224,
|
||||
.SHA256 = sha2.DIGEST_SIZE_256,
|
||||
.SHA384 = sha2.DIGEST_SIZE_384,
|
||||
.SHA512 = sha2.DIGEST_SIZE_512,
|
||||
.SHA512_256 = sha2.DIGEST_SIZE_512_256,
|
||||
.SHA3_224 = sha3.DIGEST_SIZE_224,
|
||||
.SHA3_256 = sha3.DIGEST_SIZE_256,
|
||||
.SHA3_384 = sha3.DIGEST_SIZE_384,
|
||||
.SHA3_512 = sha3.DIGEST_SIZE_512,
|
||||
.SM3 = sm3.DIGEST_SIZE,
|
||||
.Legacy_KECCAK_224 = keccak.DIGEST_SIZE_224,
|
||||
.Legacy_KECCAK_256 = keccak.DIGEST_SIZE_256,
|
||||
.Legacy_KECCAK_384 = keccak.DIGEST_SIZE_384,
|
||||
.Legacy_KECCAK_512 = keccak.DIGEST_SIZE_512,
|
||||
.Insecure_MD5 = md5.DIGEST_SIZE,
|
||||
.Insecure_SHA1 = sha1.DIGEST_SIZE,
|
||||
}
|
||||
|
||||
// BLOCK_SIZES is the Algoritm to block size in bytes.
|
||||
BLOCK_SIZES := [Algorithm]int {
|
||||
.Invalid = 0,
|
||||
.BLAKE2B = blake2b.BLOCK_SIZE,
|
||||
.BLAKE2S = blake2s.BLOCK_SIZE,
|
||||
.SHA224 = sha2.BLOCK_SIZE_256,
|
||||
.SHA256 = sha2.BLOCK_SIZE_256,
|
||||
.SHA384 = sha2.BLOCK_SIZE_512,
|
||||
.SHA512 = sha2.BLOCK_SIZE_512,
|
||||
.SHA512_256 = sha2.BLOCK_SIZE_512,
|
||||
.SHA3_224 = sha3.BLOCK_SIZE_224,
|
||||
.SHA3_256 = sha3.BLOCK_SIZE_256,
|
||||
.SHA3_384 = sha3.BLOCK_SIZE_384,
|
||||
.SHA3_512 = sha3.BLOCK_SIZE_512,
|
||||
.SM3 = sm3.BLOCK_SIZE,
|
||||
.Legacy_KECCAK_224 = keccak.BLOCK_SIZE_224,
|
||||
.Legacy_KECCAK_256 = keccak.BLOCK_SIZE_256,
|
||||
.Legacy_KECCAK_384 = keccak.BLOCK_SIZE_384,
|
||||
.Legacy_KECCAK_512 = keccak.BLOCK_SIZE_512,
|
||||
.Insecure_MD5 = md5.BLOCK_SIZE,
|
||||
.Insecure_SHA1 = sha1.BLOCK_SIZE,
|
||||
}
|
||||
|
||||
// Context is a concrete instantiation of a specific hash algorithm.
|
||||
Context :: struct {
|
||||
_algo: Algorithm,
|
||||
_impl: union {
|
||||
blake2b.Context,
|
||||
blake2s.Context,
|
||||
sha2.Context_256,
|
||||
sha2.Context_512,
|
||||
sha3.Context,
|
||||
sm3.Context,
|
||||
keccak.Context,
|
||||
md5.Context,
|
||||
sha1.Context,
|
||||
},
|
||||
}
|
||||
|
||||
@(private)
|
||||
_IMPL_IDS := [Algorithm]typeid {
|
||||
.Invalid = nil,
|
||||
.BLAKE2B = typeid_of(blake2b.Context),
|
||||
.BLAKE2S = typeid_of(blake2s.Context),
|
||||
.SHA224 = typeid_of(sha2.Context_256),
|
||||
.SHA256 = typeid_of(sha2.Context_256),
|
||||
.SHA384 = typeid_of(sha2.Context_512),
|
||||
.SHA512 = typeid_of(sha2.Context_512),
|
||||
.SHA512_256 = typeid_of(sha2.Context_512),
|
||||
.SHA3_224 = typeid_of(sha3.Context),
|
||||
.SHA3_256 = typeid_of(sha3.Context),
|
||||
.SHA3_384 = typeid_of(sha3.Context),
|
||||
.SHA3_512 = typeid_of(sha3.Context),
|
||||
.SM3 = typeid_of(sm3.Context),
|
||||
.Legacy_KECCAK_224 = typeid_of(keccak.Context),
|
||||
.Legacy_KECCAK_256 = typeid_of(keccak.Context),
|
||||
.Legacy_KECCAK_384 = typeid_of(keccak.Context),
|
||||
.Legacy_KECCAK_512 = typeid_of(keccak.Context),
|
||||
.Insecure_MD5 = typeid_of(md5.Context),
|
||||
.Insecure_SHA1 = typeid_of(sha1.Context),
|
||||
}
|
||||
|
||||
// init initializes a Context with a specific hash Algorithm.
|
||||
init :: proc(ctx: ^Context, algorithm: Algorithm) {
|
||||
if ctx._impl != nil {
|
||||
reset(ctx)
|
||||
}
|
||||
|
||||
// Directly specialize the union by setting the type ID (save a copy).
|
||||
reflect.set_union_variant_typeid(
|
||||
ctx._impl,
|
||||
_IMPL_IDS[algorithm],
|
||||
)
|
||||
switch algorithm {
|
||||
case .BLAKE2B:
|
||||
blake2b.init(&ctx._impl.(blake2b.Context))
|
||||
case .BLAKE2S:
|
||||
blake2s.init(&ctx._impl.(blake2s.Context))
|
||||
case .SHA224:
|
||||
sha2.init_224(&ctx._impl.(sha2.Context_256))
|
||||
case .SHA256:
|
||||
sha2.init_256(&ctx._impl.(sha2.Context_256))
|
||||
case .SHA384:
|
||||
sha2.init_384(&ctx._impl.(sha2.Context_512))
|
||||
case .SHA512:
|
||||
sha2.init_512(&ctx._impl.(sha2.Context_512))
|
||||
case .SHA512_256:
|
||||
sha2.init_512_256(&ctx._impl.(sha2.Context_512))
|
||||
case .SHA3_224:
|
||||
sha3.init_224(&ctx._impl.(sha3.Context))
|
||||
case .SHA3_256:
|
||||
sha3.init_256(&ctx._impl.(sha3.Context))
|
||||
case .SHA3_384:
|
||||
sha3.init_384(&ctx._impl.(sha3.Context))
|
||||
case .SHA3_512:
|
||||
sha3.init_512(&ctx._impl.(sha3.Context))
|
||||
case .SM3:
|
||||
sm3.init(&ctx._impl.(sm3.Context))
|
||||
case .Legacy_KECCAK_224:
|
||||
keccak.init_224(&ctx._impl.(keccak.Context))
|
||||
case .Legacy_KECCAK_256:
|
||||
keccak.init_256(&ctx._impl.(keccak.Context))
|
||||
case .Legacy_KECCAK_384:
|
||||
keccak.init_384(&ctx._impl.(keccak.Context))
|
||||
case .Legacy_KECCAK_512:
|
||||
keccak.init_512(&ctx._impl.(keccak.Context))
|
||||
case .Insecure_MD5:
|
||||
md5.init(&ctx._impl.(md5.Context))
|
||||
case .Insecure_SHA1:
|
||||
sha1.init(&ctx._impl.(sha1.Context))
|
||||
case .Invalid:
|
||||
panic("crypto/hash: uninitialized algorithm")
|
||||
case:
|
||||
panic("crypto/hash: invalid algorithm")
|
||||
}
|
||||
|
||||
ctx._algo = algorithm
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
switch &impl in ctx._impl {
|
||||
case blake2b.Context:
|
||||
blake2b.update(&impl, data)
|
||||
case blake2s.Context:
|
||||
blake2s.update(&impl, data)
|
||||
case sha2.Context_256:
|
||||
sha2.update(&impl, data)
|
||||
case sha2.Context_512:
|
||||
sha2.update(&impl, data)
|
||||
case sha3.Context:
|
||||
sha3.update(&impl, data)
|
||||
case sm3.Context:
|
||||
sm3.update(&impl, data)
|
||||
case keccak.Context:
|
||||
keccak.update(&impl, data)
|
||||
case md5.Context:
|
||||
md5.update(&impl, data)
|
||||
case sha1.Context:
|
||||
sha1.update(&impl, data)
|
||||
case:
|
||||
panic("crypto/hash: uninitialized algorithm")
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
switch &impl in ctx._impl {
|
||||
case blake2b.Context:
|
||||
blake2b.final(&impl, hash, finalize_clone)
|
||||
case blake2s.Context:
|
||||
blake2s.final(&impl, hash, finalize_clone)
|
||||
case sha2.Context_256:
|
||||
sha2.final(&impl, hash, finalize_clone)
|
||||
case sha2.Context_512:
|
||||
sha2.final(&impl, hash, finalize_clone)
|
||||
case sha3.Context:
|
||||
sha3.final(&impl, hash, finalize_clone)
|
||||
case sm3.Context:
|
||||
sm3.final(&impl, hash, finalize_clone)
|
||||
case keccak.Context:
|
||||
keccak.final(&impl, hash, finalize_clone)
|
||||
case md5.Context:
|
||||
md5.final(&impl, hash, finalize_clone)
|
||||
case sha1.Context:
|
||||
sha1.final(&impl, hash, finalize_clone)
|
||||
case:
|
||||
panic("crypto/hash: uninitialized algorithm")
|
||||
}
|
||||
|
||||
if !finalize_clone {
|
||||
reset(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
// XXX/yawning: Maybe these cases should panic, because both cases,
|
||||
// are probably bugs.
|
||||
if ctx == other {
|
||||
return
|
||||
}
|
||||
if ctx._impl != nil {
|
||||
reset(ctx)
|
||||
}
|
||||
|
||||
ctx._algo = other._algo
|
||||
|
||||
reflect.set_union_variant_typeid(
|
||||
ctx._impl,
|
||||
reflect.union_variant_typeid(other._impl),
|
||||
)
|
||||
switch &src_impl in other._impl {
|
||||
case blake2b.Context:
|
||||
blake2b.clone(&ctx._impl.(blake2b.Context), &src_impl)
|
||||
case blake2s.Context:
|
||||
blake2s.clone(&ctx._impl.(blake2s.Context), &src_impl)
|
||||
case sha2.Context_256:
|
||||
sha2.clone(&ctx._impl.(sha2.Context_256), &src_impl)
|
||||
case sha2.Context_512:
|
||||
sha2.clone(&ctx._impl.(sha2.Context_512), &src_impl)
|
||||
case sha3.Context:
|
||||
sha3.clone(&ctx._impl.(sha3.Context), &src_impl)
|
||||
case sm3.Context:
|
||||
sm3.clone(&ctx._impl.(sm3.Context), &src_impl)
|
||||
case keccak.Context:
|
||||
keccak.clone(&ctx._impl.(keccak.Context), &src_impl)
|
||||
case md5.Context:
|
||||
md5.clone(&ctx._impl.(md5.Context), &src_impl)
|
||||
case sha1.Context:
|
||||
sha1.clone(&ctx._impl.(sha1.Context), &src_impl)
|
||||
case:
|
||||
panic("crypto/hash: uninitialized algorithm")
|
||||
}
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
switch &impl in ctx._impl {
|
||||
case blake2b.Context:
|
||||
blake2b.reset(&impl)
|
||||
case blake2s.Context:
|
||||
blake2s.reset(&impl)
|
||||
case sha2.Context_256:
|
||||
sha2.reset(&impl)
|
||||
case sha2.Context_512:
|
||||
sha2.reset(&impl)
|
||||
case sha3.Context:
|
||||
sha3.reset(&impl)
|
||||
case sm3.Context:
|
||||
sm3.reset(&impl)
|
||||
case keccak.Context:
|
||||
keccak.reset(&impl)
|
||||
case md5.Context:
|
||||
md5.reset(&impl)
|
||||
case sha1.Context:
|
||||
sha1.reset(&impl)
|
||||
case:
|
||||
// Unlike clone, calling reset repeatedly is fine.
|
||||
}
|
||||
|
||||
ctx._algo = .Invalid
|
||||
ctx._impl = nil
|
||||
}
|
||||
|
||||
// algorithm returns the Algorithm used by a Context instance.
|
||||
algorithm :: proc(ctx: ^Context) -> Algorithm {
|
||||
return ctx._algo
|
||||
}
|
||||
|
||||
// digest_size returns the digest size of a Context instance in bytes.
|
||||
digest_size :: proc(ctx: ^Context) -> int {
|
||||
return DIGEST_SIZES[ctx._algo]
|
||||
}
|
||||
|
||||
// block_size returns the block size of a Context instance in bytes.
|
||||
block_size :: proc(ctx: ^Context) -> int {
|
||||
return BLOCK_SIZES[ctx._algo]
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
package hkdf implements the HKDF HMAC-based Extract-and-Expand Key
|
||||
Derivation Function.
|
||||
|
||||
See: https://www.rfc-editor.org/rfc/rfc5869
|
||||
*/
|
||||
package hkdf
|
||||
|
||||
import "core:crypto/hash"
|
||||
import "core:crypto/hmac"
|
||||
import "core:mem"
|
||||
|
||||
// extract_and_expand derives output keying material (OKM) via the
|
||||
// HKDF-Extract and HKDF-Expand algorithms, with the specified has
|
||||
// function, salt, input keying material (IKM), and optional info.
|
||||
// The dst buffer must be less-than-or-equal to 255 HMAC tags.
|
||||
extract_and_expand :: proc(algorithm: hash.Algorithm, salt, ikm, info, dst: []byte) {
|
||||
h_len := hash.DIGEST_SIZES[algorithm]
|
||||
|
||||
tmp: [hash.MAX_DIGEST_SIZE]byte
|
||||
prk := tmp[:h_len]
|
||||
defer mem.zero_explicit(raw_data(prk), h_len)
|
||||
|
||||
extract(algorithm, salt, ikm, prk)
|
||||
expand(algorithm, prk, info, dst)
|
||||
}
|
||||
|
||||
// extract derives a pseudorandom key (PRK) via the HKDF-Extract algorithm,
|
||||
// with the specified hash function, salt, and input keying material (IKM).
|
||||
// It requires that the dst buffer be the HMAC tag size for the specified
|
||||
// hash function.
|
||||
extract :: proc(algorithm: hash.Algorithm, salt, ikm, dst: []byte) {
|
||||
// PRK = HMAC-Hash(salt, IKM)
|
||||
hmac.sum(algorithm, dst, ikm, salt)
|
||||
}
|
||||
|
||||
// expand derives output keying material (OKM) via the HKDF-Expand algorithm,
|
||||
// with the specified hash function, pseudorandom key (PRK), and optional
|
||||
// info. The dst buffer must be less-than-or-equal to 255 HMAC tags.
|
||||
expand :: proc(algorithm: hash.Algorithm, prk, info, dst: []byte) {
|
||||
h_len := hash.DIGEST_SIZES[algorithm]
|
||||
|
||||
// (<= 255*HashLen)
|
||||
dk_len := len(dst)
|
||||
switch {
|
||||
case dk_len == 0:
|
||||
return
|
||||
case dk_len > h_len * 255:
|
||||
panic("crypto/hkdf: derived key too long")
|
||||
case:
|
||||
}
|
||||
|
||||
// The output OKM is calculated as follows:
|
||||
//
|
||||
// N = ceil(L/HashLen)
|
||||
// T = T(1) | T(2) | T(3) | ... | T(N)
|
||||
// OKM = first L octets of T
|
||||
//
|
||||
// where:
|
||||
// T(0) = empty string (zero length)
|
||||
// T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
|
||||
// T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
|
||||
// T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
|
||||
// ...
|
||||
|
||||
n := dk_len / h_len
|
||||
r := dk_len % h_len
|
||||
|
||||
base: hmac.Context
|
||||
defer hmac.reset(&base)
|
||||
|
||||
hmac.init(&base, algorithm, prk)
|
||||
|
||||
dst_blk := dst
|
||||
prev: []byte
|
||||
|
||||
for i in 1 ..= n {
|
||||
_F(&base, prev, info, i, dst_blk[:h_len])
|
||||
|
||||
prev = dst_blk[:h_len]
|
||||
dst_blk = dst_blk[h_len:]
|
||||
}
|
||||
|
||||
if r > 0 {
|
||||
tmp: [hash.MAX_DIGEST_SIZE]byte
|
||||
blk := tmp[:h_len]
|
||||
defer mem.zero_explicit(raw_data(blk), h_len)
|
||||
|
||||
_F(&base, prev, info, n + 1, blk)
|
||||
copy(dst_blk, blk)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_F :: proc(base: ^hmac.Context, prev, info: []byte, i: int, dst_blk: []byte) {
|
||||
prf: hmac.Context
|
||||
|
||||
hmac.clone(&prf, base)
|
||||
hmac.update(&prf, prev)
|
||||
hmac.update(&prf, info)
|
||||
hmac.update(&prf, []byte{u8(i)})
|
||||
hmac.final(&prf, dst_blk)
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
package hmac implements the HMAC MAC algorithm.
|
||||
|
||||
See:
|
||||
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf
|
||||
*/
|
||||
package hmac
|
||||
|
||||
import "core:crypto"
|
||||
import "core:crypto/hash"
|
||||
import "core:mem"
|
||||
|
||||
// sum will compute the HMAC with the specified algorithm and key
|
||||
// over msg, and write the computed tag to dst. It requires that
|
||||
// the dst buffer is the tag size.
|
||||
sum :: proc(algorithm: hash.Algorithm, dst, msg, key: []byte) {
|
||||
ctx: Context
|
||||
|
||||
init(&ctx, algorithm, key)
|
||||
update(&ctx, msg)
|
||||
final(&ctx, dst)
|
||||
}
|
||||
|
||||
// verify will verify the HMAC tag computed with the specified algorithm
|
||||
// and key over msg and return true iff the tag is valid. It requires
|
||||
// that the tag is correctly sized.
|
||||
verify :: proc(algorithm: hash.Algorithm, tag, msg, key: []byte) -> bool {
|
||||
tag_buf: [hash.MAX_DIGEST_SIZE]byte
|
||||
|
||||
derived_tag := tag_buf[:hash.DIGEST_SIZES[algorithm]]
|
||||
sum(algorithm, derived_tag, msg, key)
|
||||
|
||||
return crypto.compare_constant_time(derived_tag, tag) == 1
|
||||
}
|
||||
|
||||
// Context is a concrete instantiation of HMAC with a specific hash
|
||||
// algorithm.
|
||||
Context :: struct {
|
||||
_o_hash: hash.Context, // H(k ^ ipad) (not finalized)
|
||||
_i_hash: hash.Context, // H(k ^ opad) (not finalized)
|
||||
_tag_sz: int,
|
||||
_is_initialized: bool,
|
||||
}
|
||||
|
||||
// init initializes a Context with a specific hash Algorithm and key.
|
||||
init :: proc(ctx: ^Context, algorithm: hash.Algorithm, key: []byte) {
|
||||
if ctx._is_initialized {
|
||||
reset(ctx)
|
||||
}
|
||||
|
||||
_init_hashes(ctx, algorithm, key)
|
||||
|
||||
ctx._tag_sz = hash.DIGEST_SIZES[algorithm]
|
||||
ctx._is_initialized = true
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
hash.update(&ctx._i_hash, data)
|
||||
}
|
||||
|
||||
// final finalizes the Context, writes the tag to dst, and calls
|
||||
// reset on the Context.
|
||||
final :: proc(ctx: ^Context, dst: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
defer (reset(ctx))
|
||||
|
||||
if len(dst) != ctx._tag_sz {
|
||||
panic("crypto/hmac: invalid destination tag size")
|
||||
}
|
||||
|
||||
hash.final(&ctx._i_hash, dst) // H((k ^ ipad) || text)
|
||||
|
||||
hash.update(&ctx._o_hash, dst) // H((k ^ opad) || H((k ^ ipad) || text))
|
||||
hash.final(&ctx._o_hash, dst)
|
||||
}
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
if ctx == other {
|
||||
return
|
||||
}
|
||||
|
||||
hash.clone(&ctx._o_hash, &other._o_hash)
|
||||
hash.clone(&ctx._i_hash, &other._i_hash)
|
||||
ctx._tag_sz = other._tag_sz
|
||||
ctx._is_initialized = other._is_initialized
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
if !ctx._is_initialized {
|
||||
return
|
||||
}
|
||||
|
||||
hash.reset(&ctx._o_hash)
|
||||
hash.reset(&ctx._i_hash)
|
||||
ctx._tag_sz = 0
|
||||
ctx._is_initialized = false
|
||||
}
|
||||
|
||||
// algorithm returns the Algorithm used by a Context instance.
|
||||
algorithm :: proc(ctx: ^Context) -> hash.Algorithm {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
return hash.algorithm(&ctx._i_hash)
|
||||
}
|
||||
|
||||
// tag_size returns the tag size of a Context instance in bytes.
|
||||
tag_size :: proc(ctx: ^Context) -> int {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
return ctx._tag_sz
|
||||
}
|
||||
|
||||
@(private)
|
||||
_I_PAD :: 0x36
|
||||
_O_PAD :: 0x5c
|
||||
|
||||
@(private)
|
||||
_init_hashes :: proc(ctx: ^Context, algorithm: hash.Algorithm, key: []byte) {
|
||||
K0_buf: [hash.MAX_BLOCK_SIZE]byte
|
||||
kPad_buf: [hash.MAX_BLOCK_SIZE]byte
|
||||
|
||||
kLen := len(key)
|
||||
B := hash.BLOCK_SIZES[algorithm]
|
||||
K0 := K0_buf[:B]
|
||||
defer mem.zero_explicit(raw_data(K0), B)
|
||||
|
||||
switch {
|
||||
case kLen == B, kLen < B:
|
||||
// If the length of K = B: set K0 = K.
|
||||
//
|
||||
// If the length of K < B: append zeros to the end of K to
|
||||
// create a B-byte string K0 (e.g., if K is 20 bytes in
|
||||
// length and B = 64, then K will be appended with 44 zero
|
||||
// bytes x’00’).
|
||||
//
|
||||
// K0 is zero-initialized, so the copy handles both cases.
|
||||
copy(K0, key)
|
||||
case kLen > B:
|
||||
// If the length of K > B: hash K to obtain an L byte string,
|
||||
// then append (B-L) zeros to create a B-byte string K0
|
||||
// (i.e., K0 = H(K) || 00...00).
|
||||
tmpCtx := &ctx._o_hash // Saves allocating a hash.Context.
|
||||
hash.init(tmpCtx, algorithm)
|
||||
hash.update(tmpCtx, key)
|
||||
hash.final(tmpCtx, K0)
|
||||
}
|
||||
|
||||
// Initialize the hashes, and write the padded keys:
|
||||
// - ctx._i_hash -> H(K0 ^ ipad)
|
||||
// - ctx._o_hash -> H(K0 ^ opad)
|
||||
|
||||
hash.init(&ctx._o_hash, algorithm)
|
||||
hash.init(&ctx._i_hash, algorithm)
|
||||
|
||||
kPad := kPad_buf[:B]
|
||||
defer mem.zero_explicit(raw_data(kPad), B)
|
||||
|
||||
for v, i in K0 {
|
||||
kPad[i] = v ~ _I_PAD
|
||||
}
|
||||
hash.update(&ctx._i_hash, kPad)
|
||||
|
||||
for v, i in K0 {
|
||||
kPad[i] = v ~ _O_PAD
|
||||
}
|
||||
hash.update(&ctx._o_hash, kPad)
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
package kmac implements the KMAC MAC algorithm.
|
||||
|
||||
See:
|
||||
- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
|
||||
*/
|
||||
package kmac
|
||||
|
||||
import "../_sha3"
|
||||
import "core:crypto"
|
||||
import "core:crypto/shake"
|
||||
|
||||
// MIN_KEY_SIZE_128 is the minimum key size for KMAC128 in bytes.
|
||||
MIN_KEY_SIZE_128 :: 128 / 8
|
||||
// MIN_KEY_SIZE_256 is the minimum key size for KMAC256 in bytes.
|
||||
MIN_KEY_SIZE_256 :: 256 / 8
|
||||
|
||||
// MIN_TAG_SIZE is the absolute minimum tag size for KMAC in bytes (8.4.2).
|
||||
// Most callers SHOULD use at least 128-bits if not 256-bits for the tag
|
||||
// size.
|
||||
MIN_TAG_SIZE :: 32 / 8
|
||||
|
||||
// sum will compute the KMAC with the specified security strength,
|
||||
// key, and domain separator over msg, and write the computed digest to
|
||||
// dst.
|
||||
sum :: proc(sec_strength: int, dst, msg, key, domain_sep: []byte) {
|
||||
ctx: Context
|
||||
|
||||
_init_kmac(&ctx, key, domain_sep, sec_strength)
|
||||
update(&ctx, msg)
|
||||
final(&ctx, dst)
|
||||
}
|
||||
|
||||
// verify will verify the KMAC tag computed with the specified security
|
||||
// strength, key and domain separator over msg and return true iff the
|
||||
// tag is valid.
|
||||
verify :: proc(sec_strength: int, tag, msg, key, domain_sep: []byte, allocator := context.temp_allocator) -> bool {
|
||||
derived_tag := make([]byte, len(tag), allocator)
|
||||
|
||||
sum(sec_strength, derived_tag, msg, key, domain_sep)
|
||||
|
||||
return crypto.compare_constant_time(derived_tag, tag) == 1
|
||||
}
|
||||
|
||||
// Context is a KMAC instance.
|
||||
Context :: distinct shake.Context
|
||||
|
||||
// init_128 initializes a Context for KMAC28. This routine will panic if
|
||||
// the key length is less than MIN_KEY_SIZE_128.
|
||||
init_128 :: proc(ctx: ^Context, key, domain_sep: []byte) {
|
||||
_init_kmac(ctx, key, domain_sep, 128)
|
||||
}
|
||||
|
||||
// init_256 initializes a Context for KMAC256. This routine will panic if
|
||||
// the key length is less than MIN_KEY_SIZE_256.
|
||||
init_256 :: proc(ctx: ^Context, key, domain_sep: []byte) {
|
||||
_init_kmac(ctx, key, domain_sep, 256)
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
shake.write(transmute(^shake.Context)(ctx), data)
|
||||
}
|
||||
|
||||
// final finalizes the Context, writes the tag to dst, and calls reset
|
||||
// on the Context. This routine will panic if the dst length is less than
|
||||
// MIN_TAG_SIZE.
|
||||
final :: proc(ctx: ^Context, dst: []byte) {
|
||||
assert(ctx.is_initialized)
|
||||
defer reset(ctx)
|
||||
|
||||
if len(dst) < MIN_TAG_SIZE {
|
||||
panic("crypto/kmac: invalid KMAC tag_size, too short")
|
||||
}
|
||||
|
||||
_sha3.final_cshake(transmute(^_sha3.Context)(ctx), dst)
|
||||
}
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
if ctx == other {
|
||||
return
|
||||
}
|
||||
|
||||
shake.clone(transmute(^shake.Context)(ctx), transmute(^shake.Context)(other))
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
if !ctx.is_initialized {
|
||||
return
|
||||
}
|
||||
|
||||
shake.reset(transmute(^shake.Context)(ctx))
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_kmac :: proc(ctx: ^Context, key, s: []byte, sec_strength: int) {
|
||||
if ctx.is_initialized {
|
||||
reset(ctx)
|
||||
}
|
||||
|
||||
if len(key) < sec_strength / 8 {
|
||||
panic("crypto/kmac: invalid KMAC key, too short")
|
||||
}
|
||||
|
||||
ctx_ := transmute(^_sha3.Context)(ctx)
|
||||
_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
|
||||
_sha3.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
|
||||
}
|
||||
|
||||
@(private)
|
||||
N_KMAC := []byte{'K', 'M', 'A', 'C'}
|
||||
@@ -1,3 +1,11 @@
|
||||
/*
|
||||
package keccak implements the Keccak hash algorithm family.
|
||||
|
||||
During the SHA-3 standardization process, the padding scheme was changed
|
||||
thus Keccac and SHA-3 produce different outputs. Most users should use
|
||||
SHA-3 and/or SHAKE instead, however the legacy algorithm is provided for
|
||||
backward compatibility purposes.
|
||||
*/
|
||||
package keccak
|
||||
|
||||
/*
|
||||
@@ -6,372 +14,82 @@ package keccak
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Interface for the Keccak hashing algorithm.
|
||||
This is done because the padding in the SHA3 standard was changed by the NIST, resulting in a different output.
|
||||
*/
|
||||
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
|
||||
import "../../_sha3"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// DIGEST_SIZE_224 is the Keccak-224 digest size.
|
||||
DIGEST_SIZE_224 :: 28
|
||||
// DIGEST_SIZE_256 is the Keccak-256 digest size.
|
||||
DIGEST_SIZE_256 :: 32
|
||||
// DIGEST_SIZE_384 is the Keccak-384 digest size.
|
||||
DIGEST_SIZE_384 :: 48
|
||||
// DIGEST_SIZE_512 is the Keccak-512 digest size.
|
||||
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))
|
||||
}
|
||||
// BLOCK_SIZE_224 is the Keccak-224 block size in bytes.
|
||||
BLOCK_SIZE_224 :: _sha3.RATE_224
|
||||
// BLOCK_SIZE_256 is the Keccak-256 block size in bytes.
|
||||
BLOCK_SIZE_256 :: _sha3.RATE_256
|
||||
// BLOCK_SIZE_384 is the Keccak-384 block size in bytes.
|
||||
BLOCK_SIZE_384 :: _sha3.RATE_384
|
||||
// BLOCK_SIZE_512 is the Keccak-512 block size in bytes.
|
||||
BLOCK_SIZE_512 :: _sha3.RATE_512
|
||||
|
||||
// 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: Context
|
||||
// Context is a Keccak instance.
|
||||
Context :: distinct _sha3.Context
|
||||
|
||||
// init_224 initializes a Context for Keccak-224.
|
||||
init_224 :: proc(ctx: ^Context) {
|
||||
ctx.mdlen = DIGEST_SIZE_224
|
||||
ctx.is_keccak = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_224
|
||||
ctx.is_keccak = 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: Context
|
||||
ctx.mdlen = DIGEST_SIZE_224
|
||||
ctx.is_keccak = 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(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: Context
|
||||
// init_256 initializes a Context for Keccak-256.
|
||||
init_256 :: proc(ctx: ^Context) {
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
ctx.is_keccak = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
ctx.is_keccak = true
|
||||
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: Context
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
ctx.is_keccak = 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_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: Context
|
||||
// init_384 initializes a Context for Keccak-384.
|
||||
init_384 :: proc(ctx: ^Context) {
|
||||
ctx.mdlen = DIGEST_SIZE_384
|
||||
ctx.is_keccak = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_384
|
||||
ctx.is_keccak = 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: Context
|
||||
ctx.mdlen = DIGEST_SIZE_384
|
||||
ctx.is_keccak = 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(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: Context
|
||||
// init_512 initializes a Context for Keccak-512.
|
||||
init_512 :: proc(ctx: ^Context) {
|
||||
ctx.mdlen = DIGEST_SIZE_512
|
||||
ctx.is_keccak = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_512
|
||||
ctx.is_keccak = true
|
||||
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: Context
|
||||
ctx.mdlen = DIGEST_SIZE_512
|
||||
ctx.is_keccak = 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_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
|
||||
*/
|
||||
|
||||
Context :: _sha3.Sha3_Context
|
||||
|
||||
init :: proc(ctx: ^Context) {
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(ctx)
|
||||
@(private)
|
||||
_init :: proc(ctx: ^Context) {
|
||||
ctx.dsbyte = _sha3.DS_KECCAK
|
||||
_sha3.init(transmute(^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
_sha3.update(ctx, data)
|
||||
_sha3.update(transmute(^_sha3.Context)(ctx), data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Context, hash: []byte) {
|
||||
_sha3.final(ctx, 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) {
|
||||
_sha3.final(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
|
||||
}
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
+52
-100
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
package md5 implements the MD5 hash algorithm.
|
||||
|
||||
WARNING: The MD5 algorithm is known to be insecure and should only be
|
||||
used for interoperating with legacy applications.
|
||||
|
||||
See:
|
||||
- https://eprint.iacr.org/2005/075
|
||||
- https://datatracker.ietf.org/doc/html/rfc1321
|
||||
*/
|
||||
package md5
|
||||
|
||||
/*
|
||||
@@ -6,103 +16,29 @@ package md5
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the MD5 hashing algorithm, as defined in RFC 1321 <https://datatracker.ietf.org/doc/html/rfc1321>
|
||||
*/
|
||||
|
||||
import "core:encoding/endian"
|
||||
import "core:io"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// DIGEST_SIZE is the MD5 digest size in bytes.
|
||||
DIGEST_SIZE :: 16
|
||||
|
||||
// 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 MD5 block size in bytes.
|
||||
BLOCK_SIZE :: 64
|
||||
|
||||
// Context is a MD5 instance.
|
||||
Context :: struct {
|
||||
data: [BLOCK_SIZE]byte,
|
||||
state: [4]u32,
|
||||
bitlen: u64,
|
||||
datalen: u32,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
// 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: 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) {
|
||||
ctx: 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: 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 initializes a Context.
|
||||
init :: proc(ctx: ^Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
@@ -115,6 +51,7 @@ init :: proc(ctx: ^Context) {
|
||||
ctx.is_initialized = true
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
@@ -129,13 +66,26 @@ update :: proc(ctx: ^Context, data: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Context, hash: []byte) {
|
||||
// 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) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
if len(hash) < DIGEST_SIZE {
|
||||
panic("crypto/md5: invalid destination digest size")
|
||||
}
|
||||
|
||||
ctx := ctx
|
||||
if finalize_clone {
|
||||
tmp_ctx: Context
|
||||
clone(&tmp_ctx, ctx)
|
||||
ctx = &tmp_ctx
|
||||
}
|
||||
defer(reset(ctx))
|
||||
|
||||
i := ctx.datalen
|
||||
|
||||
if ctx.datalen < 56 {
|
||||
@@ -163,25 +113,27 @@ final :: proc(ctx: ^Context, hash: []byte) {
|
||||
for i = 0; i < DIGEST_SIZE / 4; i += 1 {
|
||||
endian.unchecked_put_u32le(hash[i * 4:], ctx.state[i])
|
||||
}
|
||||
}
|
||||
|
||||
ctx.is_initialized = false
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^$T) {
|
||||
ctx^ = other^
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^$T) {
|
||||
if !ctx.is_initialized {
|
||||
return
|
||||
}
|
||||
|
||||
mem.zero_explicit(ctx, size_of(ctx^))
|
||||
}
|
||||
|
||||
/*
|
||||
MD5 implementation
|
||||
*/
|
||||
|
||||
BLOCK_SIZE :: 64
|
||||
|
||||
Context :: struct {
|
||||
data: [BLOCK_SIZE]byte,
|
||||
state: [4]u32,
|
||||
bitlen: u64,
|
||||
datalen: u32,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
/*
|
||||
@note(zh): F, G, H and I, as mentioned in the RFC, have been inlined into FF, GG, HH
|
||||
and II respectively, instead of declaring them separately.
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
package sha1 implements the SHA1 hash algorithm.
|
||||
|
||||
WARNING: The SHA1 algorithm is known to be insecure and should only be
|
||||
used for interoperating with legacy applications.
|
||||
|
||||
See:
|
||||
- https://eprint.iacr.org/2017/190
|
||||
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
|
||||
- https://datatracker.ietf.org/doc/html/rfc3174
|
||||
*/
|
||||
package sha1
|
||||
|
||||
/*
|
||||
@@ -6,103 +17,30 @@ package sha1
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the SHA1 hashing algorithm, as defined in RFC 3174 <https://datatracker.ietf.org/doc/html/rfc3174>
|
||||
*/
|
||||
|
||||
import "core:encoding/endian"
|
||||
import "core:io"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// DIGEST_SIZE is the SHA1 digest size in bytes.
|
||||
DIGEST_SIZE :: 20
|
||||
|
||||
// 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 SHA1 block size in bytes.
|
||||
BLOCK_SIZE :: 64
|
||||
|
||||
// Context is a SHA1 instance.
|
||||
Context :: struct {
|
||||
data: [BLOCK_SIZE]byte,
|
||||
state: [5]u32,
|
||||
k: [4]u32,
|
||||
bitlen: u64,
|
||||
datalen: u32,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
// 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: 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) {
|
||||
ctx: 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: 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 initializes a Context.
|
||||
init :: proc(ctx: ^Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
@@ -120,6 +58,7 @@ init :: proc(ctx: ^Context) {
|
||||
ctx.is_initialized = true
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
@@ -134,13 +73,26 @@ update :: proc(ctx: ^Context, data: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Context, hash: []byte) {
|
||||
// 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) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
if len(hash) < DIGEST_SIZE {
|
||||
panic("crypto/sha1: invalid destination digest size")
|
||||
}
|
||||
|
||||
ctx := ctx
|
||||
if finalize_clone {
|
||||
tmp_ctx: Context
|
||||
clone(&tmp_ctx, ctx)
|
||||
ctx = &tmp_ctx
|
||||
}
|
||||
defer(reset(ctx))
|
||||
|
||||
i := ctx.datalen
|
||||
|
||||
if ctx.datalen < 56 {
|
||||
@@ -168,26 +120,27 @@ final :: proc(ctx: ^Context, hash: []byte) {
|
||||
for i = 0; i < DIGEST_SIZE / 4; i += 1 {
|
||||
endian.unchecked_put_u32be(hash[i * 4:], ctx.state[i])
|
||||
}
|
||||
}
|
||||
|
||||
ctx.is_initialized = false
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^$T) {
|
||||
ctx^ = other^
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^$T) {
|
||||
if !ctx.is_initialized {
|
||||
return
|
||||
}
|
||||
|
||||
mem.zero_explicit(ctx, size_of(ctx^))
|
||||
}
|
||||
|
||||
/*
|
||||
SHA1 implementation
|
||||
*/
|
||||
|
||||
BLOCK_SIZE :: 64
|
||||
|
||||
Context :: struct {
|
||||
data: [BLOCK_SIZE]byte,
|
||||
datalen: u32,
|
||||
bitlen: u64,
|
||||
state: [5]u32,
|
||||
k: [4]u32,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
@(private)
|
||||
transform :: proc "contextless" (ctx: ^Context, data: []byte) {
|
||||
a, b, c, d, e, i, t: u32
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
package pbkdf2 implements the PBKDF2 password-based key derivation function.
|
||||
|
||||
See: https://www.rfc-editor.org/rfc/rfc2898
|
||||
*/
|
||||
package pbkdf2
|
||||
|
||||
import "core:crypto/hash"
|
||||
import "core:crypto/hmac"
|
||||
import "core:encoding/endian"
|
||||
import "core:mem"
|
||||
|
||||
// derive invokes PBKDF2-HMAC with the specified hash algorithm, password,
|
||||
// salt, iteration count, and outputs the derived key to dst.
|
||||
derive :: proc(
|
||||
hmac_hash: hash.Algorithm,
|
||||
password: []byte,
|
||||
salt: []byte,
|
||||
iterations: u32,
|
||||
dst: []byte,
|
||||
) {
|
||||
h_len := hash.DIGEST_SIZES[hmac_hash]
|
||||
|
||||
// 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long"
|
||||
// and stop.
|
||||
|
||||
dk_len := len(dst)
|
||||
switch {
|
||||
case dk_len == 0:
|
||||
return
|
||||
case u64(dk_len) > u64(max(u32)) * u64(h_len):
|
||||
// This is so beyond anything that is practical or reasonable,
|
||||
// so just panic instead of returning an error.
|
||||
panic("crypto/pbkdf2: derived key too long")
|
||||
case:
|
||||
}
|
||||
|
||||
// 2. Let l be the number of hLen-octet blocks in the derived key,
|
||||
// rounding up, and let r be the number of octets in the last block.
|
||||
|
||||
l := dk_len / h_len // Don't need to round up.
|
||||
r := dk_len % h_len
|
||||
|
||||
// 3. For each block of the derived key apply the function F defined
|
||||
// below to the password P, the salt S, the iteration count c, and
|
||||
// the block index to compute the block.
|
||||
//
|
||||
// 4. Concatenate the blocks and extract the first dkLen octets to
|
||||
// produce a derived key DK.
|
||||
//
|
||||
// 5. Output the derived key DK.
|
||||
|
||||
// Each iteration of F is always `PRF (P, ...)`, so instantiate the
|
||||
// PRF, and clone since memcpy is faster than having to re-initialize
|
||||
// HMAC repeatedly.
|
||||
|
||||
base: hmac.Context
|
||||
defer hmac.reset(&base)
|
||||
|
||||
hmac.init(&base, hmac_hash, password)
|
||||
|
||||
// Process all of the blocks that will be written directly to dst.
|
||||
dst_blk := dst
|
||||
for i in 1 ..= l { // F expects i starting at 1.
|
||||
_F(&base, salt, iterations, u32(i), dst_blk[:h_len])
|
||||
dst_blk = dst_blk[h_len:]
|
||||
}
|
||||
|
||||
// Instead of rounding l up, just proceass the one extra block iff
|
||||
// r != 0.
|
||||
if r > 0 {
|
||||
tmp: [hash.MAX_DIGEST_SIZE]byte
|
||||
blk := tmp[:h_len]
|
||||
defer mem.zero_explicit(raw_data(blk), h_len)
|
||||
|
||||
_F(&base, salt, iterations, u32(l + 1), blk)
|
||||
copy(dst_blk, blk)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_F :: proc(base: ^hmac.Context, salt: []byte, c: u32, i: u32, dst_blk: []byte) {
|
||||
h_len := len(dst_blk)
|
||||
|
||||
tmp: [hash.MAX_DIGEST_SIZE]byte
|
||||
u := tmp[:h_len]
|
||||
defer mem.zero_explicit(raw_data(u), h_len)
|
||||
|
||||
// F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
|
||||
//
|
||||
// where
|
||||
//
|
||||
// U_1 = PRF (P, S || INT (i)) ,
|
||||
// U_2 = PRF (P, U_1) ,
|
||||
// ...
|
||||
// U_c = PRF (P, U_{c-1}) .
|
||||
//
|
||||
// Here, INT (i) is a four-octet encoding of the integer i, most
|
||||
// significant octet first.
|
||||
|
||||
prf: hmac.Context
|
||||
|
||||
// U_1: PRF (P, S || INT (i))
|
||||
hmac.clone(&prf, base)
|
||||
hmac.update(&prf, salt)
|
||||
endian.unchecked_put_u32be(u, i) // Use u as scratch space.
|
||||
hmac.update(&prf, u[:4])
|
||||
hmac.final(&prf, u)
|
||||
copy(dst_blk, u)
|
||||
|
||||
// U_2 ... U_c: U_n = PRF (P, U_(n-1))
|
||||
for _ in 1 ..< c {
|
||||
hmac.clone(&prf, base)
|
||||
hmac.update(&prf, u)
|
||||
hmac.final(&prf, u)
|
||||
|
||||
// XOR dst_blk and u.
|
||||
for v, i in u {
|
||||
dst_blk[i] ~= v
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,31 @@
|
||||
/*
|
||||
package poly1305 implements the Poly1305 one-time MAC algorithm.
|
||||
|
||||
See:
|
||||
- https://datatracker.ietf.org/doc/html/rfc8439
|
||||
*/
|
||||
package poly1305
|
||||
|
||||
import "core:crypto"
|
||||
import field "core:crypto/_fiat/field_poly1305"
|
||||
import "core:encoding/endian"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
// KEY_SIZE is the Poly1305 key size in bytes.
|
||||
KEY_SIZE :: 32
|
||||
// TAG_SIZE is the Poly1305 tag size in bytes.
|
||||
TAG_SIZE :: 16
|
||||
|
||||
@(private)
|
||||
_BLOCK_SIZE :: 16
|
||||
|
||||
sum :: proc (dst, msg, key: []byte) {
|
||||
// sum will compute the Poly1305 MAC with the key over msg, and write
|
||||
// the computed tag to dst. It requires that the dst buffer is the tag
|
||||
// size.
|
||||
//
|
||||
// The key SHOULD be unique and MUST be unpredictable for each invocation.
|
||||
sum :: proc(dst, msg, key: []byte) {
|
||||
ctx: Context = ---
|
||||
|
||||
init(&ctx, key)
|
||||
@@ -19,13 +33,12 @@ sum :: proc (dst, msg, key: []byte) {
|
||||
final(&ctx, dst)
|
||||
}
|
||||
|
||||
verify :: proc (tag, msg, key: []byte) -> bool {
|
||||
// verify will verify the Poly1305 tag computed with the key over msg and
|
||||
// return true iff the tag is valid. It requires that the tag is correctly
|
||||
// sized.
|
||||
verify :: proc(tag, msg, key: []byte) -> bool {
|
||||
ctx: Context = ---
|
||||
derived_tag: [16]byte = ---
|
||||
|
||||
if len(tag) != TAG_SIZE {
|
||||
panic("crypto/poly1305: invalid tag size")
|
||||
}
|
||||
derived_tag: [TAG_SIZE]byte = ---
|
||||
|
||||
init(&ctx, key)
|
||||
update(&ctx, msg)
|
||||
@@ -34,18 +47,19 @@ verify :: proc (tag, msg, key: []byte) -> bool {
|
||||
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||
}
|
||||
|
||||
// Context is a Poly1305 instance.
|
||||
Context :: struct {
|
||||
_r: field.Tight_Field_Element,
|
||||
_a: field.Tight_Field_Element,
|
||||
_s: field.Tight_Field_Element,
|
||||
|
||||
_buffer: [_BLOCK_SIZE]byte,
|
||||
_leftover: int,
|
||||
|
||||
_r: field.Tight_Field_Element,
|
||||
_a: field.Tight_Field_Element,
|
||||
_s: [2]u64,
|
||||
_buffer: [_BLOCK_SIZE]byte,
|
||||
_leftover: int,
|
||||
_is_initialized: bool,
|
||||
}
|
||||
|
||||
init :: proc (ctx: ^Context, key: []byte) {
|
||||
// init initializes a Context with the specified key. The key SHOULD be
|
||||
// unique and MUST be unpredictable for each invocation.
|
||||
init :: proc(ctx: ^Context, key: []byte) {
|
||||
if len(key) != KEY_SIZE {
|
||||
panic("crypto/poly1305: invalid key size")
|
||||
}
|
||||
@@ -53,11 +67,12 @@ init :: proc (ctx: ^Context, key: []byte) {
|
||||
// r = le_bytes_to_num(key[0..15])
|
||||
// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
|
||||
tmp_lo := endian.unchecked_get_u64le(key[0:]) & 0x0ffffffc0fffffff
|
||||
tmp_hi := endian.unchecked_get_u64le(key[8:]) & 0xffffffc0ffffffc
|
||||
tmp_hi := endian.unchecked_get_u64le(key[8:]) & 0x0ffffffc0ffffffc
|
||||
field.fe_from_u64s(&ctx._r, tmp_lo, tmp_hi)
|
||||
|
||||
// s = le_bytes_to_num(key[16..31])
|
||||
field.fe_from_bytes(&ctx._s, key[16:32], 0)
|
||||
ctx._s[0] = endian.unchecked_get_u64le(key[16:])
|
||||
ctx._s[1] = endian.unchecked_get_u64le(key[24:])
|
||||
|
||||
// a = 0
|
||||
field.fe_zero(&ctx._a)
|
||||
@@ -68,7 +83,8 @@ init :: proc (ctx: ^Context, key: []byte) {
|
||||
ctx._is_initialized = true
|
||||
}
|
||||
|
||||
update :: proc (ctx: ^Context, data: []byte) {
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
msg := data
|
||||
@@ -105,8 +121,11 @@ update :: proc (ctx: ^Context, data: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc (ctx: ^Context, dst: []byte) {
|
||||
// final finalizes the Context, writes the tag to dst, and calls
|
||||
// reset on the Context.
|
||||
final :: proc(ctx: ^Context, dst: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
defer reset(ctx)
|
||||
|
||||
if len(dst) != TAG_SIZE {
|
||||
panic("poly1305: invalid destination tag size")
|
||||
@@ -121,19 +140,25 @@ final :: proc (ctx: ^Context, dst: []byte) {
|
||||
_blocks(ctx, ctx._buffer[:], true)
|
||||
}
|
||||
|
||||
// a += s
|
||||
field.fe_add(field.fe_relax_cast(&ctx._a), &ctx._a, &ctx._s) // _a unreduced
|
||||
field.fe_carry(&ctx._a, field.fe_relax_cast(&ctx._a)) // _a reduced
|
||||
|
||||
// return num_to_16_le_bytes(a)
|
||||
// a += s (NOT mod p)
|
||||
tmp: [32]byte = ---
|
||||
field.fe_to_bytes(&tmp, &ctx._a)
|
||||
copy_slice(dst, tmp[0:16])
|
||||
|
||||
reset(ctx)
|
||||
c: u64
|
||||
lo := endian.unchecked_get_u64le(tmp[0:])
|
||||
hi := endian.unchecked_get_u64le(tmp[8:])
|
||||
|
||||
lo, c = bits.add_u64(lo, ctx._s[0], 0)
|
||||
hi, _ = bits.add_u64(hi, ctx._s[1], c)
|
||||
|
||||
// return num_to_16_le_bytes(a)
|
||||
endian.unchecked_put_u64le(dst[0:], lo)
|
||||
endian.unchecked_put_u64le(dst[8:], hi)
|
||||
}
|
||||
|
||||
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._r, size_of(ctx._r))
|
||||
mem.zero_explicit(&ctx._a, size_of(ctx._a))
|
||||
mem.zero_explicit(&ctx._s, size_of(ctx._s))
|
||||
@@ -143,7 +168,7 @@ reset :: proc (ctx: ^Context) {
|
||||
}
|
||||
|
||||
@(private)
|
||||
_blocks :: proc (ctx: ^Context, msg: []byte, final := false) {
|
||||
_blocks :: proc "contextless" (ctx: ^Context, msg: []byte, final := false) {
|
||||
n: field.Tight_Field_Element = ---
|
||||
final_byte := byte(!final)
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
//+build freebsd, openbsd
|
||||
package crypto
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
foreign libc {
|
||||
arc4random_buf :: proc(buf: [^]byte, nbytes: uint) ---
|
||||
}
|
||||
|
||||
_rand_bytes :: proc(dst: []byte) {
|
||||
arc4random_buf(raw_data(dst), len(dst))
|
||||
}
|
||||
|
||||
_has_rand_bytes :: proc () -> bool {
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package crypto
|
||||
|
||||
import "core:fmt"
|
||||
import "core:sys/darwin"
|
||||
|
||||
_rand_bytes :: proc(dst: []byte) {
|
||||
res := darwin.SecRandomCopyBytes(count=len(dst), bytes=raw_data(dst))
|
||||
if res != .Success {
|
||||
msg := darwin.CFStringCopyToOdinString(darwin.SecCopyErrorMessageString(res))
|
||||
panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", res, msg))
|
||||
}
|
||||
}
|
||||
|
||||
_has_rand_bytes :: proc () -> bool {
|
||||
return true
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
//+build !linux
|
||||
//+build !windows
|
||||
//+build !openbsd
|
||||
//+build !freebsd
|
||||
//+build !darwin
|
||||
//+build !js
|
||||
package crypto
|
||||
|
||||
when ODIN_OS != .Linux && ODIN_OS != .OpenBSD && ODIN_OS != .Windows && ODIN_OS != .JS {
|
||||
_rand_bytes :: proc(dst: []byte) {
|
||||
unimplemented("crypto: rand_bytes not supported on this OS")
|
||||
}
|
||||
_rand_bytes :: proc(dst: []byte) {
|
||||
unimplemented("crypto: rand_bytes not supported on this OS")
|
||||
}
|
||||
|
||||
_has_rand_bytes :: proc () -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -18,3 +18,7 @@ _rand_bytes :: proc(dst: []byte) {
|
||||
dst = dst[to_read:]
|
||||
}
|
||||
}
|
||||
|
||||
_has_rand_bytes :: proc () -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -34,3 +34,7 @@ _rand_bytes :: proc (dst: []byte) {
|
||||
dst = dst[n_read:]
|
||||
}
|
||||
}
|
||||
|
||||
_has_rand_bytes :: proc () -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package crypto
|
||||
|
||||
import "core:c"
|
||||
|
||||
foreign import libc "system:c"
|
||||
foreign libc {
|
||||
arc4random_buf :: proc "c" (buf: rawptr, nbytes: c.size_t) ---
|
||||
}
|
||||
|
||||
_rand_bytes :: proc (dst: []byte) {
|
||||
arc4random_buf(raw_data(dst), len(dst))
|
||||
}
|
||||
@@ -21,3 +21,7 @@ _rand_bytes :: proc(dst: []byte) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_has_rand_bytes :: proc () -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
package ristretto255 implement the ristretto255 prime-order group.
|
||||
|
||||
See:
|
||||
- https://www.rfc-editor.org/rfc/rfc9496
|
||||
*/
|
||||
package ristretto255
|
||||
|
||||
import grp "core:crypto/_edwards25519"
|
||||
import field "core:crypto/_fiat/field_curve25519"
|
||||
import "core:mem"
|
||||
|
||||
// ELEMENT_SIZE is the size of a byte-encoded ristretto255 group element.
|
||||
ELEMENT_SIZE :: 32
|
||||
// WIDE_ELEMENT_SIZE is the side of a wide byte-encoded ristretto255
|
||||
// group element.
|
||||
WIDE_ELEMENT_SIZE :: 64
|
||||
|
||||
@(private)
|
||||
FE_NEG_ONE := field.Tight_Field_Element {
|
||||
2251799813685228,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
2251799813685247,
|
||||
}
|
||||
@(private)
|
||||
FE_INVSQRT_A_MINUS_D := field.Tight_Field_Element {
|
||||
278908739862762,
|
||||
821645201101625,
|
||||
8113234426968,
|
||||
1777959178193151,
|
||||
2118520810568447,
|
||||
}
|
||||
@(private)
|
||||
FE_ONE_MINUS_D_SQ := field.Tight_Field_Element {
|
||||
1136626929484150,
|
||||
1998550399581263,
|
||||
496427632559748,
|
||||
118527312129759,
|
||||
45110755273534,
|
||||
}
|
||||
@(private)
|
||||
FE_D_MINUS_ONE_SQUARED := field.Tight_Field_Element {
|
||||
1507062230895904,
|
||||
1572317787530805,
|
||||
683053064812840,
|
||||
317374165784489,
|
||||
1572899562415810,
|
||||
}
|
||||
@(private)
|
||||
FE_SQRT_AD_MINUS_ONE := field.Tight_Field_Element {
|
||||
2241493124984347,
|
||||
425987919032274,
|
||||
2207028919301688,
|
||||
1220490630685848,
|
||||
974799131293748,
|
||||
}
|
||||
@(private)
|
||||
GE_IDENTITY := Group_Element{grp.GE_IDENTITY, true}
|
||||
|
||||
// Group_Element is a ristretto255 group element. The zero-initialized
|
||||
// value is invalid.
|
||||
Group_Element :: struct {
|
||||
// WARNING: While the internal representation is an Edwards25519
|
||||
// group element, this is not guaranteed to always be the case,
|
||||
// and your code *WILL* break if you mess with `_p`.
|
||||
_p: grp.Group_Element,
|
||||
_is_initialized: bool,
|
||||
}
|
||||
|
||||
// ge_clear clears ge to the uninitialized state.
|
||||
ge_clear :: proc "contextless" (ge: ^Group_Element) {
|
||||
mem.zero_explicit(ge, size_of(Group_Element))
|
||||
}
|
||||
|
||||
// ge_set sets `ge = a`.
|
||||
ge_set :: proc(ge, a: ^Group_Element) {
|
||||
_ge_assert_initialized([]^Group_Element{a})
|
||||
|
||||
grp.ge_set(&ge._p, &a._p)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_identity sets ge to the identity (neutral) element.
|
||||
ge_identity :: proc "contextless" (ge: ^Group_Element) {
|
||||
grp.ge_identity(&ge._p)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_generator sets ge to the group generator.
|
||||
ge_generator :: proc "contextless" (ge: ^Group_Element) {
|
||||
grp.ge_generator(&ge._p)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_set_bytes sets ge to the result of decoding b as a ristretto255
|
||||
// group element, and returns true on success.
|
||||
@(require_results)
|
||||
ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
|
||||
// 1. Interpret the string as an unsigned integer s in little-endian
|
||||
// representation. If the length of the string is not 32 bytes or
|
||||
// if the resulting value is >= p, decoding fails.
|
||||
//
|
||||
// 2. If IS_NEGATIVE(s) returns TRUE, decoding fails.
|
||||
|
||||
if len(b) != ELEMENT_SIZE {
|
||||
return false
|
||||
}
|
||||
if b[31] & 128 != 0 || b[0] & 1 != 0 {
|
||||
// Fail early if b is clearly > p, or negative.
|
||||
return false
|
||||
}
|
||||
|
||||
b_ := transmute(^[32]byte)(raw_data(b))
|
||||
|
||||
s: field.Tight_Field_Element = ---
|
||||
defer field.fe_clear(&s)
|
||||
|
||||
field.fe_from_bytes(&s, b_)
|
||||
if field.fe_equal_bytes(&s, b_) != 1 {
|
||||
// Reject non-canonical encodings of s.
|
||||
return false
|
||||
}
|
||||
|
||||
// 3. Process s as follows:
|
||||
v, u1, u2: field.Loose_Field_Element = ---, ---, ---
|
||||
tmp, u2_sqr: field.Tight_Field_Element = ---, ---
|
||||
|
||||
// ss = s^2
|
||||
// u1 = 1 - ss
|
||||
// u2 = 1 + ss
|
||||
// u2_sqr = u2^2
|
||||
field.fe_carry_square(&tmp, field.fe_relax_cast(&s))
|
||||
field.fe_sub(&u1, &field.FE_ONE, &tmp)
|
||||
field.fe_add(&u2, &field.FE_ONE, &tmp)
|
||||
field.fe_carry_square(&u2_sqr, &u2)
|
||||
|
||||
// v = -(D * u1^2) - u2_sqr
|
||||
field.fe_carry_square(&tmp, &u1)
|
||||
field.fe_carry_mul(&tmp, field.fe_relax_cast(&grp.FE_D), field.fe_relax_cast(&tmp))
|
||||
field.fe_carry_add(&tmp, &tmp, &u2_sqr)
|
||||
field.fe_opp(&v, &tmp)
|
||||
|
||||
// (was_square, invsqrt) = SQRT_RATIO_M1(1, v * u2_sqr)
|
||||
field.fe_carry_mul(&tmp, &v, field.fe_relax_cast(&u2_sqr))
|
||||
was_square := field.fe_carry_sqrt_ratio_m1(
|
||||
&tmp,
|
||||
field.fe_relax_cast(&field.FE_ONE),
|
||||
field.fe_relax_cast(&tmp),
|
||||
)
|
||||
|
||||
// den_x = invsqrt * u2
|
||||
// den_y = invsqrt * den_x * v
|
||||
x, y, t: field.Tight_Field_Element = ---, ---, ---
|
||||
field.fe_carry_mul(&x, field.fe_relax_cast(&tmp), &u2)
|
||||
field.fe_carry_mul(&y, field.fe_relax_cast(&tmp), field.fe_relax_cast(&x))
|
||||
field.fe_carry_mul(&y, field.fe_relax_cast(&y), &v)
|
||||
|
||||
// x = CT_ABS(2 * s * den_x)
|
||||
field.fe_carry_mul(&x, field.fe_relax_cast(&s), field.fe_relax_cast(&x))
|
||||
field.fe_carry_add(&x, &x, &x)
|
||||
field.fe_carry_abs(&x, &x)
|
||||
|
||||
// y = u1 * den_y
|
||||
field.fe_carry_mul(&y, &u1, field.fe_relax_cast(&y))
|
||||
|
||||
// t = x * y
|
||||
field.fe_carry_mul(&t, field.fe_relax_cast(&x), field.fe_relax_cast(&y))
|
||||
|
||||
field.fe_clear_vec([]^field.Loose_Field_Element{&v, &u1, &u2})
|
||||
field.fe_clear_vec([]^field.Tight_Field_Element{&tmp, &u2_sqr})
|
||||
defer field.fe_clear_vec([]^field.Tight_Field_Element{&x, &y, &t})
|
||||
|
||||
// 4. If was_square is FALSE, IS_NEGATIVE(t) returns TRUE, or y = 0,
|
||||
// decoding fails. Otherwise, return the group element represented
|
||||
// by the internal representation (x, y, 1, t) as the result of
|
||||
// decoding.
|
||||
|
||||
switch {
|
||||
case was_square == 0:
|
||||
// Not sure why the RFC doesn't have this just fail early.
|
||||
return false
|
||||
case field.fe_is_negative(&t) != 0:
|
||||
return false
|
||||
case field.fe_equal(&y, &field.FE_ZERO) != 0:
|
||||
return false
|
||||
}
|
||||
|
||||
field.fe_set(&ge._p.x, &x)
|
||||
field.fe_set(&ge._p.y, &y)
|
||||
field.fe_one(&ge._p.z)
|
||||
field.fe_set(&ge._p.t, &t)
|
||||
ge._is_initialized = true
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ge_set_wide_bytes sets ge to the result of deriving a ristretto255
|
||||
// group element, from a wide (512-bit) byte string.
|
||||
ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) {
|
||||
if len(b) != WIDE_ELEMENT_SIZE {
|
||||
panic("crypto/ristretto255: invalid wide input size")
|
||||
}
|
||||
|
||||
// The element derivation function on an input string b proceeds as
|
||||
// follows:
|
||||
//
|
||||
// 1. Compute P1 as MAP(b[0:32]).
|
||||
// 2. Compute P2 as MAP(b[32:64]).
|
||||
// 3. Return P1 + P2.
|
||||
|
||||
p1, p2: Group_Element = ---, ---
|
||||
ge_map(&p1, b[0:32])
|
||||
ge_map(&p2, b[32:64])
|
||||
|
||||
ge_add(ge, &p1, &p2)
|
||||
|
||||
ge_clear(&p1)
|
||||
ge_clear(&p2)
|
||||
}
|
||||
|
||||
// ge_bytes sets dst to the canonical encoding of ge.
|
||||
ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
|
||||
_ge_assert_initialized([]^Group_Element{ge})
|
||||
if len(dst) != ELEMENT_SIZE {
|
||||
panic("crypto/ristretto255: invalid destination size")
|
||||
}
|
||||
|
||||
x0, y0, z0, t0 := &ge._p.x, &ge._p.y, &ge._p.z, &ge._p.t
|
||||
|
||||
// 1. Process the internal representation into a field element s as
|
||||
// follows:
|
||||
|
||||
// u1 = (z0 + y0) * (z0 - y0)
|
||||
// u2 = x0 * y0
|
||||
u1, u2: field.Tight_Field_Element = ---, ---
|
||||
tmp1, tmp2: field.Loose_Field_Element = ---, ---
|
||||
field.fe_add(&tmp1, z0, y0)
|
||||
field.fe_sub(&tmp2, z0, y0)
|
||||
field.fe_carry_mul(&u1, &tmp1, &tmp2)
|
||||
field.fe_carry_mul(&u2, field.fe_relax_cast(x0), field.fe_relax_cast(y0))
|
||||
|
||||
// Ignore was_square since this is always square.
|
||||
// (_, invsqrt) = SQRT_RATIO_M1(1, u1 * u2^2)
|
||||
tmp: field.Tight_Field_Element = ---
|
||||
field.fe_carry_square(&tmp, field.fe_relax_cast(&u2))
|
||||
field.fe_carry_mul(&tmp, field.fe_relax_cast(&u1), field.fe_relax_cast(&tmp))
|
||||
_ = field.fe_carry_sqrt_ratio_m1(
|
||||
&tmp,
|
||||
field.fe_relax_cast(&field.FE_ONE),
|
||||
field.fe_relax_cast(&tmp),
|
||||
)
|
||||
|
||||
// den1 = invsqrt * u1
|
||||
// den2 = invsqrt * u2
|
||||
// z_inv = den1 * den2 * t0
|
||||
den1, den2 := &u1, &u2
|
||||
z_inv: field.Tight_Field_Element = ---
|
||||
field.fe_carry_mul(den1, field.fe_relax_cast(&tmp), field.fe_relax_cast(&u1))
|
||||
field.fe_carry_mul(den2, field.fe_relax_cast(&tmp), field.fe_relax_cast(&u2))
|
||||
field.fe_carry_mul(&z_inv, field.fe_relax_cast(den1), field.fe_relax_cast(den2))
|
||||
field.fe_carry_mul(&z_inv, field.fe_relax_cast(&z_inv), field.fe_relax_cast(t0))
|
||||
|
||||
// rotate = IS_NEGATIVE(t0 * z_inv)
|
||||
// Note: Reordered from the RFC because invsqrt is no longer needed.
|
||||
field.fe_carry_mul(&tmp, field.fe_relax_cast(t0), field.fe_relax_cast(&z_inv))
|
||||
rotate := field.fe_is_negative(&tmp)
|
||||
|
||||
// ix0 = x0 * SQRT_M1
|
||||
// iy0 = y0 * SQRT_M1
|
||||
// enchanted_denominator = den1 * INVSQRT_A_MINUS_D
|
||||
ix0, iy0: field.Tight_Field_Element = ---, ---
|
||||
field.fe_carry_mul(&ix0, field.fe_relax_cast(x0), field.fe_relax_cast(&field.FE_SQRT_M1))
|
||||
field.fe_carry_mul(&iy0, field.fe_relax_cast(y0), field.fe_relax_cast(&field.FE_SQRT_M1))
|
||||
field.fe_carry_mul(&tmp, field.fe_relax_cast(den1), field.fe_relax_cast(&FE_INVSQRT_A_MINUS_D))
|
||||
|
||||
// Conditionally rotate x and y.
|
||||
// x = CT_SELECT(iy0 IF rotate ELSE x0)
|
||||
// y = CT_SELECT(ix0 IF rotate ELSE y0)
|
||||
// z = z0
|
||||
// den_inv = CT_SELECT(enchanted_denominator IF rotate ELSE den2)
|
||||
x, y: field.Tight_Field_Element = ---, ---
|
||||
field.fe_cond_select(&x, x0, &iy0, rotate)
|
||||
field.fe_cond_select(&y, y0, &ix0, rotate)
|
||||
field.fe_cond_select(&tmp, den2, &tmp, rotate)
|
||||
|
||||
// y = CT_SELECT(-y IF IS_NEGATIVE(x * z_inv) ELSE y)
|
||||
field.fe_carry_mul(&x, field.fe_relax_cast(&x), field.fe_relax_cast(&z_inv))
|
||||
field.fe_cond_negate(&y, &y, field.fe_is_negative(&x))
|
||||
|
||||
// s = CT_ABS(den_inv * (z - y))
|
||||
field.fe_sub(&tmp1, z0, &y)
|
||||
field.fe_carry_mul(&tmp, field.fe_relax_cast(&tmp), &tmp1)
|
||||
field.fe_carry_abs(&tmp, &tmp)
|
||||
|
||||
// 2. Return the 32-byte little-endian encoding of s. More
|
||||
// specifically, this is the encoding of the canonical
|
||||
// representation of s as an integer between 0 and p-1, inclusive.
|
||||
dst_ := transmute(^[32]byte)(raw_data(dst))
|
||||
field.fe_to_bytes(dst_, &tmp)
|
||||
|
||||
field.fe_clear_vec([]^field.Tight_Field_Element{&u1, &u2, &tmp, &z_inv, &ix0, &iy0, &x, &y})
|
||||
field.fe_clear_vec([]^field.Loose_Field_Element{&tmp1, &tmp2})
|
||||
}
|
||||
|
||||
// ge_add sets `ge = a + b`.
|
||||
ge_add :: proc(ge, a, b: ^Group_Element) {
|
||||
_ge_assert_initialized([]^Group_Element{a, b})
|
||||
|
||||
grp.ge_add(&ge._p, &a._p, &b._p)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_double sets `ge = a + a`.
|
||||
ge_double :: proc(ge, a: ^Group_Element) {
|
||||
_ge_assert_initialized([]^Group_Element{a})
|
||||
|
||||
grp.ge_double(&ge._p, &a._p)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_negate sets `ge = -a`.
|
||||
ge_negate :: proc(ge, a: ^Group_Element) {
|
||||
_ge_assert_initialized([]^Group_Element{a})
|
||||
|
||||
grp.ge_negate(&ge._p, &a._p)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_scalarmult sets `ge = A * sc`.
|
||||
ge_scalarmult :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
|
||||
_ge_assert_initialized([]^Group_Element{A})
|
||||
|
||||
grp.ge_scalarmult(&ge._p, &A._p, sc)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_scalarmult_generator sets `ge = G * sc`
|
||||
ge_scalarmult_generator :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar) {
|
||||
grp.ge_scalarmult_basepoint(&ge._p, sc)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_scalarmult_vartime sets `ge = A * sc` in variable time.
|
||||
ge_scalarmult_vartime :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
|
||||
_ge_assert_initialized([]^Group_Element{A})
|
||||
|
||||
grp.ge_scalarmult_vartime(&ge._p, &A._p, sc)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_double_scalarmult_generator_vartime sets `ge = A * a + G * b` in variable
|
||||
// time.
|
||||
ge_double_scalarmult_generator_vartime :: proc(
|
||||
ge: ^Group_Element,
|
||||
a: ^Scalar,
|
||||
A: ^Group_Element,
|
||||
b: ^Scalar,
|
||||
) {
|
||||
_ge_assert_initialized([]^Group_Element{A})
|
||||
|
||||
grp.ge_double_scalarmult_basepoint_vartime(&ge._p, a, &A._p, b)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_cond_negate sets `ge = a` iff `ctrl == 0` and `ge = -a` iff `ctrl == 1`.
|
||||
// Behavior for all other values of ctrl are undefined,
|
||||
ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) {
|
||||
_ge_assert_initialized([]^Group_Element{a})
|
||||
|
||||
grp.ge_cond_negate(&ge._p, &a._p, ctrl)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_cond_assign sets `ge = ge` iff `ctrl == 0` and `ge = a` iff `ctrl == 1`.
|
||||
// Behavior for all other values of ctrl are undefined,
|
||||
ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) {
|
||||
_ge_assert_initialized([]^Group_Element{ge, a})
|
||||
|
||||
grp.ge_cond_assign(&ge._p, &a._p, ctrl)
|
||||
}
|
||||
|
||||
// ge_cond_select sets `ge = a` iff `ctrl == 0` and `ge = b` iff `ctrl == 1`.
|
||||
// Behavior for all other values of ctrl are undefined,
|
||||
ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) {
|
||||
_ge_assert_initialized([]^Group_Element{a, b})
|
||||
|
||||
grp.ge_cond_select(&ge._p, &a._p, &b._p, ctrl)
|
||||
ge._is_initialized = true
|
||||
}
|
||||
|
||||
// ge_equal returns 1 iff `a == b`, and 0 otherwise.
|
||||
@(require_results)
|
||||
ge_equal :: proc(a, b: ^Group_Element) -> int {
|
||||
_ge_assert_initialized([]^Group_Element{a, b})
|
||||
|
||||
// CT_EQ(x1 * y2, y1 * x2) | CT_EQ(y1 * y2, x1 * x2)
|
||||
ax_by, ay_bx, ay_by, ax_bx: field.Tight_Field_Element = ---, ---, ---, ---
|
||||
field.fe_carry_mul(&ax_by, field.fe_relax_cast(&a._p.x), field.fe_relax_cast(&b._p.y))
|
||||
field.fe_carry_mul(&ay_bx, field.fe_relax_cast(&a._p.y), field.fe_relax_cast(&b._p.x))
|
||||
field.fe_carry_mul(&ay_by, field.fe_relax_cast(&a._p.y), field.fe_relax_cast(&b._p.y))
|
||||
field.fe_carry_mul(&ax_bx, field.fe_relax_cast(&a._p.x), field.fe_relax_cast(&b._p.x))
|
||||
|
||||
ret := field.fe_equal(&ax_by, &ay_bx) | field.fe_equal(&ay_by, &ax_bx)
|
||||
|
||||
field.fe_clear_vec([]^field.Tight_Field_Element{&ax_by, &ay_bx, &ay_by, &ax_bx})
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// ge_is_identity returns 1 iff `ge` is the identity element, and 0 otherwise.
|
||||
@(require_results)
|
||||
ge_is_identity :: proc(ge: ^Group_Element) -> int {
|
||||
return ge_equal(ge, &GE_IDENTITY)
|
||||
}
|
||||
|
||||
@(private)
|
||||
ge_map :: proc "contextless" (ge: ^Group_Element, b: []byte) {
|
||||
b_ := transmute(^[32]byte)(raw_data(b))
|
||||
|
||||
// The MAP function is defined on 32-byte strings as:
|
||||
//
|
||||
// 1. Mask the most significant bit in the final byte of the string,
|
||||
// and interpret the string as an unsigned integer r in little-
|
||||
// endian representation. Reduce r modulo p to obtain a field
|
||||
// element t.
|
||||
// * Masking the most significant bit is equivalent to interpreting
|
||||
// the whole string as an unsigned integer in little-endian
|
||||
// representation and then reducing it modulo 2^255.
|
||||
t: field.Tight_Field_Element = ---
|
||||
field.fe_from_bytes(&t, b_)
|
||||
|
||||
// 2. Process t as follows:
|
||||
//
|
||||
// r = SQRT_M1 * t^2
|
||||
// u = (r + 1) * ONE_MINUS_D_SQ
|
||||
// v = (-1 - r*D) * (r + D)
|
||||
tmp1: field.Loose_Field_Element = ---
|
||||
r, u, v: field.Tight_Field_Element = ---, ---, ---
|
||||
|
||||
field.fe_carry_square(&r, field.fe_relax_cast(&t))
|
||||
field.fe_carry_mul(&r, field.fe_relax_cast(&field.FE_SQRT_M1), field.fe_relax_cast(&r))
|
||||
|
||||
field.fe_add(&tmp1, &field.FE_ONE, &r)
|
||||
field.fe_carry_mul(&u, &tmp1, field.fe_relax_cast(&FE_ONE_MINUS_D_SQ))
|
||||
|
||||
field.fe_carry_mul(&v, field.fe_relax_cast(&r), field.fe_relax_cast(&grp.FE_D))
|
||||
field.fe_carry_add(&v, &field.FE_ONE, &v)
|
||||
field.fe_carry_opp(&v, &v)
|
||||
field.fe_add(&tmp1, &r, &grp.FE_D)
|
||||
field.fe_carry_mul(&v, field.fe_relax_cast(&v), &tmp1)
|
||||
|
||||
// (was_square, s) = SQRT_RATIO_M1(u, v)
|
||||
// s_prime = -CT_ABS(s*t)
|
||||
// s = CT_SELECT(s IF was_square ELSE s_prime)
|
||||
// c = CT_SELECT(-1 IF was_square ELSE r)
|
||||
s, s_prime, c: field.Tight_Field_Element = ---, ---, ---
|
||||
was_square := field.fe_carry_sqrt_ratio_m1(
|
||||
&s,
|
||||
field.fe_relax_cast(&u),
|
||||
field.fe_relax_cast(&v),
|
||||
)
|
||||
field.fe_carry_mul(&s_prime, field.fe_relax_cast(&s), field.fe_relax_cast(&t))
|
||||
field.fe_carry_abs(&s_prime, &s_prime)
|
||||
field.fe_carry_opp(&s_prime, &s_prime)
|
||||
field.fe_cond_select(&s, &s_prime, &s, was_square)
|
||||
field.fe_cond_select(&c, &r, &FE_NEG_ONE, was_square)
|
||||
|
||||
// N = c * (r - 1) * D_MINUS_ONE_SQ - v
|
||||
N: field.Tight_Field_Element = ---
|
||||
field.fe_sub(&tmp1, &r, &field.FE_ONE)
|
||||
field.fe_carry_mul(&N, field.fe_relax_cast(&c), &tmp1)
|
||||
field.fe_carry_mul(&N, field.fe_relax_cast(&N), field.fe_relax_cast(&FE_D_MINUS_ONE_SQUARED))
|
||||
field.fe_carry_sub(&N, &N, &v)
|
||||
|
||||
// w0 = 2 * s * v
|
||||
// w1 = N * SQRT_AD_MINUS_ONE
|
||||
// w2 = 1 - s^2
|
||||
// w3 = 1 + s^2
|
||||
w0, w1: field.Tight_Field_Element = ---, ---
|
||||
w2, w3: field.Loose_Field_Element = ---, ---
|
||||
field.fe_carry_mul(&w0, field.fe_relax_cast(&s), field.fe_relax_cast(&v))
|
||||
field.fe_carry_add(&w0, &w0, &w0)
|
||||
field.fe_carry_mul(&w1, field.fe_relax_cast(&N), field.fe_relax_cast(&FE_SQRT_AD_MINUS_ONE))
|
||||
field.fe_carry_square(&s, field.fe_relax_cast(&s))
|
||||
field.fe_sub(&w2, &field.FE_ONE, &s)
|
||||
field.fe_add(&w3, &field.FE_ONE, &s)
|
||||
|
||||
// 3. Return the group element represented by the internal
|
||||
// representation (w0*w3, w2*w1, w1*w3, w0*w2).
|
||||
|
||||
field.fe_carry_mul(&ge._p.x, field.fe_relax_cast(&w0), &w3)
|
||||
field.fe_carry_mul(&ge._p.y, &w2, field.fe_relax_cast(&w1))
|
||||
field.fe_carry_mul(&ge._p.z, field.fe_relax_cast(&w1), &w3)
|
||||
field.fe_carry_mul(&ge._p.t, field.fe_relax_cast(&w0), &w2)
|
||||
ge._is_initialized = true
|
||||
|
||||
field.fe_clear_vec([]^field.Tight_Field_Element{&r, &u, &v, &s, &s_prime, &c, &N, &w0, &w1})
|
||||
field.fe_clear_vec([]^field.Loose_Field_Element{&tmp1, &w2, &w3})
|
||||
}
|
||||
|
||||
@(private)
|
||||
_ge_assert_initialized :: proc(ges: []^Group_Element) {
|
||||
for ge in ges {
|
||||
if !ge._is_initialized {
|
||||
panic("crypto/ristretto255: uninitialized group element")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package ristretto255
|
||||
|
||||
import grp "core:crypto/_edwards25519"
|
||||
|
||||
// SCALAR_SIZE is the size of a byte-encoded ristretto255 scalar.
|
||||
SCALAR_SIZE :: 32
|
||||
// WIDE_SCALAR_SIZE is the size of a wide byte-encoded ristretto255
|
||||
// scalar.
|
||||
WIDE_SCALAR_SIZE :: 64
|
||||
|
||||
// Scalar is a ristretto255 scalar. The zero-initialized value is valid,
|
||||
// and represents `0`.
|
||||
Scalar :: grp.Scalar
|
||||
|
||||
// sc_clear clears sc to the uninitialized state.
|
||||
sc_clear :: proc "contextless" (sc: ^Scalar) {
|
||||
grp.sc_clear(sc)
|
||||
}
|
||||
|
||||
// sc_set sets `sc = a`.
|
||||
sc_set :: proc "contextless" (sc, a: ^Scalar) {
|
||||
grp.sc_set(sc, a)
|
||||
}
|
||||
|
||||
// sc_set_u64 sets `sc = i`.
|
||||
sc_set_u64 :: proc "contextless" (sc: ^Scalar, i: u64) {
|
||||
grp.sc_set_u64(sc, i)
|
||||
}
|
||||
|
||||
// sc_set_bytes sets sc to the result of decoding b as a ristretto255
|
||||
// scalar, and returns true on success.
|
||||
@(require_results)
|
||||
sc_set_bytes :: proc(sc: ^Scalar, b: []byte) -> bool {
|
||||
if len(b) != SCALAR_SIZE {
|
||||
return false
|
||||
}
|
||||
|
||||
return grp.sc_set_bytes(sc, b)
|
||||
}
|
||||
|
||||
// sc_set_wide_bytes sets sc to the result of deriving a ristretto255
|
||||
// scalar, from a wide (512-bit) byte string by interpreting b as a
|
||||
// little-endian value, and reducing it mod the group order.
|
||||
sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
|
||||
if len(b) != WIDE_SCALAR_SIZE {
|
||||
panic("crypto/ristretto255: invalid wide input size")
|
||||
}
|
||||
|
||||
b_ := transmute(^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
|
||||
grp.sc_set_bytes_wide(sc, b_)
|
||||
}
|
||||
|
||||
// sc_bytes sets dst to the canonical encoding of sc.
|
||||
sc_bytes :: proc(sc: ^Scalar, dst: []byte) {
|
||||
if len(dst) != SCALAR_SIZE {
|
||||
panic("crypto/ristretto255: invalid destination size")
|
||||
}
|
||||
|
||||
grp.sc_bytes(dst, sc)
|
||||
}
|
||||
|
||||
// sc_add sets `sc = a + b`.
|
||||
sc_add :: proc "contextless" (sc, a, b: ^Scalar) {
|
||||
grp.sc_add(sc, a, b)
|
||||
}
|
||||
|
||||
// sc_sub sets `sc = a - b`.
|
||||
sc_sub :: proc "contextless" (sc, a, b: ^Scalar) {
|
||||
grp.sc_sub(sc, a, b)
|
||||
}
|
||||
|
||||
// sc_negate sets `sc = -a`.
|
||||
sc_negate :: proc "contextless" (sc, a: ^Scalar) {
|
||||
grp.sc_negate(sc, a)
|
||||
}
|
||||
|
||||
// sc_mul sets `sc = a * b`.
|
||||
sc_mul :: proc "contextless" (sc, a, b: ^Scalar) {
|
||||
grp.sc_mul(sc, a, b)
|
||||
}
|
||||
|
||||
// sc_square sets `sc = a^2`.
|
||||
sc_square :: proc "contextless" (sc, a: ^Scalar) {
|
||||
grp.sc_square(sc, a)
|
||||
}
|
||||
|
||||
// sc_cond_assign sets `sc = sc` iff `ctrl == 0` and `sc = a` iff `ctrl == 1`.
|
||||
// Behavior for all other values of ctrl are undefined,
|
||||
sc_cond_assign :: proc(sc, a: ^Scalar, ctrl: int) {
|
||||
grp.sc_cond_assign(sc, a, ctrl)
|
||||
}
|
||||
|
||||
// sc_equal returns 1 iff `a == b`, and 0 otherwise.
|
||||
@(require_results)
|
||||
sc_equal :: proc(a, b: ^Scalar) -> int {
|
||||
return grp.sc_equal(a, b)
|
||||
}
|
||||
+92
-429
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
package sha2 implements the SHA2 hash algorithm family.
|
||||
|
||||
See:
|
||||
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
|
||||
- https://datatracker.ietf.org/doc/html/rfc3874
|
||||
*/
|
||||
package sha2
|
||||
|
||||
/*
|
||||
@@ -6,431 +13,83 @@ package sha2
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the SHA2 hashing algorithm, as defined in <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf>
|
||||
and in RFC 3874 <https://datatracker.ietf.org/doc/html/rfc3874>
|
||||
*/
|
||||
|
||||
import "core:encoding/endian"
|
||||
import "core:io"
|
||||
import "core:math/bits"
|
||||
import "core:os"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
import "core:mem"
|
||||
|
||||
// DIGEST_SIZE_224 is the SHA-224 digest size in bytes.
|
||||
DIGEST_SIZE_224 :: 28
|
||||
// DIGEST_SIZE_256 is the SHA-256 digest size in bytes.
|
||||
DIGEST_SIZE_256 :: 32
|
||||
// DIGEST_SIZE_384 is the SHA-384 digest size in bytes.
|
||||
DIGEST_SIZE_384 :: 48
|
||||
// DIGEST_SIZE_512 is the SHA-512 digest size in bytes.
|
||||
DIGEST_SIZE_512 :: 64
|
||||
// DIGEST_SIZE_512_256 is the SHA-512/256 digest size in bytes.
|
||||
DIGEST_SIZE_512_256 :: 32
|
||||
|
||||
// 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))
|
||||
// BLOCK_SIZE_256 is the SHA-224 and SHA-256 block size in bytes.
|
||||
BLOCK_SIZE_256 :: 64
|
||||
// BLOCK_SIZE_512 is the SHA-384, SHA-512, and SHA-512/256 block size
|
||||
// in bytes.
|
||||
BLOCK_SIZE_512 :: 128
|
||||
|
||||
// Context_256 is a SHA-224 or SHA-256 instance.
|
||||
Context_256 :: struct {
|
||||
block: [BLOCK_SIZE_256]byte,
|
||||
h: [8]u32,
|
||||
bitlength: u64,
|
||||
length: u64,
|
||||
md_bits: int,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
// 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: Context_256
|
||||
// Context_512 is a SHA-384, SHA-512 or SHA-512/256 instance.
|
||||
Context_512 :: struct {
|
||||
block: [BLOCK_SIZE_512]byte,
|
||||
h: [8]u64,
|
||||
bitlength: u64,
|
||||
length: u64,
|
||||
md_bits: int,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
// init_224 initializes a Context_256 for SHA-224.
|
||||
init_224 :: proc(ctx: ^Context_256) {
|
||||
ctx.md_bits = 224
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context_256
|
||||
ctx.md_bits = 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: Context_256
|
||||
ctx.md_bits = 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: Context_256
|
||||
// init_256 initializes a Context_256 for SHA-256.
|
||||
init_256 :: proc(ctx: ^Context_256) {
|
||||
ctx.md_bits = 256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context_256
|
||||
ctx.md_bits = 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: Context_256
|
||||
ctx.md_bits = 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: Context_512
|
||||
// init_384 initializes a Context_512 for SHA-384.
|
||||
init_384 :: proc(ctx: ^Context_512) {
|
||||
ctx.md_bits = 384
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context_512
|
||||
ctx.md_bits = 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: Context_512
|
||||
ctx.md_bits = 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: Context_512
|
||||
// init_512 initializes a Context_512 for SHA-512.
|
||||
init_512 :: proc(ctx: ^Context_512) {
|
||||
ctx.md_bits = 512
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context_512
|
||||
ctx.md_bits = 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: Context_512
|
||||
ctx.md_bits = 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,
|
||||
}
|
||||
|
||||
// hash_string_512_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512_256 :: proc(data: string) -> [DIGEST_SIZE_512_256]byte {
|
||||
return hash_bytes_512_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512_256 :: proc(data: []byte) -> [DIGEST_SIZE_512_256]byte {
|
||||
hash: [DIGEST_SIZE_512_256]byte
|
||||
ctx: Context_512
|
||||
// init_512_256 initializes a Context_512 for SHA-512/256.
|
||||
init_512_256 :: proc(ctx: ^Context_512) {
|
||||
ctx.md_bits = 256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_512_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_512_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_512_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_512_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_512_256 :: proc(data, hash: []byte) {
|
||||
ctx: Context_512
|
||||
ctx.md_bits = 256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_512_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_512_256]byte
|
||||
ctx: Context_512
|
||||
ctx.md_bits = 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_512_256 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_512_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512_256]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_512_256(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_512_256(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_512_256]byte{}, false
|
||||
}
|
||||
|
||||
hash_512_256 :: proc {
|
||||
hash_stream_512_256,
|
||||
hash_file_512_256,
|
||||
hash_bytes_512_256,
|
||||
hash_string_512_256,
|
||||
hash_bytes_to_buffer_512_256,
|
||||
hash_string_to_buffer_512_256,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^$T) {
|
||||
@(private)
|
||||
_init :: proc(ctx: ^$T) {
|
||||
when T == Context_256 {
|
||||
switch ctx.md_bits {
|
||||
case 224:
|
||||
@@ -497,13 +156,14 @@ init :: proc(ctx: ^$T) {
|
||||
ctx.is_initialized = true
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^$T, data: []byte) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
when T == Context_256 {
|
||||
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
|
||||
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
|
||||
} else when T == Context_512 {
|
||||
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
|
||||
CURR_BLOCK_SIZE :: BLOCK_SIZE_512
|
||||
}
|
||||
|
||||
data := data
|
||||
@@ -528,21 +188,34 @@ update :: proc(ctx: ^$T, data: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^$T, hash: []byte) {
|
||||
// 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: ^$T, hash: []byte, finalize_clone: bool = false) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
if len(hash) * 8 < ctx.md_bits {
|
||||
panic("crypto/sha2: invalid destination digest size")
|
||||
}
|
||||
|
||||
ctx := ctx
|
||||
if finalize_clone {
|
||||
tmp_ctx: T
|
||||
clone(&tmp_ctx, ctx)
|
||||
ctx = &tmp_ctx
|
||||
}
|
||||
defer(reset(ctx))
|
||||
|
||||
length := ctx.length
|
||||
|
||||
raw_pad: [SHA512_BLOCK_SIZE]byte
|
||||
raw_pad: [BLOCK_SIZE_512]byte
|
||||
when T == Context_256 {
|
||||
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
|
||||
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
|
||||
pm_len := 8 // 64-bits for length
|
||||
} else when T == Context_512 {
|
||||
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
|
||||
CURR_BLOCK_SIZE :: BLOCK_SIZE_512
|
||||
pm_len := 16 // 128-bits for length
|
||||
}
|
||||
pad := raw_pad[:CURR_BLOCK_SIZE]
|
||||
@@ -576,37 +249,27 @@ final :: proc(ctx: ^$T, hash: []byte) {
|
||||
endian.unchecked_put_u64be(hash[i * 8:], ctx.h[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.is_initialized = false
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^$T) {
|
||||
ctx^ = other^
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^$T) {
|
||||
if !ctx.is_initialized {
|
||||
return
|
||||
}
|
||||
|
||||
mem.zero_explicit(ctx, size_of(ctx^))
|
||||
}
|
||||
|
||||
/*
|
||||
SHA2 implementation
|
||||
*/
|
||||
|
||||
SHA256_BLOCK_SIZE :: 64
|
||||
SHA512_BLOCK_SIZE :: 128
|
||||
|
||||
Context_256 :: struct {
|
||||
block: [SHA256_BLOCK_SIZE]byte,
|
||||
h: [8]u32,
|
||||
bitlength: u64,
|
||||
length: u64,
|
||||
md_bits: int,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
Context_512 :: struct {
|
||||
block: [SHA512_BLOCK_SIZE]byte,
|
||||
h: [8]u64,
|
||||
bitlength: u64,
|
||||
length: u64,
|
||||
md_bits: int,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
@(private)
|
||||
sha256_k := [64]u32 {
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
|
||||
@@ -737,12 +400,12 @@ sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) {
|
||||
w: [64]u32
|
||||
wv: [8]u32
|
||||
t1, t2: u32
|
||||
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
|
||||
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
|
||||
} else when T == Context_512 {
|
||||
w: [80]u64
|
||||
wv: [8]u64
|
||||
t1, t2: u64
|
||||
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
|
||||
CURR_BLOCK_SIZE :: BLOCK_SIZE_512
|
||||
}
|
||||
|
||||
data := data
|
||||
|
||||
+61
-328
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
package sha3 implements the SHA3 hash algorithm family.
|
||||
|
||||
The SHAKE XOF can be found in crypto/shake. While discouraged if the
|
||||
pre-standardization Keccak algorithm is required, it can be found in
|
||||
crypto/legacy/keccak.
|
||||
|
||||
See:
|
||||
- https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf
|
||||
*/
|
||||
package sha3
|
||||
|
||||
/*
|
||||
@@ -6,359 +16,82 @@ package sha3
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Interface for the SHA3 hashing algorithm. The SHAKE functionality can be found in package shake.
|
||||
If you wish to compute a Keccak hash, you can use the keccak package, it will use the original padding.
|
||||
*/
|
||||
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
|
||||
import "../_sha3"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// DIGEST_SIZE_224 is the SHA3-224 digest size.
|
||||
DIGEST_SIZE_224 :: 28
|
||||
// DIGEST_SIZE_256 is the SHA3-256 digest size.
|
||||
DIGEST_SIZE_256 :: 32
|
||||
// DIGEST_SIZE_384 is the SHA3-384 digest size.
|
||||
DIGEST_SIZE_384 :: 48
|
||||
// DIGEST_SIZE_512 is the SHA3-512 digest size.
|
||||
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))
|
||||
}
|
||||
// BLOCK_SIZE_224 is the SHA3-224 block size in bytes.
|
||||
BLOCK_SIZE_224 :: _sha3.RATE_224
|
||||
// BLOCK_SIZE_256 is the SHA3-256 block size in bytes.
|
||||
BLOCK_SIZE_256 :: _sha3.RATE_256
|
||||
// BLOCK_SIZE_384 is the SHA3-384 block size in bytes.
|
||||
BLOCK_SIZE_384 :: _sha3.RATE_384
|
||||
// BLOCK_SIZE_512 is the SHA3-512 block size in bytes.
|
||||
BLOCK_SIZE_512 :: _sha3.RATE_512
|
||||
|
||||
// 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: Context
|
||||
// Context is a SHA3 instance.
|
||||
Context :: distinct _sha3.Context
|
||||
|
||||
// init_224 initializes a Context for SHA3-224.
|
||||
init_224 :: proc(ctx: ^Context) {
|
||||
ctx.mdlen = DIGEST_SIZE_224
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_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: Context
|
||||
ctx.mdlen = DIGEST_SIZE_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: Context
|
||||
// init_256 initializes a Context for SHA3-256.
|
||||
init_256 :: proc(ctx: ^Context) {
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_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: Context
|
||||
ctx.mdlen = DIGEST_SIZE_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: Context
|
||||
// init_384 initializes a Context for SHA3-384.
|
||||
init_384 :: proc(ctx: ^Context) {
|
||||
ctx.mdlen = DIGEST_SIZE_384
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_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: Context
|
||||
ctx.mdlen = DIGEST_SIZE_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: Context
|
||||
// init_512 initializes a Context for SHA3-512.
|
||||
init_512 :: proc(ctx: ^Context) {
|
||||
ctx.mdlen = DIGEST_SIZE_512
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
_init(ctx)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_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: Context
|
||||
ctx.mdlen = DIGEST_SIZE_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
|
||||
*/
|
||||
|
||||
Context :: _sha3.Sha3_Context
|
||||
|
||||
init :: proc(ctx: ^Context) {
|
||||
_sha3.init(ctx)
|
||||
@(private)
|
||||
_init :: proc(ctx: ^Context) {
|
||||
ctx.dsbyte = _sha3.DS_SHA3
|
||||
_sha3.init(transmute(^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
_sha3.update(ctx, data)
|
||||
_sha3.update(transmute(^_sha3.Context)(ctx), data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Context, hash: []byte) {
|
||||
_sha3.final(ctx, 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) {
|
||||
_sha3.final(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
|
||||
}
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
+44
-178
@@ -1,3 +1,12 @@
|
||||
/*
|
||||
package shake implements the SHAKE and cSHAKE XOF algorithm families.
|
||||
|
||||
The SHA3 hash algorithm can be found in the crypto/sha3.
|
||||
|
||||
See:
|
||||
- https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf
|
||||
- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
|
||||
*/
|
||||
package shake
|
||||
|
||||
/*
|
||||
@@ -6,201 +15,58 @@ package shake
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Interface for the SHAKE hashing algorithm.
|
||||
The SHA3 functionality can be found in package sha3.
|
||||
|
||||
TODO: This should provide an incremental squeeze interface, in addition
|
||||
to the one-shot final call.
|
||||
*/
|
||||
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
|
||||
import "../_sha3"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
// Context is a SHAKE128, SHAKE256, cSHAKE128, or cSHAKE256 instance.
|
||||
Context :: distinct _sha3.Context
|
||||
|
||||
DIGEST_SIZE_128 :: 16
|
||||
DIGEST_SIZE_256 :: 32
|
||||
|
||||
// hash_string_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
|
||||
return hash_bytes_128(transmute([]byte)(data))
|
||||
// init_128 initializes a Context for SHAKE128.
|
||||
init_128 :: proc(ctx: ^Context) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 128)
|
||||
}
|
||||
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
|
||||
hash: [DIGEST_SIZE_128]byte
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_128
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
// init_256 initializes a Context for SHAKE256.
|
||||
init_256 :: proc(ctx: ^Context) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 256)
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_128 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_128 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
|
||||
// init_cshake_128 initializes a Context for cSHAKE128.
|
||||
init_cshake_128 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, domain_sep, 128)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_128 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_128 :: proc(data, hash: []byte) {
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_128
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
// init_cshake_256 initializes a Context for cSHAKE256.
|
||||
init_cshake_256 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, domain_sep, 256)
|
||||
}
|
||||
|
||||
// hash_stream_128 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
|
||||
hash: [DIGEST_SIZE_128]byte
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_128
|
||||
init(&ctx)
|
||||
// write writes more data into the SHAKE instance. This MUST not be called
|
||||
// after any reads have been done, and attempts to do so will panic.
|
||||
write :: proc(ctx: ^Context, data: []byte) {
|
||||
_sha3.update(transmute(^_sha3.Context)(ctx), data)
|
||||
}
|
||||
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = io.read(s, buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
// read reads output from the SHAKE instance. There is no practical upper
|
||||
// limit to the amount of data that can be read from SHAKE. After read has
|
||||
// been called one or more times, further calls to write will panic.
|
||||
read :: proc(ctx: ^Context, dst: []byte) {
|
||||
ctx_ := transmute(^_sha3.Context)(ctx)
|
||||
if !ctx.is_finalized {
|
||||
_sha3.shake_xof(ctx_)
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
|
||||
_sha3.shake_out(ctx_, dst)
|
||||
}
|
||||
|
||||
// hash_file_128 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_128(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_128(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_128]byte{}, false
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
||||
}
|
||||
|
||||
hash_128 :: proc {
|
||||
hash_stream_128,
|
||||
hash_file_128,
|
||||
hash_bytes_128,
|
||||
hash_string_128,
|
||||
hash_bytes_to_buffer_128,
|
||||
hash_string_to_buffer_128,
|
||||
}
|
||||
|
||||
// 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: Context
|
||||
ctx.mdlen = DIGEST_SIZE_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) {
|
||||
ctx: Context
|
||||
ctx.mdlen = DIGEST_SIZE_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: Context
|
||||
ctx.mdlen = DIGEST_SIZE_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,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
Context :: _sha3.Sha3_Context
|
||||
|
||||
init :: proc(ctx: ^Context) {
|
||||
_sha3.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
_sha3.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Context, hash: []byte) {
|
||||
_sha3.shake_xof(ctx)
|
||||
_sha3.shake_out(ctx, hash[:])
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
+49
-100
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
package sm3 implements the SM3 hash algorithm.
|
||||
|
||||
See:
|
||||
- https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02
|
||||
*/
|
||||
package sm3
|
||||
|
||||
/*
|
||||
@@ -6,102 +12,29 @@ package sm3
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the SM3 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02>
|
||||
*/
|
||||
|
||||
import "core:encoding/endian"
|
||||
import "core:io"
|
||||
import "core:math/bits"
|
||||
import "core:os"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
import "core:mem"
|
||||
|
||||
// DIGEST_SIZE is the SM3 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 SM3 block size in bytes.
|
||||
BLOCK_SIZE :: 64
|
||||
|
||||
// Context is a SM3 instance.
|
||||
Context :: struct {
|
||||
state: [8]u32,
|
||||
x: [BLOCK_SIZE]byte,
|
||||
bitlength: u64,
|
||||
length: u64,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
// 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: 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) {
|
||||
ctx: 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: 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 initializes a Context.
|
||||
init :: proc(ctx: ^Context) {
|
||||
ctx.state[0] = IV[0]
|
||||
ctx.state[1] = IV[1]
|
||||
@@ -118,6 +51,7 @@ init :: proc(ctx: ^Context) {
|
||||
ctx.is_initialized = true
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
@@ -143,13 +77,26 @@ update :: proc(ctx: ^Context, data: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Context, hash: []byte) {
|
||||
// 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) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
if len(hash) < DIGEST_SIZE {
|
||||
panic("crypto/sm3: invalid destination digest size")
|
||||
}
|
||||
|
||||
ctx := ctx
|
||||
if finalize_clone {
|
||||
tmp_ctx: Context
|
||||
clone(&tmp_ctx, ctx)
|
||||
ctx = &tmp_ctx
|
||||
}
|
||||
defer(reset(ctx))
|
||||
|
||||
length := ctx.length
|
||||
|
||||
pad: [BLOCK_SIZE]byte
|
||||
@@ -168,25 +115,27 @@ final :: proc(ctx: ^Context, hash: []byte) {
|
||||
for i := 0; i < DIGEST_SIZE / 4; i += 1 {
|
||||
endian.unchecked_put_u32be(hash[i * 4:], ctx.state[i])
|
||||
}
|
||||
}
|
||||
|
||||
ctx.is_initialized = false
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
ctx^ = other^
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
if !ctx.is_initialized {
|
||||
return
|
||||
}
|
||||
|
||||
mem.zero_explicit(ctx, size_of(ctx^))
|
||||
}
|
||||
|
||||
/*
|
||||
SM3 implementation
|
||||
*/
|
||||
|
||||
BLOCK_SIZE :: 64
|
||||
|
||||
Context :: struct {
|
||||
state: [8]u32,
|
||||
x: [BLOCK_SIZE]byte,
|
||||
bitlength: u64,
|
||||
length: u64,
|
||||
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
@(private)
|
||||
IV := [8]u32 {
|
||||
0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
package tuplehash implements the TupleHash and TupleHashXOF algorithms.
|
||||
|
||||
See:
|
||||
- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
|
||||
*/
|
||||
package tuplehash
|
||||
|
||||
import "../_sha3"
|
||||
|
||||
// Context is a TupleHash or TupleHashXOF instance.
|
||||
Context :: distinct _sha3.Context
|
||||
|
||||
// init_128 initializes a Context for TupleHash128 or TupleHashXOF128.
|
||||
init_128 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 128)
|
||||
}
|
||||
|
||||
// init_256 initializes a Context for TupleHash256 or TupleHashXOF256.
|
||||
init_256 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 256)
|
||||
}
|
||||
|
||||
// write_element writes a tuple element into the TupleHash or TupleHashXOF
|
||||
// instance. This MUST not be called after any reads have been done, and
|
||||
// any attempts to do so will panic.
|
||||
write_element :: proc(ctx: ^Context, data: []byte) {
|
||||
_, _ = _sha3.encode_string(transmute(^_sha3.Context)(ctx), data)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
_sha3.final_cshake(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
|
||||
}
|
||||
|
||||
// read reads output from the TupleHashXOF instance. There is no practical
|
||||
// upper limit to the amount of data that can be read from TupleHashXOF.
|
||||
// After read has been called one or more times, further calls to
|
||||
// write_element will panic.
|
||||
read :: proc(ctx: ^Context, dst: []byte) {
|
||||
ctx_ := transmute(^_sha3.Context)(ctx)
|
||||
if !ctx.is_finalized {
|
||||
_sha3.encode_byte_len(ctx_, 0, false) // right_encode
|
||||
_sha3.shake_xof(ctx_)
|
||||
}
|
||||
|
||||
_sha3.shake_out(ctx_, dst)
|
||||
}
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
@(private)
|
||||
N_TUPLEHASH := []byte{'T', 'u', 'p', 'l', 'e', 'H', 'a', 's', 'h'}
|
||||
@@ -1,9 +1,18 @@
|
||||
/*
|
||||
package x25519 implements the X25519 (aka curve25519) Elliptic-Curve
|
||||
Diffie-Hellman key exchange protocol.
|
||||
|
||||
See:
|
||||
- https://www.rfc-editor.org/rfc/rfc7748
|
||||
*/
|
||||
package x25519
|
||||
|
||||
import field "core:crypto/_fiat/field_curve25519"
|
||||
import "core:mem"
|
||||
|
||||
// SCALAR_SIZE is the size of a X25519 scalar (private key) in bytes.
|
||||
SCALAR_SIZE :: 32
|
||||
// POINT_SIZE is the size of a X25519 point (public key/shared secret) in bytes.
|
||||
POINT_SIZE :: 32
|
||||
|
||||
@(private)
|
||||
@@ -14,11 +23,11 @@ _scalar_bit :: #force_inline proc "contextless" (s: ^[32]byte, i: int) -> u8 {
|
||||
if i < 0 {
|
||||
return 0
|
||||
}
|
||||
return (s[i>>3] >> uint(i&7)) & 1
|
||||
return (s[i >> 3] >> uint(i & 7)) & 1
|
||||
}
|
||||
|
||||
@(private)
|
||||
_scalarmult :: proc (out, scalar, point: ^[32]byte) {
|
||||
_scalarmult :: proc "contextless" (out, scalar, point: ^[32]byte) {
|
||||
// Montgomery pseduo-multiplication taken from Monocypher.
|
||||
|
||||
// computes the scalar product
|
||||
@@ -26,7 +35,7 @@ _scalarmult :: proc (out, scalar, point: ^[32]byte) {
|
||||
field.fe_from_bytes(&x1, point)
|
||||
|
||||
// computes the actual scalar product (the result is in x2 and z2)
|
||||
x2, x3, z2, z3: field.Tight_Field_Element = ---, ---, ---, ---
|
||||
x2, x3, z2, z3: field.Tight_Field_Element = ---, ---, ---, ---
|
||||
t0, t1: field.Loose_Field_Element = ---, ---
|
||||
|
||||
// Montgomery ladder
|
||||
@@ -38,7 +47,7 @@ _scalarmult :: proc (out, scalar, point: ^[32]byte) {
|
||||
field.fe_one(&z3)
|
||||
|
||||
swap: int
|
||||
for pos := 255-1; pos >= 0; pos = pos - 1 {
|
||||
for pos := 255 - 1; pos >= 0; pos = pos - 1 {
|
||||
// constant time conditional swap before ladder step
|
||||
b := int(_scalar_bit(scalar, pos))
|
||||
swap ~= b // xor trick avoids swapping at the end of the loop
|
||||
@@ -85,16 +94,13 @@ _scalarmult :: proc (out, scalar, point: ^[32]byte) {
|
||||
field.fe_carry_mul(&x2, field.fe_relax_cast(&x2), field.fe_relax_cast(&z2))
|
||||
field.fe_to_bytes(out, &x2)
|
||||
|
||||
mem.zero_explicit(&x1, size_of(x1))
|
||||
mem.zero_explicit(&x2, size_of(x2))
|
||||
mem.zero_explicit(&x3, size_of(x3))
|
||||
mem.zero_explicit(&z2, size_of(z2))
|
||||
mem.zero_explicit(&z3, size_of(z3))
|
||||
mem.zero_explicit(&t0, size_of(t0))
|
||||
mem.zero_explicit(&t1, size_of(t1))
|
||||
field.fe_clear_vec([]^field.Tight_Field_Element{&x1, &x2, &x3, &z2, &z3})
|
||||
field.fe_clear_vec([]^field.Loose_Field_Element{&t0, &t1})
|
||||
}
|
||||
|
||||
scalarmult :: proc (dst, scalar, point: []byte) {
|
||||
// scalarmult "multiplies" the provided scalar and point, and writes the
|
||||
// resulting point to dst.
|
||||
scalarmult :: proc(dst, scalar, point: []byte) {
|
||||
if len(scalar) != SCALAR_SIZE {
|
||||
panic("crypto/x25519: invalid scalar size")
|
||||
}
|
||||
@@ -123,7 +129,8 @@ scalarmult :: proc (dst, scalar, point: []byte) {
|
||||
mem.zero_explicit(&d, size_of(d))
|
||||
}
|
||||
|
||||
scalarmult_basepoint :: proc (dst, scalar: []byte) {
|
||||
// TODO/perf: Switch to using a precomputed table.
|
||||
// scalarmult_basepoint "multiplies" the provided scalar with the X25519
|
||||
// base point and writes the resulting point to dst.
|
||||
scalarmult_basepoint :: proc(dst, scalar: []byte) {
|
||||
scalarmult(dst, scalar, _BASE_POINT[:])
|
||||
}
|
||||
|
||||
+18
-25
@@ -123,40 +123,34 @@ Returns:
|
||||
|
||||
See doc.odin for an example.
|
||||
*/
|
||||
initialize_symbols :: proc(symbol_table: ^$T, library_path: string, symbol_prefix := "", handle_field_name := "__handle") -> (count: int, ok: bool) where intrinsics.type_is_struct(T) {
|
||||
initialize_symbols :: proc(
|
||||
symbol_table: ^$T, library_path: string,
|
||||
symbol_prefix := "", handle_field_name := "__handle",
|
||||
) -> (count: int = -1, ok: bool = false) where intrinsics.type_is_struct(T) {
|
||||
assert(symbol_table != nil)
|
||||
handle: Library
|
||||
|
||||
if handle, ok = load_library(library_path); !ok {
|
||||
return -1, false
|
||||
}
|
||||
|
||||
// `symbol_table` must be a struct because of the where clause, so this can't fail.
|
||||
ti := runtime.type_info_base(type_info_of(T))
|
||||
s, _ := ti.variant.(runtime.Type_Info_Struct)
|
||||
handle := load_library(library_path) or_return
|
||||
|
||||
// Buffer to concatenate the prefix + symbol name.
|
||||
prefixed_symbol_buf: [2048]u8 = ---
|
||||
|
||||
sym_ptr: rawptr
|
||||
for field_name, i in s.names {
|
||||
count = 0
|
||||
for field, i in reflect.struct_fields_zipped(T) {
|
||||
// Calculate address of struct member
|
||||
field_ptr := rawptr(uintptr(rawptr(symbol_table)) + uintptr(s.offsets[i]))
|
||||
field_ptr := rawptr(uintptr(symbol_table) + field.offset)
|
||||
|
||||
// If we've come across the struct member for the handle, store it and continue scanning for other symbols.
|
||||
if field_name == handle_field_name {
|
||||
if field.name == handle_field_name {
|
||||
// We appear to be hot reloading. Unload previous incarnation of the library.
|
||||
if old_handle := (^Library)(field_ptr)^; old_handle != nil {
|
||||
if ok = unload_library(old_handle); !ok {
|
||||
return count, ok
|
||||
}
|
||||
unload_library(old_handle) or_return
|
||||
}
|
||||
(^Library)(field_ptr)^ = handle
|
||||
continue
|
||||
}
|
||||
|
||||
// We're not the library handle, so the field needs to be a pointer type, be it a procedure pointer or an exported global.
|
||||
if !(reflect.is_procedure(s.types[i]) || reflect.is_pointer(s.types[i])) {
|
||||
if !(reflect.is_procedure(field.type) || reflect.is_pointer(field.type)) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -164,22 +158,21 @@ initialize_symbols :: proc(symbol_table: ^$T, library_path: string, symbol_prefi
|
||||
prefixed_name: string
|
||||
|
||||
// Do we have a symbol override tag?
|
||||
if override, tag_ok := reflect.struct_tag_lookup(reflect.Struct_Tag(s.tags[i]), "dynlib"); tag_ok {
|
||||
prefixed_name = string(override)
|
||||
if override, tag_ok := reflect.struct_tag_lookup(field.tag, "dynlib"); tag_ok {
|
||||
prefixed_name = override
|
||||
}
|
||||
|
||||
// No valid symbol override tag found, fall back to `<symbol_prefix>name`.
|
||||
if len(prefixed_name) == 0 {
|
||||
offset := copy(prefixed_symbol_buf[:], symbol_prefix)
|
||||
copy(prefixed_symbol_buf[offset:], field_name)
|
||||
prefixed_name = string(prefixed_symbol_buf[:len(symbol_prefix) + len(field_name)])
|
||||
copy(prefixed_symbol_buf[offset:], field.name)
|
||||
prefixed_name = string(prefixed_symbol_buf[:len(symbol_prefix) + len(field.name)])
|
||||
}
|
||||
|
||||
// Assign procedure (or global) pointer if found.
|
||||
if sym_ptr, ok = symbol_address(handle, prefixed_name); ok {
|
||||
(^rawptr)(field_ptr)^ = sym_ptr
|
||||
count += 1
|
||||
}
|
||||
sym_ptr := symbol_address(handle, prefixed_name) or_continue
|
||||
(^rawptr)(field_ptr)^ = sym_ptr
|
||||
count += 1
|
||||
}
|
||||
return count, count > 0
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
||||
}
|
||||
defer file.nodes = file.nodes[:node_count]
|
||||
|
||||
for node_idx in 0..<header.internal_node_count {
|
||||
for _ in 0..<header.internal_node_count {
|
||||
node := &file.nodes[node_count]
|
||||
type := read_value(r, Node_Type) or_return
|
||||
if type > max(Node_Type) {
|
||||
|
||||
+109
-24
@@ -51,6 +51,11 @@ Marshal_Options :: struct {
|
||||
// NOTE: This will temp allocate and sort a list for each map.
|
||||
sort_maps_by_key: bool,
|
||||
|
||||
// Output enum value's name instead of its underlying value.
|
||||
//
|
||||
// NOTE: If a name isn't found it'll use the underlying value.
|
||||
use_enum_names: bool,
|
||||
|
||||
// Internal state
|
||||
indentation: int,
|
||||
mjson_skipped_first_braces_start: bool,
|
||||
@@ -228,6 +233,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
case runtime.Type_Info_Matrix:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Bit_Field:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Array:
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
@@ -238,7 +246,6 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Enumerated_Array:
|
||||
index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
@@ -291,14 +298,14 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
|
||||
// check for string type
|
||||
{
|
||||
v := any{key, info.key.id}
|
||||
ti := runtime.type_info_base(type_info_of(v.id))
|
||||
a := any{v.data, ti.id}
|
||||
kv := any{key, info.key.id}
|
||||
kti := runtime.type_info_base(type_info_of(kv.id))
|
||||
ka := any{kv.data, kti.id}
|
||||
name: string
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
#partial switch info in kti.variant {
|
||||
case runtime.Type_Info_String:
|
||||
switch s in a {
|
||||
switch s in ka {
|
||||
case string: name = s
|
||||
case cstring: name = string(s)
|
||||
}
|
||||
@@ -328,13 +335,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
|
||||
// check for string type
|
||||
{
|
||||
v := any{key, info.key.id}
|
||||
ti := runtime.type_info_base(type_info_of(v.id))
|
||||
a := any{v.data, ti.id}
|
||||
kv := any{key, info.key.id}
|
||||
kti := runtime.type_info_base(type_info_of(kv.id))
|
||||
ka := any{kv.data, kti.id}
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
#partial switch info in kti.variant {
|
||||
case runtime.Type_Info_String:
|
||||
switch s in a {
|
||||
switch s in ka {
|
||||
case string: name = s
|
||||
case cstring: name = string(s)
|
||||
}
|
||||
@@ -359,24 +366,93 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
opt_write_end(w, opt, '}') or_return
|
||||
|
||||
case runtime.Type_Info_Struct:
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
|
||||
for name, i in info.names {
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
if json_name := string(reflect.struct_tag_get(auto_cast info.tags[i], "json")); json_name != "" {
|
||||
opt_write_key(w, opt, json_name) or_return
|
||||
} else {
|
||||
opt_write_key(w, opt, name) or_return
|
||||
is_omitempty :: proc(v: any) -> bool {
|
||||
v := v
|
||||
if v == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
id := info.types[i].id
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i])
|
||||
marshal_to_writer(w, any{data, id}, opt) or_return
|
||||
ti := runtime.type_info_core(type_info_of(v.id))
|
||||
#partial switch info in ti.variant {
|
||||
case runtime.Type_Info_String:
|
||||
switch x in v {
|
||||
case string:
|
||||
return x == ""
|
||||
case cstring:
|
||||
return x == nil || x == ""
|
||||
}
|
||||
case runtime.Type_Info_Any:
|
||||
return v.(any) == nil
|
||||
case runtime.Type_Info_Type_Id:
|
||||
return v.(typeid) == nil
|
||||
case runtime.Type_Info_Pointer,
|
||||
runtime.Type_Info_Multi_Pointer,
|
||||
runtime.Type_Info_Procedure:
|
||||
return (^rawptr)(v.data)^ == nil
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
return (^runtime.Raw_Dynamic_Array)(v.data).len == 0
|
||||
case runtime.Type_Info_Slice:
|
||||
return (^runtime.Raw_Slice)(v.data).len == 0
|
||||
case runtime.Type_Info_Union,
|
||||
runtime.Type_Info_Bit_Set,
|
||||
runtime.Type_Info_Soa_Pointer:
|
||||
return reflect.is_nil(v)
|
||||
case runtime.Type_Info_Map:
|
||||
return (^runtime.Raw_Map)(v.data).len == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
marshal_struct_fields :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
|
||||
ti := runtime.type_info_base(type_info_of(v.id))
|
||||
info := ti.variant.(runtime.Type_Info_Struct)
|
||||
for name, i in info.names {
|
||||
omitempty := false
|
||||
|
||||
json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json"))
|
||||
for flag in strings.split_iterator(&extra, ",") {
|
||||
switch flag {
|
||||
case "omitempty":
|
||||
omitempty = true
|
||||
}
|
||||
}
|
||||
|
||||
id := info.types[i].id
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i])
|
||||
the_value := any{data, id}
|
||||
|
||||
if is_omitempty(the_value) {
|
||||
continue
|
||||
}
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
if json_name != "" {
|
||||
opt_write_key(w, opt, json_name) or_return
|
||||
} else {
|
||||
// Marshal the fields of 'using _: T' fields directly into the parent struct
|
||||
if info.usings[i] && name == "_" {
|
||||
marshal_struct_fields(w, the_value, opt) or_return
|
||||
continue
|
||||
} else {
|
||||
opt_write_key(w, opt, name) or_return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
marshal_to_writer(w, the_value, opt) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
marshal_struct_fields(w, v, opt) or_return
|
||||
opt_write_end(w, opt, '}') or_return
|
||||
|
||||
case runtime.Type_Info_Union:
|
||||
if len(info.variants) == 0 || v.data == nil {
|
||||
io.write_string(w, "null") or_return
|
||||
return nil
|
||||
}
|
||||
|
||||
tag_ptr := uintptr(v.data) + info.tag_offset
|
||||
tag_any := any{rawptr(tag_ptr), info.tag_type.id}
|
||||
|
||||
@@ -401,7 +477,16 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Enum:
|
||||
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
|
||||
if !opt.use_enum_names || len(info.names) == 0 {
|
||||
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
|
||||
} else {
|
||||
name, found := reflect.enum_name_from_value_any(v)
|
||||
if found {
|
||||
return marshal_to_writer(w, name, opt)
|
||||
} else {
|
||||
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
|
||||
}
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||
|
||||
@@ -204,8 +204,8 @@ parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
}
|
||||
|
||||
@(private)
|
||||
bytes_make :: proc(size, alignment: int, allocator: mem.Allocator) -> (bytes: []byte, err: Error) {
|
||||
b, berr := mem.alloc_bytes(size, alignment, allocator)
|
||||
bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #caller_location) -> (bytes: []byte, err: Error) {
|
||||
b, berr := mem.alloc_bytes(size, alignment, allocator, loc)
|
||||
if berr != nil {
|
||||
if berr == .Out_Of_Memory {
|
||||
err = .Out_Of_Memory
|
||||
@@ -217,9 +217,9 @@ bytes_make :: proc(size, alignment: int, allocator: mem.Allocator) -> (bytes: []
|
||||
return
|
||||
}
|
||||
|
||||
clone_string :: proc(s: string, allocator: mem.Allocator) -> (str: string, err: Error) {
|
||||
clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_location) -> (str: string, err: Error) {
|
||||
n := len(s)
|
||||
b := bytes_make(n+1, 1, allocator) or_return
|
||||
b := bytes_make(n+1, 1, allocator, loc) or_return
|
||||
copy(b, s)
|
||||
if len(b) > n {
|
||||
b[n] = 0
|
||||
@@ -290,7 +290,7 @@ parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill): unquote_string assumes a mostly valid string
|
||||
unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator) -> (value: string, err: Error) {
|
||||
unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator, loc := #caller_location) -> (value: string, err: Error) {
|
||||
get_u2_rune :: proc(s: string) -> rune {
|
||||
if len(s) < 4 || s[0] != '\\' || s[1] != 'x' {
|
||||
return -1
|
||||
@@ -359,7 +359,7 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
|
||||
i += w
|
||||
}
|
||||
if i == len(s) {
|
||||
return clone_string(s, allocator)
|
||||
return clone_string(s, allocator, loc)
|
||||
}
|
||||
|
||||
b := bytes_make(len(s) + 2*utf8.UTF_MAX, 1, allocator) or_return
|
||||
|
||||
@@ -6,6 +6,7 @@ import "core:reflect"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
|
||||
Unmarshal_Data_Error :: enum {
|
||||
Invalid_Data,
|
||||
@@ -342,6 +343,16 @@ unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_loca
|
||||
return prev
|
||||
}
|
||||
|
||||
@(private)
|
||||
json_name_from_tag_value :: proc(value: string) -> (json_name, extra: string) {
|
||||
json_name = value
|
||||
if comma_index := strings.index_byte(json_name, ','); comma_index >= 0 {
|
||||
json_name = json_name[:comma_index]
|
||||
extra = json_name[comma_index:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
|
||||
@@ -368,16 +379,23 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
unmarshal_expect_token(p, .Colon)
|
||||
|
||||
fields := reflect.struct_fields_zipped(ti.id)
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
|
||||
|
||||
field_used := make([]bool, len(fields), context.temp_allocator)
|
||||
|
||||
field_test :: #force_inline proc "contextless" (field_used: [^]byte, offset: uintptr) -> bool {
|
||||
prev_set := field_used[offset/8] & byte(offset&7) != 0
|
||||
field_used[offset/8] |= byte(offset&7)
|
||||
return prev_set
|
||||
}
|
||||
|
||||
field_used_bytes := (reflect.size_of_typeid(ti.id)+7)/8
|
||||
field_used := intrinsics.alloca(field_used_bytes, 1)
|
||||
intrinsics.mem_zero(field_used, field_used_bytes)
|
||||
|
||||
use_field_idx := -1
|
||||
|
||||
for field, field_idx in fields {
|
||||
tag_value := string(reflect.struct_tag_get(field.tag, "json"))
|
||||
if key == tag_value {
|
||||
json_name, _ := json_name_from_tag_value(tag_value)
|
||||
if key == json_name {
|
||||
use_field_idx = field_idx
|
||||
break
|
||||
}
|
||||
@@ -392,14 +410,45 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
}
|
||||
}
|
||||
|
||||
if use_field_idx >= 0 {
|
||||
if field_used[use_field_idx] {
|
||||
check_children_using_fields :: proc(key: string, parent: typeid) -> (
|
||||
offset: uintptr,
|
||||
type: ^reflect.Type_Info,
|
||||
found: bool,
|
||||
) {
|
||||
for field in reflect.struct_fields_zipped(parent) {
|
||||
if field.is_using && field.name == "_" {
|
||||
offset, type, found = check_children_using_fields(key, field.type.id)
|
||||
if found {
|
||||
offset += field.offset
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if field.name == key {
|
||||
offset = field.offset
|
||||
type = field.type
|
||||
found = true
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
offset: uintptr
|
||||
type: ^reflect.Type_Info
|
||||
field_found: bool = use_field_idx >= 0
|
||||
|
||||
if field_found {
|
||||
offset = fields[use_field_idx].offset
|
||||
type = fields[use_field_idx].type
|
||||
} else {
|
||||
offset, type, field_found = check_children_using_fields(key, ti.id)
|
||||
}
|
||||
|
||||
if field_found {
|
||||
if field_test(field_used, offset) {
|
||||
return .Multiple_Use_Field
|
||||
}
|
||||
field_used[use_field_idx] = true
|
||||
offset := fields[use_field_idx].offset
|
||||
type := fields[use_field_idx].type
|
||||
name := fields[use_field_idx].name
|
||||
|
||||
field_ptr := rawptr(uintptr(v.data) + offset)
|
||||
field := any{field_ptr, type.id}
|
||||
@@ -411,6 +460,12 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
continue struct_loop
|
||||
} else {
|
||||
// allows skipping unused struct fields
|
||||
|
||||
// NOTE(bill): prevent possible memory leak if a string is unquoted
|
||||
allocator := p.allocator
|
||||
defer p.allocator = allocator
|
||||
p.allocator = mem.nil_allocator()
|
||||
|
||||
parse_value(p) or_return
|
||||
if parse_comma(p) {
|
||||
break struct_loop
|
||||
@@ -439,9 +494,9 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
|
||||
|
||||
mem.zero_slice(elem_backing)
|
||||
if err := unmarshal_value(p, map_backing_value); err != nil {
|
||||
if uerr := unmarshal_value(p, map_backing_value); uerr != nil {
|
||||
delete(key, p.allocator)
|
||||
return err
|
||||
return uerr
|
||||
}
|
||||
|
||||
key_ptr := rawptr(&key)
|
||||
@@ -492,7 +547,6 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
case:
|
||||
return UNSUPPORTED_TYPE
|
||||
}
|
||||
|
||||
+467
-226
@@ -147,16 +147,30 @@ aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> str
|
||||
// *Allocates Using Context's Allocator*
|
||||
//
|
||||
// Inputs:
|
||||
// - fmt: A format string with placeholders for the provided arguments.
|
||||
// - args: A variadic list of arguments to be formatted.
|
||||
// - newline: Whether the string should end with a newline. (See `aprintfln`.)
|
||||
//
|
||||
// Returns: A formatted string. The returned string must be freed accordingly.
|
||||
//
|
||||
aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newline := false) -> string {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, allocator)
|
||||
sbprintf(&str, fmt, ..args, newline=newline)
|
||||
return strings.to_string(str)
|
||||
}
|
||||
// Creates a formatted string using a format string and arguments, followed by a newline.
|
||||
//
|
||||
// *Allocates Using Context's Allocator*
|
||||
//
|
||||
// Inputs:
|
||||
// - fmt: A format string with placeholders for the provided arguments.
|
||||
// - args: A variadic list of arguments to be formatted.
|
||||
//
|
||||
// Returns: A formatted string. The returned string must be freed accordingly.
|
||||
//
|
||||
aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, allocator)
|
||||
sbprintf(&str, fmt, ..args)
|
||||
return strings.to_string(str)
|
||||
aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string {
|
||||
return aprintf(fmt, ..args, allocator=allocator, newline=true)
|
||||
}
|
||||
// Creates a formatted string
|
||||
//
|
||||
@@ -195,16 +209,30 @@ tprintln :: proc(args: ..any, sep := " ") -> string {
|
||||
// *Allocates Using Context's Temporary Allocator*
|
||||
//
|
||||
// Inputs:
|
||||
// - fmt: A format string with placeholders for the provided arguments.
|
||||
// - args: A variadic list of arguments to be formatted.
|
||||
// - newline: Whether the string should end with a newline. (See `tprintfln`.)
|
||||
//
|
||||
// Returns: A formatted string.
|
||||
//
|
||||
tprintf :: proc(fmt: string, args: ..any, newline := false) -> string {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, context.temp_allocator)
|
||||
sbprintf(&str, fmt, ..args, newline=newline)
|
||||
return strings.to_string(str)
|
||||
}
|
||||
// Creates a formatted string using a format string and arguments, followed by a newline.
|
||||
//
|
||||
// *Allocates Using Context's Temporary Allocator*
|
||||
//
|
||||
// Inputs:
|
||||
// - fmt: A format string with placeholders for the provided arguments.
|
||||
// - args: A variadic list of arguments to be formatted.
|
||||
//
|
||||
// Returns: A formatted string.
|
||||
//
|
||||
tprintf :: proc(fmt: string, args: ..any) -> string {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, context.temp_allocator)
|
||||
sbprintf(&str, fmt, ..args)
|
||||
return strings.to_string(str)
|
||||
tprintfln :: proc(fmt: string, args: ..any) -> string {
|
||||
return tprintf(fmt, ..args, newline=true)
|
||||
}
|
||||
// Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer.
|
||||
//
|
||||
@@ -238,12 +266,25 @@ bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
|
||||
// - buf: The backing buffer
|
||||
// - fmt: A format string with placeholders for the provided arguments
|
||||
// - args: A variadic list of arguments to be formatted
|
||||
// - newline: Whether the string should end with a newline. (See `bprintfln`.)
|
||||
//
|
||||
// Returns: A formatted string
|
||||
//
|
||||
bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
|
||||
bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> string {
|
||||
sb := strings.builder_from_bytes(buf)
|
||||
return sbprintf(&sb, fmt, ..args)
|
||||
return sbprintf(&sb, fmt, ..args, newline=newline)
|
||||
}
|
||||
// Creates a formatted string using a supplied buffer as the backing array, followed by a newline. Writes into the buffer.
|
||||
//
|
||||
// Inputs:
|
||||
// - buf: The backing buffer
|
||||
// - fmt: A format string with placeholders for the provided arguments
|
||||
// - args: A variadic list of arguments to be formatted
|
||||
//
|
||||
// Returns: A formatted string
|
||||
//
|
||||
bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string {
|
||||
return bprintf(buf, fmt, ..args, newline=true)
|
||||
}
|
||||
// Runtime assertion with a formatted message
|
||||
//
|
||||
@@ -294,17 +335,31 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
||||
// Inputs:
|
||||
// - format: A format string with placeholders for the provided arguments
|
||||
// - args: A variadic list of arguments to be formatted
|
||||
// - newline: Whether the string should end with a newline. (See `caprintfln`.)
|
||||
//
|
||||
// Returns: A formatted C string
|
||||
//
|
||||
caprintf :: proc(format: string, args: ..any) -> cstring {
|
||||
caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str)
|
||||
sbprintf(&str, format, ..args)
|
||||
sbprintf(&str, format, ..args, newline=newline)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
}
|
||||
// Creates a formatted C string, followed by a newline.
|
||||
//
|
||||
// *Allocates Using Context's Allocator*
|
||||
//
|
||||
// Inputs:
|
||||
// - format: A format string with placeholders for the provided arguments
|
||||
// - args: A variadic list of arguments to be formatted
|
||||
//
|
||||
// Returns: A formatted C string
|
||||
//
|
||||
caprintfln :: proc(format: string, args: ..any) -> cstring {
|
||||
return caprintf(format, ..args, newline=true)
|
||||
}
|
||||
// Creates a formatted C string
|
||||
//
|
||||
// *Allocates Using Context's Temporary Allocator*
|
||||
@@ -312,16 +367,30 @@ caprintf :: proc(format: string, args: ..any) -> cstring {
|
||||
// Inputs:
|
||||
// - format: A format string with placeholders for the provided arguments
|
||||
// - args: A variadic list of arguments to be formatted
|
||||
// - newline: Whether the string should end with a newline. (See `ctprintfln`.)
|
||||
//
|
||||
// Returns: A formatted C string
|
||||
//
|
||||
ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, context.temp_allocator)
|
||||
sbprintf(&str, format, ..args, newline=newline)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
}
|
||||
// Creates a formatted C string, followed by a newline.
|
||||
//
|
||||
// *Allocates Using Context's Temporary Allocator*
|
||||
//
|
||||
// Inputs:
|
||||
// - format: A format string with placeholders for the provided arguments
|
||||
// - args: A variadic list of arguments to be formatted
|
||||
//
|
||||
// Returns: A formatted C string
|
||||
//
|
||||
ctprintf :: proc(format: string, args: ..any) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, context.temp_allocator)
|
||||
sbprintf(&str, format, ..args)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
ctprintfln :: proc(format: string, args: ..any) -> cstring {
|
||||
return ctprintf(format, ..args, newline=true)
|
||||
}
|
||||
// Formats using the default print settings and writes to the given strings.Builder
|
||||
//
|
||||
@@ -355,13 +424,25 @@ sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
|
||||
// - buf: A pointer to a strings.Builder buffer
|
||||
// - fmt: The format string
|
||||
// - args: A variadic list of arguments to be formatted
|
||||
// - newline: Whether a trailing newline should be written. (See `sbprintfln`.)
|
||||
//
|
||||
// Returns: The resulting formatted string
|
||||
//
|
||||
sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string {
|
||||
wprintf(strings.to_writer(buf), fmt, ..args, flush=true)
|
||||
sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := false) -> string {
|
||||
wprintf(strings.to_writer(buf), fmt, ..args, flush=true, newline=newline)
|
||||
return strings.to_string(buf^)
|
||||
}
|
||||
// Formats and writes to a strings.Builder buffer according to the specified format string, followed by a newline.
|
||||
//
|
||||
// Inputs:
|
||||
// - buf: A pointer to a strings.Builder to store the formatted string
|
||||
// - args: A variadic list of arguments to be formatted
|
||||
//
|
||||
// Returns: A formatted string
|
||||
//
|
||||
sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string {
|
||||
return sbprintf(buf, format, ..args, newline=true)
|
||||
}
|
||||
// Formats and writes to an io.Writer using the default print settings
|
||||
//
|
||||
// Inputs:
|
||||
@@ -435,10 +516,11 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int {
|
||||
// - w: An io.Writer to write to
|
||||
// - fmt: The format string
|
||||
// - args: A variadic list of arguments to be formatted
|
||||
// - newline: Whether a trailing newline should be written. (See `wprintfln`.)
|
||||
//
|
||||
// Returns: The number of bytes written
|
||||
//
|
||||
wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true) -> int {
|
||||
wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline := false) -> int {
|
||||
fi: Info
|
||||
arg_index: int = 0
|
||||
end := len(fmt)
|
||||
@@ -708,12 +790,27 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true) -> int {
|
||||
}
|
||||
io.write_string(fi.writer, ")", &fi.n)
|
||||
}
|
||||
|
||||
if newline {
|
||||
io.write_byte(w, '\n', &fi.n)
|
||||
}
|
||||
if flush {
|
||||
io.flush(w)
|
||||
}
|
||||
|
||||
return fi.n
|
||||
}
|
||||
// Formats and writes to an io.Writer according to the specified format string, followed by a newline.
|
||||
//
|
||||
// Inputs:
|
||||
// - w: The io.Writer to write to.
|
||||
// - args: A variadic list of arguments to be formatted.
|
||||
//
|
||||
// Returns: The number of bytes written.
|
||||
//
|
||||
wprintfln :: proc(w: io.Writer, format: string, args: ..any, flush := true) -> int {
|
||||
return wprintf(w, format, ..args, flush=flush, newline=true)
|
||||
}
|
||||
// Writes a ^runtime.Type_Info value to an io.Writer
|
||||
//
|
||||
// Inputs:
|
||||
@@ -875,7 +972,7 @@ fmt_bad_verb :: proc(fi: ^Info, verb: rune) {
|
||||
//
|
||||
fmt_bool :: proc(fi: ^Info, b: bool, verb: rune) {
|
||||
switch verb {
|
||||
case 't', 'v':
|
||||
case 't', 'v', 'w':
|
||||
fmt_string(fi, b ? "true" : "false", 's')
|
||||
case:
|
||||
fmt_bad_verb(fi, verb)
|
||||
@@ -1112,7 +1209,7 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
|
||||
switch verb {
|
||||
case 'c', 'r', 'v':
|
||||
io.write_rune(fi.writer, r, &fi.n)
|
||||
case 'q':
|
||||
case 'q', 'w':
|
||||
fi.n += io.write_quoted_rune(fi.writer, r)
|
||||
case:
|
||||
fmt_int(fi, u64(r), false, 32, verb)
|
||||
@@ -1129,7 +1226,8 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
|
||||
//
|
||||
fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
|
||||
switch verb {
|
||||
case 'v': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
|
||||
case 'v', 'w':
|
||||
_fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
|
||||
case 'b': _fmt_int(fi, u, 2, is_signed, bit_size, __DIGITS_LOWER)
|
||||
case 'o': _fmt_int(fi, u, 8, is_signed, bit_size, __DIGITS_LOWER)
|
||||
case 'i', 'd': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
|
||||
@@ -1164,7 +1262,8 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
|
||||
//
|
||||
fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: rune) {
|
||||
switch verb {
|
||||
case 'v': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
|
||||
case 'v', 'w':
|
||||
_fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
|
||||
case 'b': _fmt_int_128(fi, u, 2, is_signed, bit_size, __DIGITS_LOWER)
|
||||
case 'o': _fmt_int_128(fi, u, 8, is_signed, bit_size, __DIGITS_LOWER)
|
||||
case 'i', 'd': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
|
||||
@@ -1254,7 +1353,7 @@ _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: b
|
||||
//
|
||||
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
|
||||
switch verb {
|
||||
case 'g', 'G', 'v':
|
||||
case 'g', 'G', 'v', 'w':
|
||||
_fmt_float_as(fi, v, bit_size, verb, 'g', -1)
|
||||
case 'f', 'F':
|
||||
_fmt_float_as(fi, v, bit_size, verb, 'f', 3)
|
||||
@@ -1327,7 +1426,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
|
||||
io.write_string(fi.writer, s, &fi.n)
|
||||
}
|
||||
|
||||
case 'q': // quoted string
|
||||
case 'q', 'w': // quoted string
|
||||
io.write_quoted_string(fi.writer, s, '"', &fi.n)
|
||||
|
||||
case 'x', 'X':
|
||||
@@ -1370,7 +1469,7 @@ fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) {
|
||||
fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
|
||||
u := u64(uintptr(p))
|
||||
switch verb {
|
||||
case 'p', 'v':
|
||||
case 'p', 'v', 'w':
|
||||
if !fi.hash && verb == 'v' {
|
||||
io.write_string(fi.writer, "0x", &fi.n)
|
||||
}
|
||||
@@ -1408,34 +1507,9 @@ fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) {
|
||||
//
|
||||
// Returns: The string representation of the enum value and a boolean indicating success.
|
||||
//
|
||||
@(require_results)
|
||||
enum_value_to_string :: proc(val: any) -> (string, bool) {
|
||||
v := val
|
||||
v.id = runtime.typeid_base(v.id)
|
||||
type_info := type_info_of(v.id)
|
||||
|
||||
#partial switch e in type_info.variant {
|
||||
case: return "", false
|
||||
case runtime.Type_Info_Enum:
|
||||
Enum_Value :: runtime.Type_Info_Enum_Value
|
||||
|
||||
ev_, ok := reflect.as_i64(val)
|
||||
ev := Enum_Value(ev_)
|
||||
|
||||
if ok {
|
||||
if len(e.values) == 0 {
|
||||
return "", true
|
||||
} else {
|
||||
for val, idx in e.values {
|
||||
if val == ev {
|
||||
return e.names[idx], true
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
return reflect.enum_name_from_value_any(val)
|
||||
}
|
||||
// Returns the enum value of a string representation.
|
||||
//
|
||||
@@ -1463,7 +1537,7 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) {
|
||||
// Inputs:
|
||||
// - fi: Pointer to the Info struct containing format settings.
|
||||
// - v: The enum value to format.
|
||||
// - verb: The format specifier character (e.g. 'i','d','f','s','v','q').
|
||||
// - verb: The format specifier character (e.g. 'i','d','f','s','v','q','w').
|
||||
//
|
||||
fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
if v.id == nil || v.data == nil {
|
||||
@@ -1487,6 +1561,15 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, 'i')
|
||||
io.write_string(fi.writer, ")", &fi.n)
|
||||
}
|
||||
case 'w':
|
||||
if str, ok := enum_value_to_string(v); ok {
|
||||
io.write_byte(fi.writer, '.', &fi.n)
|
||||
io.write_string(fi.writer, str, &fi.n)
|
||||
} else {
|
||||
io.write_string(fi.writer, "%!(BAD ENUM VALUE=", &fi.n)
|
||||
fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, 'i')
|
||||
io.write_string(fi.writer, ")", &fi.n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1675,8 +1758,8 @@ fmt_write_indent :: proc(fi: ^Info) {
|
||||
// - verb: The formatting verb to be used for the array elements.
|
||||
//
|
||||
fmt_write_array :: proc(fi: ^Info, array_data: rawptr, count: int, elem_size: int, elem_id: typeid, verb: rune) {
|
||||
io.write_byte(fi.writer, '[', &fi.n)
|
||||
defer io.write_byte(fi.writer, ']', &fi.n)
|
||||
io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n)
|
||||
defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
|
||||
|
||||
if count <= 0 {
|
||||
return
|
||||
@@ -1749,6 +1832,13 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb:
|
||||
r, w := utf8.decode_rune_in_string(value)
|
||||
value = value[w:]
|
||||
if value == "" || value[0] == ',' {
|
||||
if verb^ == 'w' {
|
||||
// TODO(bill): is this a good idea overriding that field tags if 'w' is used?
|
||||
switch r {
|
||||
case 's': r = 'q'
|
||||
case: r = 'w'
|
||||
}
|
||||
}
|
||||
verb^ = r
|
||||
if len(value) > 0 && value[0] == ',' {
|
||||
field_name := value[1:]
|
||||
@@ -1760,7 +1850,7 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb:
|
||||
switch r {
|
||||
case 's', 'q':
|
||||
handle_optional_len(data, info, field_name, optional_len)
|
||||
case 'v':
|
||||
case 'v', 'w':
|
||||
#partial switch reflect.type_kind(info.types[idx].id) {
|
||||
case .String, .Multi_Pointer, .Array, .Slice, .Dynamic_Array:
|
||||
handle_optional_len(data, info, field_name, optional_len)
|
||||
@@ -1782,7 +1872,7 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb:
|
||||
// - type_name: The name of the type being formatted
|
||||
//
|
||||
fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_Struct, type_name: string) {
|
||||
if the_verb != 'v' {
|
||||
if the_verb != 'v' && the_verb != 'w' {
|
||||
fmt_bad_verb(fi, the_verb)
|
||||
return
|
||||
}
|
||||
@@ -1799,7 +1889,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
||||
is_soa := info.soa_kind != .None
|
||||
|
||||
io.write_string(fi.writer, type_name, &fi.n)
|
||||
io.write_byte(fi.writer, '[' if is_soa else '{', &fi.n)
|
||||
io.write_byte(fi.writer, '[' if is_soa && the_verb == 'v' else '{', &fi.n)
|
||||
fi.record_level += 1
|
||||
defer fi.record_level -= 1
|
||||
|
||||
@@ -1817,7 +1907,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
||||
if hash {
|
||||
for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
|
||||
}
|
||||
io.write_byte(fi.writer, ']' if is_soa else '}', &fi.n)
|
||||
io.write_byte(fi.writer, ']' if is_soa && the_verb == 'v' else '}', &fi.n)
|
||||
}
|
||||
|
||||
if is_soa {
|
||||
@@ -1900,7 +1990,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
|
||||
for name, i in info.names {
|
||||
optional_len: int = -1
|
||||
use_nul_termination: bool = false
|
||||
verb := 'v'
|
||||
verb := the_verb if the_verb == 'w' else 'v'
|
||||
if handle_tag(v.data, info, i, &verb, &optional_len, &use_nul_termination) {
|
||||
continue
|
||||
}
|
||||
@@ -1979,7 +2069,7 @@ fmt_array_nul_terminated :: proc(fi: ^Info, data: rawptr, max_n: int, elem_size:
|
||||
// - n: The number of elements in the array.
|
||||
// - elem_size: The size of each element in the array.
|
||||
// - elem: Pointer to the type information of the array element.
|
||||
// - verb: The formatting verb (e.g. 's','q','p').
|
||||
// - verb: The formatting verb (e.g. 's','q','p','w').
|
||||
//
|
||||
fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) {
|
||||
if data == nil && n > 0 {
|
||||
@@ -2058,149 +2148,170 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named)
|
||||
}
|
||||
|
||||
// Built-in Custom Formatters for core library types
|
||||
switch a in v {
|
||||
case runtime.Source_Code_Location:
|
||||
io.write_string(fi.writer, a.file_path, &fi.n)
|
||||
if verb != 'w' {
|
||||
switch a in v {
|
||||
case runtime.Source_Code_Location:
|
||||
io.write_string(fi.writer, a.file_path, &fi.n)
|
||||
|
||||
when ODIN_ERROR_POS_STYLE == .Default {
|
||||
io.write_byte(fi.writer, '(', &fi.n)
|
||||
io.write_int(fi.writer, int(a.line), 10, &fi.n)
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
io.write_int(fi.writer, int(a.column), 10, &fi.n)
|
||||
io.write_byte(fi.writer, ')', &fi.n)
|
||||
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
io.write_int(fi.writer, int(a.line), 10, &fi.n)
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
io.write_int(fi.writer, int(a.column), 10, &fi.n)
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
} else {
|
||||
#panic("Unhandled ODIN_ERROR_POS_STYLE")
|
||||
}
|
||||
return
|
||||
|
||||
case time.Duration:
|
||||
ffrac :: proc(buf: []byte, v: u64, prec: int) -> (nw: int, nv: u64) {
|
||||
v := v
|
||||
w := len(buf)
|
||||
print := false
|
||||
for _ in 0..<prec {
|
||||
digit := v % 10
|
||||
print = print || digit != 0
|
||||
if print {
|
||||
w -= 1
|
||||
buf[w] = byte(digit) + '0'
|
||||
}
|
||||
v /= 10
|
||||
}
|
||||
if print {
|
||||
w -= 1
|
||||
buf[w] = '.'
|
||||
}
|
||||
return w, v
|
||||
}
|
||||
fint :: proc(buf: []byte, v: u64) -> int {
|
||||
v := v
|
||||
w := len(buf)
|
||||
if v == 0 {
|
||||
w -= 1
|
||||
buf[w] = '0'
|
||||
when ODIN_ERROR_POS_STYLE == .Default {
|
||||
io.write_byte(fi.writer, '(', &fi.n)
|
||||
io.write_int(fi.writer, int(a.line), 10, &fi.n)
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
io.write_int(fi.writer, int(a.column), 10, &fi.n)
|
||||
io.write_byte(fi.writer, ')', &fi.n)
|
||||
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
io.write_int(fi.writer, int(a.line), 10, &fi.n)
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
io.write_int(fi.writer, int(a.column), 10, &fi.n)
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
} else {
|
||||
for v > 0 {
|
||||
w -= 1
|
||||
buf[w] = byte(v%10) + '0'
|
||||
#panic("Unhandled ODIN_ERROR_POS_STYLE")
|
||||
}
|
||||
return
|
||||
|
||||
case time.Duration:
|
||||
ffrac :: proc(buf: []byte, v: u64, prec: int) -> (nw: int, nv: u64) {
|
||||
v := v
|
||||
w := len(buf)
|
||||
print := false
|
||||
for _ in 0..<prec {
|
||||
digit := v % 10
|
||||
print = print || digit != 0
|
||||
if print {
|
||||
w -= 1
|
||||
buf[w] = byte(digit) + '0'
|
||||
}
|
||||
v /= 10
|
||||
}
|
||||
if print {
|
||||
w -= 1
|
||||
buf[w] = '.'
|
||||
}
|
||||
return w, v
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
buf: [32]byte
|
||||
w := len(buf)
|
||||
u := u64(a)
|
||||
neg := a < 0
|
||||
if neg {
|
||||
u = -u
|
||||
}
|
||||
|
||||
if u < u64(time.Second) {
|
||||
prec: int
|
||||
w -= 1
|
||||
buf[w] = 's'
|
||||
w -= 1
|
||||
switch {
|
||||
case u == 0:
|
||||
io.write_string(fi.writer, "0s", &fi.n)
|
||||
return
|
||||
case u < u64(time.Microsecond):
|
||||
prec = 0
|
||||
buf[w] = 'n'
|
||||
case u < u64(time.Millisecond):
|
||||
prec = 3
|
||||
// U+00B5 'µ' micro sign == 0xC2 0xB5
|
||||
w -= 1 // Need room for two bytes
|
||||
copy(buf[w:], "µ")
|
||||
case:
|
||||
prec = 6
|
||||
buf[w] = 'm'
|
||||
fint :: proc(buf: []byte, v: u64) -> int {
|
||||
v := v
|
||||
w := len(buf)
|
||||
if v == 0 {
|
||||
w -= 1
|
||||
buf[w] = '0'
|
||||
} else {
|
||||
for v > 0 {
|
||||
w -= 1
|
||||
buf[w] = byte(v%10) + '0'
|
||||
v /= 10
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
w, u = ffrac(buf[:w], u, prec)
|
||||
w = fint(buf[:w], u)
|
||||
} else {
|
||||
w -= 1
|
||||
buf[w] = 's'
|
||||
w, u = ffrac(buf[:w], u, 9)
|
||||
w = fint(buf[:w], u%60)
|
||||
u /= 60
|
||||
if u > 0 {
|
||||
|
||||
buf: [32]byte
|
||||
w := len(buf)
|
||||
u := u64(a)
|
||||
neg := a < 0
|
||||
if neg {
|
||||
u = -u
|
||||
}
|
||||
|
||||
if u < u64(time.Second) {
|
||||
prec: int
|
||||
w -= 1
|
||||
buf[w] = 'm'
|
||||
buf[w] = 's'
|
||||
w -= 1
|
||||
switch {
|
||||
case u == 0:
|
||||
io.write_string(fi.writer, "0s", &fi.n)
|
||||
return
|
||||
case u < u64(time.Microsecond):
|
||||
prec = 0
|
||||
buf[w] = 'n'
|
||||
case u < u64(time.Millisecond):
|
||||
prec = 3
|
||||
// U+00B5 'µ' micro sign == 0xC2 0xB5
|
||||
w -= 1 // Need room for two bytes
|
||||
copy(buf[w:], "µ")
|
||||
case:
|
||||
prec = 6
|
||||
buf[w] = 'm'
|
||||
}
|
||||
w, u = ffrac(buf[:w], u, prec)
|
||||
w = fint(buf[:w], u)
|
||||
} else {
|
||||
w -= 1
|
||||
buf[w] = 's'
|
||||
w, u = ffrac(buf[:w], u, 9)
|
||||
w = fint(buf[:w], u%60)
|
||||
u /= 60
|
||||
if u > 0 {
|
||||
w -= 1
|
||||
buf[w] = 'h'
|
||||
w = fint(buf[:w], u)
|
||||
buf[w] = 'm'
|
||||
w = fint(buf[:w], u%60)
|
||||
u /= 60
|
||||
if u > 0 {
|
||||
w -= 1
|
||||
buf[w] = 'h'
|
||||
w = fint(buf[:w], u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if neg {
|
||||
w -= 1
|
||||
buf[w] = '-'
|
||||
}
|
||||
io.write_string(fi.writer, string(buf[w:]), &fi.n)
|
||||
return
|
||||
|
||||
case time.Time:
|
||||
t := a
|
||||
y, mon, d := time.date(t)
|
||||
h, min, s := time.clock(t)
|
||||
ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9
|
||||
write_padded_number(fi, i64(y), 4)
|
||||
io.write_byte(fi.writer, '-', &fi.n)
|
||||
write_padded_number(fi, i64(mon), 2)
|
||||
io.write_byte(fi.writer, '-', &fi.n)
|
||||
write_padded_number(fi, i64(d), 2)
|
||||
io.write_byte(fi.writer, ' ', &fi.n)
|
||||
|
||||
write_padded_number(fi, i64(h), 2)
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
write_padded_number(fi, i64(min), 2)
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
write_padded_number(fi, i64(s), 2)
|
||||
io.write_byte(fi.writer, '.', &fi.n)
|
||||
write_padded_number(fi, (ns), 9)
|
||||
io.write_string(fi.writer, " +0000 UTC", &fi.n)
|
||||
return
|
||||
}
|
||||
|
||||
if neg {
|
||||
w -= 1
|
||||
buf[w] = '-'
|
||||
}
|
||||
io.write_string(fi.writer, string(buf[w:]), &fi.n)
|
||||
return
|
||||
|
||||
case time.Time:
|
||||
t := a
|
||||
y, mon, d := time.date(t)
|
||||
h, min, s := time.clock(t)
|
||||
ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9
|
||||
write_padded_number(fi, i64(y), 4)
|
||||
io.write_byte(fi.writer, '-', &fi.n)
|
||||
write_padded_number(fi, i64(mon), 2)
|
||||
io.write_byte(fi.writer, '-', &fi.n)
|
||||
write_padded_number(fi, i64(d), 2)
|
||||
io.write_byte(fi.writer, ' ', &fi.n)
|
||||
|
||||
write_padded_number(fi, i64(h), 2)
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
write_padded_number(fi, i64(min), 2)
|
||||
io.write_byte(fi.writer, ':', &fi.n)
|
||||
write_padded_number(fi, i64(s), 2)
|
||||
io.write_byte(fi.writer, '.', &fi.n)
|
||||
write_padded_number(fi, (ns), 9)
|
||||
io.write_string(fi.writer, " +0000 UTC", &fi.n)
|
||||
return
|
||||
}
|
||||
|
||||
#partial switch b in info.base.variant {
|
||||
case runtime.Type_Info_Struct:
|
||||
fmt_struct(fi, v, verb, b, info.name)
|
||||
case runtime.Type_Info_Bit_Field:
|
||||
fmt_bit_field(fi, v, verb, b, info.name)
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
fmt_bit_set(fi, v, verb = verb)
|
||||
case:
|
||||
if verb == 'w' {
|
||||
#partial switch _ in info.base.variant {
|
||||
case runtime.Type_Info_Array,
|
||||
runtime.Type_Info_Enumerated_Array,
|
||||
runtime.Type_Info_Dynamic_Array,
|
||||
runtime.Type_Info_Slice,
|
||||
runtime.Type_Info_Struct,
|
||||
runtime.Type_Info_Union,
|
||||
runtime.Type_Info_Enum,
|
||||
runtime.Type_Info_Map,
|
||||
runtime.Type_Info_Bit_Set,
|
||||
runtime.Type_Info_Simd_Vector,
|
||||
runtime.Type_Info_Matrix,
|
||||
runtime.Type_Info_Bit_Field:
|
||||
io.write_string(fi.writer, info.name, &fi.n)
|
||||
}
|
||||
}
|
||||
fmt_value(fi, any{v.data, info.base.id}, verb)
|
||||
}
|
||||
}
|
||||
@@ -2267,8 +2378,13 @@ fmt_union :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Union,
|
||||
// - info: A runtime.Type_Info_Matrix struct containing matrix type information.
|
||||
//
|
||||
fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix) {
|
||||
io.write_string(fi.writer, "matrix[", &fi.n)
|
||||
defer io.write_byte(fi.writer, ']', &fi.n)
|
||||
if verb == 'w' {
|
||||
io.write_byte(fi.writer, '{', &fi.n)
|
||||
} else {
|
||||
io.write_string(fi.writer, "matrix", &fi.n)
|
||||
io.write_byte(fi.writer, '[', &fi.n)
|
||||
}
|
||||
defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
|
||||
|
||||
fi.indent += 1
|
||||
|
||||
@@ -2280,7 +2396,11 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix
|
||||
for col in 0..<info.column_count {
|
||||
if col > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
||||
|
||||
offset := (row + col*info.elem_stride)*info.elem_size
|
||||
offset: int
|
||||
switch info.layout {
|
||||
case .Column_Major: offset = (row + col*info.elem_stride)*info.elem_size
|
||||
case .Row_Major: offset = (col + row*info.elem_stride)*info.elem_size
|
||||
}
|
||||
|
||||
data := uintptr(v.data) + uintptr(offset)
|
||||
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
|
||||
@@ -2294,7 +2414,11 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix
|
||||
for col in 0..<info.column_count {
|
||||
if col > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
||||
|
||||
offset := (row + col*info.elem_stride)*info.elem_size
|
||||
offset: int
|
||||
switch info.layout {
|
||||
case .Column_Major: offset = (row + col*info.elem_stride)*info.elem_size
|
||||
case .Row_Major: offset = (col + row*info.elem_stride)*info.elem_size
|
||||
}
|
||||
|
||||
data := uintptr(v.data) + uintptr(offset)
|
||||
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
|
||||
@@ -2308,6 +2432,96 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix
|
||||
fmt_write_indent(fi)
|
||||
}
|
||||
}
|
||||
|
||||
fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit_Field, type_name: string) {
|
||||
read_bits :: proc(ptr: [^]byte, offset, size: uintptr) -> (res: u64) {
|
||||
for i in 0..<size {
|
||||
j := i+offset
|
||||
B := ptr[j/8]
|
||||
k := j&7
|
||||
if B & (u8(1)<<k) != 0 {
|
||||
res |= u64(1)<<u64(i)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
handle_bit_field_tag :: proc(data: rawptr, info: reflect.Type_Info_Bit_Field, idx: int, verb: ^rune) -> (do_continue: bool) {
|
||||
tag := info.tags[idx]
|
||||
if vt, ok := reflect.struct_tag_lookup(reflect.Struct_Tag(tag), "fmt"); ok {
|
||||
value := strings.trim_space(string(vt))
|
||||
switch value {
|
||||
case "": return false
|
||||
case "-": return true
|
||||
}
|
||||
r, w := utf8.decode_rune_in_string(value)
|
||||
value = value[w:]
|
||||
if value == "" || value[0] == ',' {
|
||||
verb^ = r
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
io.write_string(fi.writer, type_name if len(type_name) != 0 || verb == 'w' else "bit_field", &fi.n)
|
||||
io.write_byte(fi.writer, '{', &fi.n)
|
||||
|
||||
hash := fi.hash; defer fi.hash = hash
|
||||
indent := fi.indent; defer fi.indent -= 1
|
||||
do_trailing_comma := hash
|
||||
|
||||
fi.indent += 1
|
||||
|
||||
if hash {
|
||||
io.write_byte(fi.writer, '\n', &fi.n)
|
||||
}
|
||||
defer {
|
||||
if hash {
|
||||
for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
|
||||
}
|
||||
io.write_byte(fi.writer, '}', &fi.n)
|
||||
}
|
||||
|
||||
|
||||
field_count := -1
|
||||
for name, i in info.names {
|
||||
field_verb := verb
|
||||
if handle_bit_field_tag(v.data, info, i, &field_verb) {
|
||||
continue
|
||||
}
|
||||
|
||||
field_count += 1
|
||||
|
||||
if !do_trailing_comma && field_count > 0 {
|
||||
io.write_string(fi.writer, ", ")
|
||||
}
|
||||
if hash {
|
||||
fmt_write_indent(fi)
|
||||
}
|
||||
|
||||
io.write_string(fi.writer, name, &fi.n)
|
||||
io.write_string(fi.writer, " = ", &fi.n)
|
||||
|
||||
bit_offset := info.bit_offsets[i]
|
||||
bit_size := info.bit_sizes[i]
|
||||
|
||||
value := read_bits(([^]byte)(v.data), bit_offset, bit_size)
|
||||
type := info.types[i]
|
||||
|
||||
if !reflect.is_unsigned(runtime.type_info_core(type)) {
|
||||
// Sign Extension
|
||||
m := u64(1<<(bit_size-1))
|
||||
value = (value ~ m) - m
|
||||
}
|
||||
|
||||
fmt_value(fi, any{&value, type.id}, field_verb)
|
||||
if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Formats a value based on its type and formatting verb
|
||||
//
|
||||
// Inputs:
|
||||
@@ -2467,11 +2681,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
defer fi.record_level -= 1
|
||||
|
||||
if fi.hash {
|
||||
io.write_string(fi.writer, "[\n", &fi.n)
|
||||
io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n)
|
||||
io.write_byte(fi.writer, '\n', &fi.n)
|
||||
defer {
|
||||
io.write_byte(fi.writer, '\n', &fi.n)
|
||||
fmt_write_indent(fi)
|
||||
io.write_byte(fi.writer, ']', &fi.n)
|
||||
io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
|
||||
}
|
||||
indent := fi.indent
|
||||
fi.indent += 1
|
||||
@@ -2495,8 +2710,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
io.write_string(fi.writer, ",\n", &fi.n)
|
||||
}
|
||||
} else {
|
||||
io.write_byte(fi.writer, '[', &fi.n)
|
||||
defer io.write_byte(fi.writer, ']', &fi.n)
|
||||
io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n)
|
||||
defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
|
||||
for i in 0..<info.count {
|
||||
if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
||||
|
||||
@@ -2561,38 +2776,61 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
|
||||
|
||||
case runtime.Type_Info_Map:
|
||||
if verb != 'v' {
|
||||
switch verb {
|
||||
case:
|
||||
fmt_bad_verb(fi, verb)
|
||||
return
|
||||
}
|
||||
|
||||
io.write_string(fi.writer, "map[", &fi.n)
|
||||
defer io.write_byte(fi.writer, ']', &fi.n)
|
||||
fi.record_level += 1
|
||||
defer fi.record_level -= 1
|
||||
|
||||
m := (^mem.Raw_Map)(v.data)
|
||||
if m != nil {
|
||||
if info.map_info == nil {
|
||||
return
|
||||
case 'v', 'w':
|
||||
if verb == 'v' {
|
||||
io.write_string(fi.writer, "map", &fi.n)
|
||||
}
|
||||
map_cap := uintptr(runtime.map_cap(m^))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||
j := 0
|
||||
for bucket_index in 0..<map_cap {
|
||||
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
||||
io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n)
|
||||
defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
|
||||
|
||||
if j > 0 {
|
||||
io.write_string(fi.writer, ", ", &fi.n)
|
||||
|
||||
hash := fi.hash; defer fi.hash = hash
|
||||
indent := fi.indent; defer fi.indent -= 1
|
||||
do_trailing_comma := hash
|
||||
|
||||
fi.indent += 1
|
||||
if hash {
|
||||
io.write_byte(fi.writer, '\n', &fi.n)
|
||||
}
|
||||
defer {
|
||||
if hash {
|
||||
for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
|
||||
}
|
||||
j += 1
|
||||
}
|
||||
|
||||
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
|
||||
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
|
||||
m := (^mem.Raw_Map)(v.data)
|
||||
if m != nil {
|
||||
if info.map_info == nil {
|
||||
return
|
||||
}
|
||||
map_cap := uintptr(runtime.map_cap(m^))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||
j := 0
|
||||
for bucket_index in 0..<map_cap {
|
||||
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
||||
|
||||
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
|
||||
io.write_string(fi.writer, "=", &fi.n)
|
||||
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
|
||||
if !do_trailing_comma && j > 0 { io.write_string(fi.writer, ", ") }
|
||||
if hash {
|
||||
fmt_write_indent(fi)
|
||||
}
|
||||
j += 1
|
||||
|
||||
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
|
||||
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
|
||||
|
||||
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, verb)
|
||||
if hash {
|
||||
io.write_string(fi.writer, " = ", &fi.n)
|
||||
} else {
|
||||
io.write_string(fi.writer, "=", &fi.n)
|
||||
}
|
||||
fmt_arg(fi, any{rawptr(value), info.value.id}, verb)
|
||||
|
||||
if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2636,6 +2874,9 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
|
||||
case runtime.Type_Info_Matrix:
|
||||
fmt_matrix(fi, v, verb, info)
|
||||
|
||||
case runtime.Type_Info_Bit_Field:
|
||||
fmt_bit_field(fi, v, verb, info, "")
|
||||
}
|
||||
}
|
||||
// Formats a complex number based on the given formatting verb
|
||||
@@ -2644,11 +2885,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
// - fi: A pointer to an Info struct containing formatting information.
|
||||
// - c: The complex128 value to be formatted.
|
||||
// - bits: The number of bits in the complex number (32 or 64).
|
||||
// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H').
|
||||
// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H', 'w').
|
||||
//
|
||||
fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
|
||||
switch verb {
|
||||
case 'f', 'F', 'v', 'h', 'H':
|
||||
case 'f', 'F', 'v', 'h', 'H', 'w':
|
||||
r, i := real(c), imag(c)
|
||||
fmt_float(fi, r, bits/2, verb)
|
||||
if !fi.plus && i >= 0 {
|
||||
@@ -2668,11 +2909,11 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
|
||||
// - fi: A pointer to an Info struct containing formatting information.
|
||||
// - q: The quaternion256 value to be formatted.
|
||||
// - bits: The number of bits in the quaternion number (64, 128, or 256).
|
||||
// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H').
|
||||
// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H', 'w').
|
||||
//
|
||||
fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) {
|
||||
switch verb {
|
||||
case 'f', 'F', 'v', 'h', 'H':
|
||||
case 'f', 'F', 'v', 'h', 'H', 'w':
|
||||
r, i, j, k := real(q), imag(q), jmag(q), kmag(q)
|
||||
|
||||
fmt_float(fi, r, bits/4, verb)
|
||||
|
||||
+16
-8
@@ -30,7 +30,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int {
|
||||
return wprintln(w, ..args, sep=sep, flush=flush)
|
||||
}
|
||||
// fprintf formats according to the specified format string and writes to fd
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false) -> int {
|
||||
buf: [1024]byte
|
||||
b: bufio.Writer
|
||||
defer bufio.writer_flush(&b)
|
||||
@@ -38,7 +38,11 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
|
||||
bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
|
||||
|
||||
w := bufio.writer_to_writer(&b)
|
||||
return wprintf(w, fmt, ..args, flush=flush)
|
||||
return wprintf(w, fmt, ..args, flush=flush, newline=newline)
|
||||
}
|
||||
// fprintfln formats according to the specified format string and writes to fd, followed by a newline.
|
||||
fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
|
||||
return fprintf(fd, fmt, ..args, flush=flush, newline=true)
|
||||
}
|
||||
fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) {
|
||||
buf: [1024]byte
|
||||
@@ -62,15 +66,19 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid, flush := true) -> (n: int, err:
|
||||
}
|
||||
|
||||
// print formats using the default print settings and writes to os.stdout
|
||||
print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) }
|
||||
print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) }
|
||||
// println formats using the default print settings and writes to os.stdout
|
||||
println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) }
|
||||
println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) }
|
||||
// printf formats according to the specified format string and writes to os.stdout
|
||||
printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) }
|
||||
printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) }
|
||||
// printfln formats according to the specified format string and writes to os.stdout, followed by a newline.
|
||||
printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) }
|
||||
|
||||
// eprint formats using the default print settings and writes to os.stderr
|
||||
eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) }
|
||||
eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) }
|
||||
// eprintln formats using the default print settings and writes to os.stderr
|
||||
eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) }
|
||||
eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) }
|
||||
// eprintf formats according to the specified format string and writes to os.stderr
|
||||
eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) }
|
||||
eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) }
|
||||
// eprintfln formats according to the specified format string and writes to os.stderr, followed by a newline.
|
||||
eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) }
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
package hash
|
||||
|
||||
@(optimization_mode="speed")
|
||||
crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
||||
crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
||||
result = seed
|
||||
#no_bounds_check for b in data {
|
||||
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
|
||||
@@ -15,7 +15,7 @@ crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds
|
||||
Based on Mark Adler's v1.4 implementation in C under the ZLIB license.
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
crc64_xz :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
data := data
|
||||
result := ~u64le(seed)
|
||||
|
||||
@@ -53,7 +53,7 @@ crc64_xz :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
Generator polynomial: x^64 + x^4 + x^3 + x + 1
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
crc64_iso_3306 :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
|
||||
result := seed
|
||||
|
||||
@@ -70,7 +70,7 @@ crc64_iso_3306 :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
|
||||
return result
|
||||
}
|
||||
|
||||
crc64_iso_3306_inverse :: proc(data: []byte, seed := u64(0)) -> u64 {
|
||||
crc64_iso_3306_inverse :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 {
|
||||
result := #force_inline crc64_iso_3306(data, ~seed)
|
||||
return ~result
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package hash
|
||||
import "base:intrinsics"
|
||||
|
||||
@(optimization_mode="speed")
|
||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
crc := ~seed
|
||||
buffer := raw_data(data)
|
||||
length := len(data)
|
||||
@@ -323,7 +323,7 @@ crc32_table := [8][256]u32{
|
||||
|
||||
/*
|
||||
@(optimization_mode="speed")
|
||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
result := ~u32(seed);
|
||||
#no_bounds_check for b in data {
|
||||
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||
|
||||
+12
-12
@@ -4,7 +4,7 @@ import "core:mem"
|
||||
import "base:intrinsics"
|
||||
|
||||
@(optimization_mode="speed")
|
||||
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
|
||||
ADLER_CONST :: 65521
|
||||
|
||||
@@ -47,7 +47,7 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
|
||||
djb2 :: proc "contextless" (data: []byte, seed := u32(5381)) -> u32 {
|
||||
hash: u32 = seed
|
||||
for b in data {
|
||||
hash = (hash << 5) + hash + u32(b) // hash * 33 + u32(b)
|
||||
@@ -55,7 +55,7 @@ djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
|
||||
return hash
|
||||
}
|
||||
|
||||
djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check {
|
||||
djbx33a :: proc "contextless" (data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check {
|
||||
state := [4]u32{seed, seed, seed, seed}
|
||||
|
||||
s: u32 = 0
|
||||
@@ -74,7 +74,7 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
|
||||
|
||||
// If you have a choice, prefer fnv32a
|
||||
@(optimization_mode="speed")
|
||||
fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
fnv32_no_a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b)
|
||||
@@ -87,7 +87,7 @@ fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessar
|
||||
|
||||
// If you have a choice, prefer fnv64a
|
||||
@(optimization_mode="speed")
|
||||
fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b)
|
||||
@@ -95,7 +95,7 @@ fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
return h
|
||||
}
|
||||
@(optimization_mode="speed")
|
||||
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
for b in data {
|
||||
h = (h ~ u32(b)) * 0x01000193
|
||||
@@ -104,7 +104,7 @@ fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv64a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
h = (h ~ u64(b)) * 0x100000001b3
|
||||
@@ -113,7 +113,7 @@ fnv64a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
jenkins :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
hash: u32 = seed
|
||||
for b in data {
|
||||
hash += u32(b)
|
||||
@@ -127,7 +127,7 @@ jenkins :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51
|
||||
c2_32: u32 : 0x1b873593
|
||||
|
||||
@@ -178,7 +178,7 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
|
||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
|
||||
@(optimization_mode="speed")
|
||||
murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
m :: 0xc6a4a7935bd1e995
|
||||
r :: 47
|
||||
|
||||
@@ -219,7 +219,7 @@ murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
|
||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
|
||||
@(optimization_mode="speed")
|
||||
murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
m :: 0x5bd1e995
|
||||
r :: 24
|
||||
|
||||
@@ -287,7 +287,7 @@ murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
sdbm :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
sdbm :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
|
||||
hash: u32 = seed
|
||||
for b in data {
|
||||
hash = u32(b) + (hash<<6) + (hash<<16) - hash
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
package hash
|
||||
|
||||
ginger_hash8 :: proc(x: u8) -> u8 {
|
||||
ginger_hash8 :: proc "contextless" (x: u8) -> u8 {
|
||||
h := x * 251
|
||||
h += ~(x << 3)
|
||||
h ~= (x >> 1)
|
||||
@@ -11,7 +11,7 @@ ginger_hash8 :: proc(x: u8) -> u8 {
|
||||
}
|
||||
|
||||
|
||||
ginger_hash16 :: proc(x: u16) -> u16 {
|
||||
ginger_hash16 :: proc "contextless" (x: u16) -> u16 {
|
||||
z := (x << 8) | (x >> 8)
|
||||
h := z
|
||||
h += ~(z << 5)
|
||||
@@ -24,14 +24,14 @@ ginger_hash16 :: proc(x: u16) -> u16 {
|
||||
}
|
||||
|
||||
|
||||
ginger8 :: proc(data: []byte) -> u8 {
|
||||
ginger8 :: proc "contextless" (data: []byte) -> u8 {
|
||||
h := ginger_hash8(0)
|
||||
for b in data {
|
||||
h ~= ginger_hash8(b)
|
||||
}
|
||||
return h
|
||||
}
|
||||
ginger16 :: proc(data: []byte) -> u16 {
|
||||
ginger16 :: proc "contextless" (data: []byte) -> u16 {
|
||||
h := ginger_hash16(0)
|
||||
for b in data {
|
||||
h ~= ginger_hash16(u16(b))
|
||||
|
||||
@@ -129,7 +129,7 @@ XXH3_create_state :: proc(allocator := context.allocator) -> (res: ^XXH3_state,
|
||||
}
|
||||
|
||||
XXH3_destroy_state :: proc(state: ^XXH3_state, allocator := context.allocator) -> (err: Error) {
|
||||
free(state)
|
||||
free(state, allocator)
|
||||
return .None
|
||||
}
|
||||
|
||||
|
||||
@@ -19,15 +19,15 @@ xxh_u32 :: u32
|
||||
XXH32_DEFAULT_SEED :: XXH32_hash(0)
|
||||
|
||||
XXH32_state :: struct {
|
||||
total_len_32: XXH32_hash, /*!< Total length hashed, modulo 2^32 */
|
||||
large_len: XXH32_hash, /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */
|
||||
v1: XXH32_hash, /*!< First accumulator lane */
|
||||
v2: XXH32_hash, /*!< Second accumulator lane */
|
||||
v3: XXH32_hash, /*!< Third accumulator lane */
|
||||
v4: XXH32_hash, /*!< Fourth accumulator lane */
|
||||
mem32: [4]XXH32_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */
|
||||
memsize: XXH32_hash, /*!< Amount of data in @ref mem32 */
|
||||
reserved: XXH32_hash, /*!< Reserved field. Do not read or write to it, it may be removed. */
|
||||
total_len_32: XXH32_hash, /*!< Total length hashed, modulo 2^32 */
|
||||
large_len: XXH32_hash, /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */
|
||||
v1: XXH32_hash, /*!< First accumulator lane */
|
||||
v2: XXH32_hash, /*!< Second accumulator lane */
|
||||
v3: XXH32_hash, /*!< Third accumulator lane */
|
||||
v4: XXH32_hash, /*!< Fourth accumulator lane */
|
||||
mem32: [4]XXH32_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */
|
||||
memsize: XXH32_hash, /*!< Amount of data in @ref mem32 */
|
||||
reserved: XXH32_hash, /*!< Reserved field. Do not read or write to it, it may be removed. */
|
||||
}
|
||||
|
||||
XXH32_canonical :: struct {
|
||||
|
||||
+18
-18
@@ -651,7 +651,7 @@ alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator :=
|
||||
// We have keyed alpha.
|
||||
o: GA_Pixel
|
||||
for p in inp {
|
||||
if p == key.r {
|
||||
if p.r == key.r {
|
||||
o = GA_Pixel{0, key.g}
|
||||
} else {
|
||||
o = GA_Pixel{p.r, 255}
|
||||
@@ -710,7 +710,7 @@ alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator :=
|
||||
// We have keyed alpha.
|
||||
o: GA_Pixel_16
|
||||
for p in inp {
|
||||
if p == key.r {
|
||||
if p.r == key.r {
|
||||
o = GA_Pixel_16{0, key.g}
|
||||
} else {
|
||||
o = GA_Pixel_16{p.r, 65535}
|
||||
@@ -842,11 +842,11 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
bg := G_Pixel{}
|
||||
if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
|
||||
// Background is RGB 16-bit, take just the red channel's topmost byte.
|
||||
bg = u8(temp_bg.r >> 8)
|
||||
bg.r = u8(temp_bg.r >> 8)
|
||||
}
|
||||
|
||||
for p in inp {
|
||||
out[0] = bg if p == key else p
|
||||
out[0] = bg if p.r == key else p
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
@@ -865,8 +865,8 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
for p in inp {
|
||||
a := f32(p.g) / 255.0
|
||||
c := ((1.0 - a) * bg + a * f32(p.r))
|
||||
out[0] = u8(c)
|
||||
out = out[1:]
|
||||
out[0].r = u8(c)
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
} else if .alpha_premultiply in options {
|
||||
@@ -874,14 +874,14 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
for p in inp {
|
||||
a := f32(p.g) / 255.0
|
||||
c := f32(p.r) * a
|
||||
out[0] = u8(c)
|
||||
out = out[1:]
|
||||
out[0].r = u8(c)
|
||||
out = out[1:]
|
||||
}
|
||||
} else {
|
||||
// Just drop alpha on the floor.
|
||||
for p in inp {
|
||||
out[0] = p.r
|
||||
out = out[1:]
|
||||
out[0].r = p.r
|
||||
out = out[1:]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -951,11 +951,11 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
bg := G_Pixel_16{}
|
||||
if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
|
||||
// Background is RGB 16-bit, take just the red channel.
|
||||
bg = temp_bg.r
|
||||
bg.r = temp_bg.r
|
||||
}
|
||||
|
||||
for p in inp {
|
||||
out[0] = bg if p == key else p
|
||||
out[0] = bg if p.r == key else p
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
@@ -974,8 +974,8 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
for p in inp {
|
||||
a := f32(p.g) / 65535.0
|
||||
c := ((1.0 - a) * bg + a * f32(p.r))
|
||||
out[0] = u16(c)
|
||||
out = out[1:]
|
||||
out[0].r = u16(c)
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
} else if .alpha_premultiply in options {
|
||||
@@ -983,14 +983,14 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
for p in inp {
|
||||
a := f32(p.g) / 65535.0
|
||||
c := f32(p.r) * a
|
||||
out[0] = u16(c)
|
||||
out = out[1:]
|
||||
out[0].r = u16(c)
|
||||
out = out[1:]
|
||||
}
|
||||
} else {
|
||||
// Just drop alpha on the floor.
|
||||
for p in inp {
|
||||
out[0] = p.r
|
||||
out = out[1:]
|
||||
out[0].r = p.r
|
||||
out = out[1:]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -199,8 +199,8 @@ save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context
|
||||
for x in 0 ..< img.width {
|
||||
i := y * img.width + x
|
||||
for c in 0 ..< img.channels {
|
||||
i := i * img.channels + c
|
||||
fmt.sbprintf(&data, "%i ", pixels[i])
|
||||
j := i * img.channels + c
|
||||
fmt.sbprintf(&data, "%i ", pixels[j])
|
||||
}
|
||||
fmt.sbprint(&data, "\n")
|
||||
}
|
||||
@@ -213,8 +213,8 @@ save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context
|
||||
for x in 0 ..< img.width {
|
||||
i := y * img.width + x
|
||||
for c in 0 ..< img.channels {
|
||||
i := i * img.channels + c
|
||||
fmt.sbprintf(&data, "%i ", pixels[i])
|
||||
j := i * img.channels + c
|
||||
fmt.sbprintf(&data, "%i ", pixels[j])
|
||||
}
|
||||
fmt.sbprint(&data, "\n")
|
||||
}
|
||||
@@ -283,7 +283,7 @@ _parse_header_pnm :: proc(data: []byte) -> (header: Header, length: int, err: Er
|
||||
current_field := 0
|
||||
current_value := header_fields[0]
|
||||
|
||||
parse_loop: for d, i in data[SIG_LENGTH:] {
|
||||
parse_loop: for d in data[SIG_LENGTH:] {
|
||||
length += 1
|
||||
|
||||
// handle comments
|
||||
@@ -728,4 +728,4 @@ _register :: proc() {
|
||||
_ = destroy(img)
|
||||
}
|
||||
image.register(.NetPBM, loader, destroyer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ Multi_Reader :: struct {
|
||||
readers: [dynamic]Reader,
|
||||
}
|
||||
|
||||
@(private)
|
||||
_multi_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
|
||||
if mode == .Query {
|
||||
return query_utility({.Read, .Query})
|
||||
@@ -58,7 +57,6 @@ Multi_Writer :: struct {
|
||||
writers: [dynamic]Writer,
|
||||
}
|
||||
|
||||
@(private)
|
||||
_multi_writer_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
|
||||
if mode == .Query {
|
||||
return query_utility({.Write, .Query})
|
||||
|
||||
@@ -777,6 +777,11 @@ int_from_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, alloca
|
||||
*/
|
||||
INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}
|
||||
|
||||
@(init, private)
|
||||
_init_constants :: proc() {
|
||||
initialize_constants()
|
||||
}
|
||||
|
||||
initialize_constants :: proc() -> (res: int) {
|
||||
internal_set( INT_ZERO, 0); INT_ZERO.flags = {.Immutable}
|
||||
internal_set( INT_ONE, 1); INT_ONE.flags = {.Immutable}
|
||||
@@ -788,7 +793,7 @@ initialize_constants :: proc() -> (res: int) {
|
||||
*/
|
||||
internal_set( INT_NAN, 1); INT_NAN.flags = {.Immutable, .NaN}
|
||||
internal_set( INT_INF, 1); INT_INF.flags = {.Immutable, .Inf}
|
||||
internal_set( INT_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf}
|
||||
internal_set(INT_MINUS_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf}
|
||||
|
||||
return _DEFAULT_MUL_KARATSUBA_CUTOFF
|
||||
}
|
||||
|
||||
@@ -1181,28 +1181,18 @@ internal_cmp_digit :: internal_compare_digit
|
||||
*/
|
||||
internal_int_compare_magnitude :: #force_inline proc(a, b: ^Int) -> (comparison: int) {
|
||||
assert_if_nil(a, b)
|
||||
/*
|
||||
Compare based on used digits.
|
||||
*/
|
||||
|
||||
// Compare based on used digits.
|
||||
if a.used != b.used {
|
||||
if a.used > b.used {
|
||||
return +1
|
||||
}
|
||||
return -1
|
||||
return +1 if a.used > b.used else -1
|
||||
}
|
||||
|
||||
/*
|
||||
Same number of used digits, compare based on their value.
|
||||
*/
|
||||
// Same number of used digits, compare based on their value.
|
||||
#no_bounds_check for n := a.used - 1; n >= 0; n -= 1 {
|
||||
if a.digit[n] != b.digit[n] {
|
||||
if a.digit[n] > b.digit[n] {
|
||||
return +1
|
||||
}
|
||||
return -1
|
||||
return +1 if a.digit[n] > b.digit[n] else -1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
internal_compare_magnitude :: proc { internal_int_compare_magnitude, }
|
||||
@@ -2046,9 +2036,9 @@ internal_int_inverse_modulo :: proc(dest, a, b: ^Int, allocator := context.alloc
|
||||
if internal_is_positive(a) && internal_eq(b, 1) { return internal_zero(dest) }
|
||||
|
||||
/*
|
||||
`b` cannot be negative and has to be > 1
|
||||
`b` cannot be negative and b has to be > 1
|
||||
*/
|
||||
if internal_is_negative(b) || internal_gt(b, 1) { return .Invalid_Argument }
|
||||
if internal_is_negative(b) || !internal_gt(b, 1) { return .Invalid_Argument }
|
||||
|
||||
/*
|
||||
If the modulus is odd we can use a faster routine instead.
|
||||
@@ -2057,6 +2047,7 @@ internal_int_inverse_modulo :: proc(dest, a, b: ^Int, allocator := context.alloc
|
||||
|
||||
return _private_inverse_modulo(dest, a, b)
|
||||
}
|
||||
internal_int_invmod :: internal_int_inverse_modulo
|
||||
internal_invmod :: proc{ internal_int_inverse_modulo, }
|
||||
|
||||
/*
|
||||
@@ -2954,4 +2945,4 @@ internal_zero_unused :: proc { internal_int_zero_unused, }
|
||||
|
||||
/*
|
||||
========================== End of low-level routines ==========================
|
||||
*/
|
||||
*/
|
||||
|
||||
@@ -44,7 +44,7 @@ internal_int_prime_is_divisible :: proc(a: ^Int, allocator := context.allocator)
|
||||
Computes res == G**X mod P.
|
||||
Assumes `res`, `G`, `X` and `P` to not be `nil` and for `G`, `X` and `P` to have been initialized.
|
||||
*/
|
||||
internal_int_exponent_mod :: proc(res, G, X, P: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
internal_int_power_modulo :: proc(res, G, X, P: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
dr: int
|
||||
@@ -112,6 +112,9 @@ internal_int_exponent_mod :: proc(res, G, X, P: ^Int, allocator := context.alloc
|
||||
*/
|
||||
return _private_int_exponent_mod(res, G, X, P, 0)
|
||||
}
|
||||
internal_int_exponent_mod :: internal_int_power_modulo
|
||||
internal_int_powmod :: internal_int_power_modulo
|
||||
internal_powmod :: proc { internal_int_power_modulo, }
|
||||
|
||||
/*
|
||||
Kronecker/Legendre symbol (a|p)
|
||||
@@ -1109,7 +1112,7 @@ internal_int_prime_next_prime :: proc(a: ^Int, trials: int, bbs_style: bool, all
|
||||
Generate the restable.
|
||||
*/
|
||||
for x := 1; x < _PRIME_TAB_SIZE; x += 1 {
|
||||
res_tab = internal_mod(a, _private_prime_table[x]) or_return
|
||||
res_tab = cast(type_of(res_tab))(internal_mod(a, _private_prime_table[x]) or_return)
|
||||
}
|
||||
|
||||
for {
|
||||
@@ -1411,4 +1414,4 @@ number_of_rabin_miller_trials :: proc(bit_size: int) -> (number_of_trials: int)
|
||||
case:
|
||||
return 2 /* For keysizes bigger than 10_240 use always at least 2 Rounds */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3354
-3401
File diff suppressed because it is too large
Load Diff
+168
-21
@@ -39,7 +39,6 @@ init_from_f64 :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), val: f64) {
|
||||
}
|
||||
|
||||
init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fraction: Backing) {
|
||||
i, f := math.modf(val)
|
||||
x.i = fraction
|
||||
x.i &= 1<<Fraction_Width - 1
|
||||
x.i |= integer
|
||||
@@ -103,37 +102,51 @@ round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
|
||||
return (x.i + (1 << (Fraction_Width - 1))) >> Fraction_Width
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
|
||||
Integer_Width :: 8*size_of(Backing) - Fraction_Width
|
||||
|
||||
x := x
|
||||
buf: [48]byte
|
||||
i := 0
|
||||
if x.i < 0 {
|
||||
|
||||
if !intrinsics.type_is_unsigned(Backing) && x.i == min(Backing) {
|
||||
// edge case handling for signed numbers
|
||||
buf[i] = '-'
|
||||
i += 1
|
||||
x.i = -x.i
|
||||
}
|
||||
|
||||
integer := x.i >> Fraction_Width
|
||||
fraction := x.i & (1<<Fraction_Width - 1)
|
||||
|
||||
s := strconv.append_uint(buf[i:], u64(integer), 10)
|
||||
i += len(s)
|
||||
if fraction != 0 {
|
||||
buf[i] = '.'
|
||||
i += 1
|
||||
for fraction > 0 {
|
||||
fraction *= 10
|
||||
buf[i] = byte('0' + (fraction>>Fraction_Width))
|
||||
i += copy(buf[i:], _power_of_two_table[Integer_Width])
|
||||
} else {
|
||||
if x.i < 0 {
|
||||
buf[i] = '-'
|
||||
i += 1
|
||||
fraction &= 1<<Fraction_Width - 1
|
||||
x.i = -x.i
|
||||
}
|
||||
|
||||
when size_of(Backing) < 16 {
|
||||
T :: u64
|
||||
append_uint :: strconv.append_uint
|
||||
} else {
|
||||
T :: u128
|
||||
append_uint :: strconv.append_u128
|
||||
}
|
||||
|
||||
integer := T(x.i) >> Fraction_Width
|
||||
fraction := T(x.i) & (1<<Fraction_Width - 1)
|
||||
|
||||
s := append_uint(buf[i:], integer, 10)
|
||||
i += len(s)
|
||||
if fraction != 0 {
|
||||
buf[i] = '.'
|
||||
i += 1
|
||||
for fraction > 0 {
|
||||
fraction *= 10
|
||||
buf[i] = byte('0' + (fraction>>Fraction_Width) % 10)
|
||||
i += 1
|
||||
fraction &= 1<<Fraction_Width - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
n := copy(dst, buf[:i])
|
||||
return string(dst[:i])
|
||||
}
|
||||
@@ -147,3 +160,137 @@ to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.a
|
||||
copy(str, s)
|
||||
return string(str)
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_power_of_two_table := [129]string{
|
||||
"0.5",
|
||||
"1",
|
||||
"2",
|
||||
"4",
|
||||
"8",
|
||||
"16",
|
||||
"32",
|
||||
"64",
|
||||
"128",
|
||||
"256",
|
||||
"512",
|
||||
"1024",
|
||||
"2048",
|
||||
"4096",
|
||||
"8192",
|
||||
"16384",
|
||||
"32768",
|
||||
"65536",
|
||||
"131072",
|
||||
"262144",
|
||||
"524288",
|
||||
"1048576",
|
||||
"2097152",
|
||||
"4194304",
|
||||
"8388608",
|
||||
"16777216",
|
||||
"33554432",
|
||||
"67108864",
|
||||
"134217728",
|
||||
"268435456",
|
||||
"536870912",
|
||||
"1073741824",
|
||||
"2147483648",
|
||||
"4294967296",
|
||||
"8589934592",
|
||||
"17179869184",
|
||||
"34359738368",
|
||||
"68719476736",
|
||||
"137438953472",
|
||||
"274877906944",
|
||||
"549755813888",
|
||||
"1099511627776",
|
||||
"2199023255552",
|
||||
"4398046511104",
|
||||
"8796093022208",
|
||||
"17592186044416",
|
||||
"35184372088832",
|
||||
"70368744177664",
|
||||
"140737488355328",
|
||||
"281474976710656",
|
||||
"562949953421312",
|
||||
"1125899906842624",
|
||||
"2251799813685248",
|
||||
"4503599627370496",
|
||||
"9007199254740992",
|
||||
"18014398509481984",
|
||||
"36028797018963968",
|
||||
"72057594037927936",
|
||||
"144115188075855872",
|
||||
"288230376151711744",
|
||||
"576460752303423488",
|
||||
"1152921504606846976",
|
||||
"2305843009213693952",
|
||||
"4611686018427387904",
|
||||
"9223372036854775808",
|
||||
"18446744073709551616",
|
||||
"36893488147419103232",
|
||||
"73786976294838206464",
|
||||
"147573952589676412928",
|
||||
"295147905179352825856",
|
||||
"590295810358705651712",
|
||||
"1180591620717411303424",
|
||||
"2361183241434822606848",
|
||||
"4722366482869645213696",
|
||||
"9444732965739290427392",
|
||||
"18889465931478580854784",
|
||||
"37778931862957161709568",
|
||||
"75557863725914323419136",
|
||||
"151115727451828646838272",
|
||||
"302231454903657293676544",
|
||||
"604462909807314587353088",
|
||||
"1208925819614629174706176",
|
||||
"2417851639229258349412352",
|
||||
"4835703278458516698824704",
|
||||
"9671406556917033397649408",
|
||||
"19342813113834066795298816",
|
||||
"38685626227668133590597632",
|
||||
"77371252455336267181195264",
|
||||
"154742504910672534362390528",
|
||||
"309485009821345068724781056",
|
||||
"618970019642690137449562112",
|
||||
"1237940039285380274899124224",
|
||||
"2475880078570760549798248448",
|
||||
"4951760157141521099596496896",
|
||||
"9903520314283042199192993792",
|
||||
"19807040628566084398385987584",
|
||||
"39614081257132168796771975168",
|
||||
"79228162514264337593543950336",
|
||||
"158456325028528675187087900672",
|
||||
"316912650057057350374175801344",
|
||||
"633825300114114700748351602688",
|
||||
"1267650600228229401496703205376",
|
||||
"2535301200456458802993406410752",
|
||||
"5070602400912917605986812821504",
|
||||
"10141204801825835211973625643008",
|
||||
"20282409603651670423947251286016",
|
||||
"40564819207303340847894502572032",
|
||||
"81129638414606681695789005144064",
|
||||
"162259276829213363391578010288128",
|
||||
"324518553658426726783156020576256",
|
||||
"649037107316853453566312041152512",
|
||||
"1298074214633706907132624082305024",
|
||||
"2596148429267413814265248164610048",
|
||||
"5192296858534827628530496329220096",
|
||||
"10384593717069655257060992658440192",
|
||||
"20769187434139310514121985316880384",
|
||||
"41538374868278621028243970633760768",
|
||||
"83076749736557242056487941267521536",
|
||||
"166153499473114484112975882535043072",
|
||||
"332306998946228968225951765070086144",
|
||||
"664613997892457936451903530140172288",
|
||||
"1329227995784915872903807060280344576",
|
||||
"2658455991569831745807614120560689152",
|
||||
"5316911983139663491615228241121378304",
|
||||
"10633823966279326983230456482242756608",
|
||||
"21267647932558653966460912964485513216",
|
||||
"42535295865117307932921825928971026432",
|
||||
"85070591730234615865843651857942052864",
|
||||
"170141183460469231731687303715884105728",
|
||||
}
|
||||
|
||||
@@ -326,6 +326,7 @@ ln :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
|
||||
@(require_results)
|
||||
log2 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
INVLN2 :: 1.4426950408889634073599246810018921374266459541529859341354494069
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = INVLN2 * math.ln(x[i])
|
||||
@@ -338,6 +339,7 @@ log2 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
|
||||
@(require_results)
|
||||
log10 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
INVLN10 :: 0.4342944819032518276511289189166050822943970058036665661144537831
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = INVLN10 * math.ln(x[i])
|
||||
@@ -355,7 +357,7 @@ log :: proc "contextless" (x, b: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
out[i] = math.ln(x[i]) / math.ln(cast(ELEM_TYPE(T))b[i])
|
||||
}
|
||||
} else {
|
||||
out = INVLN10 * math.ln(x) / math.ln(cast(ELEM_TYPE(T))b)
|
||||
out = math.ln(x) / math.ln(cast(ELEM_TYPE(T))b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -172,13 +172,23 @@ projection :: proc "contextless" (x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
identity :: proc "contextless" ($T: typeid/[$N][N]$E) -> (m: T) #no_bounds_check {
|
||||
identity_array_based_matrix :: proc "contextless" ($T: typeid/[$N][N]$E) -> (m: T) #no_bounds_check {
|
||||
for i in 0..<N {
|
||||
m[i][i] = E(1)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
identity_matrix :: proc "contextless" ($T: typeid/matrix[$N, N]$E) -> T #no_bounds_check {
|
||||
return 1
|
||||
}
|
||||
|
||||
identity :: proc{
|
||||
identity_array_based_matrix,
|
||||
identity_matrix,
|
||||
}
|
||||
|
||||
transpose :: intrinsics.transpose
|
||||
|
||||
@(require_results)
|
||||
@@ -267,11 +277,36 @@ to_ptr :: proc{vector_to_ptr, matrix_to_ptr}
|
||||
|
||||
|
||||
|
||||
vector_angle_between :: proc "contextless" (a, b: $V/[$N]$E) -> E {
|
||||
a0 := normalize0(a)
|
||||
b0 := normalize0(b)
|
||||
return math.acos(dot(a0, b0))
|
||||
}
|
||||
quaternion64_angle_between :: proc "contextless" (a, b: $Q/quaternion64) -> f16 {
|
||||
c := normalize0(conj(a) * b)
|
||||
return math.acos(c.w)
|
||||
}
|
||||
quaternion128_angle_between :: proc "contextless" (a, b: $Q/quaternion128) -> f32 {
|
||||
c := normalize0(conj(a) * b)
|
||||
return math.acos(c.w)
|
||||
}
|
||||
quaternion256_angle_between :: proc "contextless" (a, b: $Q/quaternion256) -> f64 {
|
||||
c := normalize0(conj(a) * b)
|
||||
return math.acos(c.w)
|
||||
}
|
||||
angle_between :: proc{
|
||||
vector_angle_between,
|
||||
quaternion64_angle_between,
|
||||
quaternion128_angle_between,
|
||||
quaternion256_angle_between,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Splines
|
||||
|
||||
@(require_results)
|
||||
vector_slerp :: proc "contextless" (x, y: $T/[$N]$E, a: E) -> T {
|
||||
vector_slerp :: proc "contextless" (x, y: $T/[$N]$E, a: E) -> T #no_bounds_check {
|
||||
cos_alpha := dot(x, y)
|
||||
alpha := math.acos(cos_alpha)
|
||||
sin_alpha := math.sin(alpha)
|
||||
@@ -283,7 +318,7 @@ vector_slerp :: proc "contextless" (x, y: $T/[$N]$E, a: E) -> T {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
catmull_rom :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
|
||||
catmull_rom :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T #no_bounds_check {
|
||||
s2 := s*s
|
||||
s3 := s2*s
|
||||
|
||||
@@ -296,7 +331,7 @@ catmull_rom :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
hermite :: proc "contextless" (v1, t1, v2, t2: $T/[$N]$E, s: E) -> T {
|
||||
hermite :: proc "contextless" (v1, t1, v2, t2: $T/[$N]$E, s: E) -> T #no_bounds_check {
|
||||
s2 := s*s
|
||||
s3 := s2*s
|
||||
|
||||
@@ -309,7 +344,7 @@ hermite :: proc "contextless" (v1, t1, v2, t2: $T/[$N]$E, s: E) -> T {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
cubic :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
|
||||
cubic :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T #no_bounds_check {
|
||||
return ((v1 * s + v2) * s + v3) * s + v4
|
||||
}
|
||||
|
||||
@@ -390,12 +425,12 @@ inverse :: proc{
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
hermitian_adjoint :: proc "contextless" (m: $M/matrix[$N, N]$T) -> M where intrinsics.type_is_complex(T), N >= 1 {
|
||||
hermitian_adjoint :: proc "contextless" (m: $M/matrix[$N, N]$T) -> M where intrinsics.type_is_complex(T), N >= 1 #no_bounds_check {
|
||||
return conj(transpose(m))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
trace :: proc "contextless" (m: $M/matrix[$N, N]$T) -> (trace: T) {
|
||||
trace :: proc "contextless" (m: $M/matrix[$N, N]$T) -> (trace: T) #no_bounds_check {
|
||||
for i in 0..<N {
|
||||
trace += m[i, i]
|
||||
}
|
||||
@@ -403,7 +438,7 @@ trace :: proc "contextless" (m: $M/matrix[$N, N]$T) -> (trace: T) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
matrix_minor :: proc "contextless" (m: $M/matrix[$N, N]$T, #any_int row, column: int) -> (minor: T) where N > 1 {
|
||||
matrix_minor :: proc "contextless" (m: $M/matrix[$N, N]$T, #any_int row, column: int) -> (minor: T) where N > 1 #no_bounds_check {
|
||||
K :: int(N-1)
|
||||
cut_down: matrix[K, K]T
|
||||
for col_idx in 0..<K {
|
||||
@@ -419,25 +454,25 @@ matrix_minor :: proc "contextless" (m: $M/matrix[$N, N]$T, #any_int row, column:
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix1x1_determinant :: proc "contextless" (m: $M/matrix[1, 1]$T) -> (det: T) {
|
||||
matrix1x1_determinant :: proc "contextless" (m: $M/matrix[1, 1]$T) -> (det: T) #no_bounds_check {
|
||||
return m[0, 0]
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
matrix2x2_determinant :: proc "contextless" (m: $M/matrix[2, 2]$T) -> (det: T) {
|
||||
matrix2x2_determinant :: proc "contextless" (m: $M/matrix[2, 2]$T) -> (det: T) #no_bounds_check {
|
||||
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
}
|
||||
@(require_results)
|
||||
matrix3x3_determinant :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
|
||||
matrix3x3_determinant :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) #no_bounds_check {
|
||||
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
|
||||
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
|
||||
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
|
||||
return a + b + c
|
||||
}
|
||||
@(require_results)
|
||||
matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
|
||||
matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) #no_bounds_check {
|
||||
a := adjugate(m)
|
||||
#no_bounds_check for i in 0..<4 {
|
||||
for i in 0..<4 {
|
||||
det += m[0, i] * a[0, i]
|
||||
}
|
||||
return
|
||||
@@ -447,13 +482,13 @@ matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix1x1_adjugate :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
matrix1x1_adjugate :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bounds_check {
|
||||
y = x
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
|
||||
y[0, 0] = +x[1, 1]
|
||||
y[0, 1] = -x[1, 0]
|
||||
y[1, 0] = -x[0, 1]
|
||||
@@ -462,7 +497,7 @@ matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
|
||||
matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
|
||||
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
|
||||
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
|
||||
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
|
||||
@@ -477,7 +512,7 @@ matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
|
||||
matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
sign: T = 1 if (i + j) % 2 == 0 else -1
|
||||
@@ -488,13 +523,13 @@ matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
matrix1x1_inverse_transpose :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
matrix1x1_inverse_transpose :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bounds_check {
|
||||
y[0, 0] = 1/x[0, 0]
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
|
||||
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
|
||||
when intrinsics.type_is_integer(T) {
|
||||
y[0, 0] = +x[1, 1] / d
|
||||
@@ -557,13 +592,13 @@ matrix4x4_inverse_transpose :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y:
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
matrix1x1_inverse :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
|
||||
matrix1x1_inverse :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bounds_check {
|
||||
y[0, 0] = 1/x[0, 0]
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
|
||||
matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
|
||||
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
|
||||
when intrinsics.type_is_integer(T) {
|
||||
y[0, 0] = +x[1, 1] / d
|
||||
|
||||
+144
-101
@@ -584,7 +584,7 @@ angle_axis_from_quaternion :: proc {
|
||||
|
||||
|
||||
@(require_results)
|
||||
quaternion_from_forward_and_up_f16 :: proc "contextless" (forward, up: Vector3f16) -> Quaternionf16 {
|
||||
quaternion_from_forward_and_up_f16 :: proc "contextless" (forward, up: Vector3f16) -> Quaternionf16 #no_bounds_check {
|
||||
f := normalize(forward)
|
||||
s := normalize(cross(f, up))
|
||||
u := cross(s, f)
|
||||
@@ -628,7 +628,7 @@ quaternion_from_forward_and_up_f16 :: proc "contextless" (forward, up: Vector3f1
|
||||
return normalize(q)
|
||||
}
|
||||
@(require_results)
|
||||
quaternion_from_forward_and_up_f32 :: proc "contextless" (forward, up: Vector3f32) -> Quaternionf32 {
|
||||
quaternion_from_forward_and_up_f32 :: proc "contextless" (forward, up: Vector3f32) -> Quaternionf32 #no_bounds_check {
|
||||
f := normalize(forward)
|
||||
s := normalize(cross(f, up))
|
||||
u := cross(s, f)
|
||||
@@ -672,7 +672,7 @@ quaternion_from_forward_and_up_f32 :: proc "contextless" (forward, up: Vector3f3
|
||||
return normalize(q)
|
||||
}
|
||||
@(require_results)
|
||||
quaternion_from_forward_and_up_f64 :: proc "contextless" (forward, up: Vector3f64) -> Quaternionf64 {
|
||||
quaternion_from_forward_and_up_f64 :: proc "contextless" (forward, up: Vector3f64) -> Quaternionf64 #no_bounds_check {
|
||||
f := normalize(forward)
|
||||
s := normalize(cross(f, up))
|
||||
u := cross(s, f)
|
||||
@@ -886,7 +886,7 @@ quaternion_squad :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
quaternion_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (q: Quaternionf16) {
|
||||
quaternion_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (q: Quaternionf16) #no_bounds_check {
|
||||
m3: Matrix3f16 = ---
|
||||
m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
|
||||
m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
|
||||
@@ -894,7 +894,7 @@ quaternion_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (q: Quatern
|
||||
return quaternion_from_matrix3(m3)
|
||||
}
|
||||
@(require_results)
|
||||
quaternion_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (q: Quaternionf32) {
|
||||
quaternion_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (q: Quaternionf32) #no_bounds_check {
|
||||
m3: Matrix3f32 = ---
|
||||
m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
|
||||
m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
|
||||
@@ -902,7 +902,7 @@ quaternion_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (q: Quatern
|
||||
return quaternion_from_matrix3(m3)
|
||||
}
|
||||
@(require_results)
|
||||
quaternion_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (q: Quaternionf64) {
|
||||
quaternion_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (q: Quaternionf64) #no_bounds_check {
|
||||
m3: Matrix3f64 = ---
|
||||
m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
|
||||
m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
|
||||
@@ -917,7 +917,7 @@ quaternion_from_matrix4 :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
quaternion_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (q: Quaternionf16) {
|
||||
quaternion_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (q: Quaternionf16) #no_bounds_check {
|
||||
four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
|
||||
four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
|
||||
four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
|
||||
@@ -967,7 +967,7 @@ quaternion_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (q: Quatern
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
quaternion_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (q: Quaternionf32) {
|
||||
quaternion_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (q: Quaternionf32) #no_bounds_check {
|
||||
four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
|
||||
four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
|
||||
four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
|
||||
@@ -1017,7 +1017,7 @@ quaternion_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (q: Quatern
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
quaternion_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (q: Quaternionf64) {
|
||||
quaternion_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (q: Quaternionf64) #no_bounds_check {
|
||||
four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
|
||||
four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
|
||||
four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
|
||||
@@ -1147,7 +1147,7 @@ quaternion_between_two_vector3 :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix2_inverse_transpose_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
|
||||
matrix2_inverse_transpose_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) #no_bounds_check {
|
||||
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
id := 1.0/d
|
||||
c[0, 0] = +m[1, 1] * id
|
||||
@@ -1157,7 +1157,7 @@ matrix2_inverse_transpose_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matri
|
||||
return c
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_inverse_transpose_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
|
||||
matrix2_inverse_transpose_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) #no_bounds_check {
|
||||
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
id := 1.0/d
|
||||
c[0, 0] = +m[1, 1] * id
|
||||
@@ -1167,7 +1167,7 @@ matrix2_inverse_transpose_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matri
|
||||
return c
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_inverse_transpose_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) {
|
||||
matrix2_inverse_transpose_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) #no_bounds_check {
|
||||
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
id := 1.0/d
|
||||
c[0, 0] = +m[1, 1] * id
|
||||
@@ -1184,15 +1184,15 @@ matrix2_inverse_transpose :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix2_determinant_f16 :: proc "contextless" (m: Matrix2f16) -> f16 {
|
||||
matrix2_determinant_f16 :: proc "contextless" (m: Matrix2f16) -> f16 #no_bounds_check {
|
||||
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_determinant_f32 :: proc "contextless" (m: Matrix2f32) -> f32 {
|
||||
matrix2_determinant_f32 :: proc "contextless" (m: Matrix2f32) -> f32 #no_bounds_check {
|
||||
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_determinant_f64 :: proc "contextless" (m: Matrix2f64) -> f64 {
|
||||
matrix2_determinant_f64 :: proc "contextless" (m: Matrix2f64) -> f64 #no_bounds_check {
|
||||
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
}
|
||||
matrix2_determinant :: proc{
|
||||
@@ -1203,7 +1203,7 @@ matrix2_determinant :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix2_inverse_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
|
||||
matrix2_inverse_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) #no_bounds_check {
|
||||
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
id := 1.0/d
|
||||
c[0, 0] = +m[1, 1] * id
|
||||
@@ -1213,7 +1213,7 @@ matrix2_inverse_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
|
||||
return c
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_inverse_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
|
||||
matrix2_inverse_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) #no_bounds_check {
|
||||
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
id := 1.0/d
|
||||
c[0, 0] = +m[1, 1] * id
|
||||
@@ -1223,7 +1223,7 @@ matrix2_inverse_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
|
||||
return c
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_inverse_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) {
|
||||
matrix2_inverse_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) #no_bounds_check {
|
||||
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
|
||||
id := 1.0/d
|
||||
c[0, 0] = +m[1, 1] * id
|
||||
@@ -1240,7 +1240,7 @@ matrix2_inverse :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix2_adjoint_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
|
||||
matrix2_adjoint_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) #no_bounds_check {
|
||||
c[0, 0] = +m[1, 1]
|
||||
c[1, 0] = -m[0, 1]
|
||||
c[0, 1] = -m[1, 0]
|
||||
@@ -1248,7 +1248,7 @@ matrix2_adjoint_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
|
||||
return c
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_adjoint_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
|
||||
matrix2_adjoint_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) #no_bounds_check {
|
||||
c[0, 0] = +m[1, 1]
|
||||
c[1, 0] = -m[0, 1]
|
||||
c[0, 1] = -m[1, 0]
|
||||
@@ -1256,7 +1256,7 @@ matrix2_adjoint_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
|
||||
return c
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_adjoint_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) {
|
||||
matrix2_adjoint_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) #no_bounds_check {
|
||||
c[0, 0] = +m[1, 1]
|
||||
c[1, 0] = -m[0, 1]
|
||||
c[0, 1] = -m[1, 0]
|
||||
@@ -1271,7 +1271,44 @@ matrix2_adjoint :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix3_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matrix3f16) {
|
||||
matrix2_rotate_f16 :: proc "contextless" (angle_radians: f16) -> Matrix2f16 {
|
||||
c := math.cos(angle_radians)
|
||||
s := math.sin(angle_radians)
|
||||
|
||||
return Matrix2f16{
|
||||
c, -s,
|
||||
s, c,
|
||||
}
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_rotate_f32 :: proc "contextless" (angle_radians: f32) -> Matrix2f32 {
|
||||
c := math.cos(angle_radians)
|
||||
s := math.sin(angle_radians)
|
||||
|
||||
return Matrix2f32{
|
||||
c, -s,
|
||||
s, c,
|
||||
}
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_rotate_f64 :: proc "contextless" (angle_radians: f64) -> Matrix2f64 {
|
||||
c := math.cos(angle_radians)
|
||||
s := math.sin(angle_radians)
|
||||
|
||||
return Matrix2f64{
|
||||
c, -s,
|
||||
s, c,
|
||||
}
|
||||
}
|
||||
matrix2_rotate :: proc{
|
||||
matrix2_rotate_f16,
|
||||
matrix2_rotate_f32,
|
||||
matrix2_rotate_f64,
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix3_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matrix3f16) #no_bounds_check {
|
||||
qxx := q.x * q.x
|
||||
qyy := q.y * q.y
|
||||
qzz := q.z * q.z
|
||||
@@ -1296,7 +1333,7 @@ matrix3_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matr
|
||||
return m
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matrix3f32) {
|
||||
matrix3_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matrix3f32) #no_bounds_check {
|
||||
qxx := q.x * q.x
|
||||
qyy := q.y * q.y
|
||||
qzz := q.z * q.z
|
||||
@@ -1321,7 +1358,7 @@ matrix3_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matr
|
||||
return m
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (m: Matrix3f64) {
|
||||
matrix3_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (m: Matrix3f64) #no_bounds_check {
|
||||
qxx := q.x * q.x
|
||||
qyy := q.y * q.y
|
||||
qzz := q.z * q.z
|
||||
@@ -1372,21 +1409,21 @@ matrix3_inverse :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix3_determinant_f16 :: proc "contextless" (m: Matrix3f16) -> f16 {
|
||||
matrix3_determinant_f16 :: proc "contextless" (m: Matrix3f16) -> f16 #no_bounds_check {
|
||||
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
|
||||
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
|
||||
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
|
||||
return a + b + c
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_determinant_f32 :: proc "contextless" (m: Matrix3f32) -> f32 {
|
||||
matrix3_determinant_f32 :: proc "contextless" (m: Matrix3f32) -> f32 #no_bounds_check {
|
||||
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
|
||||
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
|
||||
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
|
||||
return a + b + c
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_determinant_f64 :: proc "contextless" (m: Matrix3f64) -> f64 {
|
||||
matrix3_determinant_f64 :: proc "contextless" (m: Matrix3f64) -> f64 #no_bounds_check {
|
||||
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
|
||||
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
|
||||
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
|
||||
@@ -1400,7 +1437,7 @@ matrix3_determinant :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix3_adjoint_f16 :: proc "contextless" (m: Matrix3f16) -> (adjoint: Matrix3f16) {
|
||||
matrix3_adjoint_f16 :: proc "contextless" (m: Matrix3f16) -> (adjoint: Matrix3f16) #no_bounds_check {
|
||||
adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
|
||||
adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
|
||||
adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
|
||||
@@ -1413,7 +1450,7 @@ matrix3_adjoint_f16 :: proc "contextless" (m: Matrix3f16) -> (adjoint: Matrix3f1
|
||||
return adjoint
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_adjoint_f32 :: proc "contextless" (m: Matrix3f32) -> (adjoint: Matrix3f32) {
|
||||
matrix3_adjoint_f32 :: proc "contextless" (m: Matrix3f32) -> (adjoint: Matrix3f32) #no_bounds_check {
|
||||
adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
|
||||
adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
|
||||
adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
|
||||
@@ -1426,7 +1463,7 @@ matrix3_adjoint_f32 :: proc "contextless" (m: Matrix3f32) -> (adjoint: Matrix3f3
|
||||
return adjoint
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_adjoint_f64 :: proc "contextless" (m: Matrix3f64) -> (adjoint: Matrix3f64) {
|
||||
matrix3_adjoint_f64 :: proc "contextless" (m: Matrix3f64) -> (adjoint: Matrix3f64) #no_bounds_check {
|
||||
adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
|
||||
adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
|
||||
adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
|
||||
@@ -1466,21 +1503,21 @@ matrix3_inverse_transpose :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix3_scale_f16 :: proc "contextless" (s: Vector3f16) -> (m: Matrix3f16) {
|
||||
matrix3_scale_f16 :: proc "contextless" (s: Vector3f16) -> (m: Matrix3f16) #no_bounds_check {
|
||||
m[0, 0] = s[0]
|
||||
m[1, 1] = s[1]
|
||||
m[2, 2] = s[2]
|
||||
return m
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_scale_f32 :: proc "contextless" (s: Vector3f32) -> (m: Matrix3f32) {
|
||||
matrix3_scale_f32 :: proc "contextless" (s: Vector3f32) -> (m: Matrix3f32) #no_bounds_check {
|
||||
m[0, 0] = s[0]
|
||||
m[1, 1] = s[1]
|
||||
m[2, 2] = s[2]
|
||||
return m
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_scale_f64 :: proc "contextless" (s: Vector3f64) -> (m: Matrix3f64) {
|
||||
matrix3_scale_f64 :: proc "contextless" (s: Vector3f64) -> (m: Matrix3f64) #no_bounds_check {
|
||||
m[0, 0] = s[0]
|
||||
m[1, 1] = s[1]
|
||||
m[2, 2] = s[2]
|
||||
@@ -1494,7 +1531,7 @@ matrix3_scale :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix3_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) -> (rot: Matrix3f16) {
|
||||
matrix3_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) -> (rot: Matrix3f16) #no_bounds_check {
|
||||
c := math.cos(angle_radians)
|
||||
s := math.sin(angle_radians)
|
||||
|
||||
@@ -1516,7 +1553,7 @@ matrix3_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) ->
|
||||
return rot
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) -> (rot: Matrix3f32) {
|
||||
matrix3_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) -> (rot: Matrix3f32) #no_bounds_check {
|
||||
c := math.cos(angle_radians)
|
||||
s := math.sin(angle_radians)
|
||||
|
||||
@@ -1607,7 +1644,7 @@ matrix3_look_at :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matrix4f16) {
|
||||
matrix4_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matrix4f16) #no_bounds_check {
|
||||
qxx := q.x * q.x
|
||||
qyy := q.y * q.y
|
||||
qzz := q.z * q.z
|
||||
@@ -1635,7 +1672,7 @@ matrix4_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matr
|
||||
return m
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matrix4f32) {
|
||||
matrix4_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matrix4f32) #no_bounds_check {
|
||||
qxx := q.x * q.x
|
||||
qyy := q.y * q.y
|
||||
qzz := q.z * q.z
|
||||
@@ -1663,7 +1700,7 @@ matrix4_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matr
|
||||
return m
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (m: Matrix4f64) {
|
||||
matrix4_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (m: Matrix4f64) #no_bounds_check {
|
||||
qxx := q.x * q.x
|
||||
qyy := q.y * q.y
|
||||
qzz := q.z * q.z
|
||||
@@ -1746,7 +1783,7 @@ matrix4_inverse :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_minor_f16 :: proc "contextless" (m: Matrix4f16, c, r: int) -> f16 {
|
||||
matrix4_minor_f16 :: proc "contextless" (m: Matrix4f16, c, r: int) -> f16 #no_bounds_check {
|
||||
cut_down: Matrix3f16
|
||||
for i in 0..<3 {
|
||||
col := i if i < c else i+1
|
||||
@@ -1758,7 +1795,7 @@ matrix4_minor_f16 :: proc "contextless" (m: Matrix4f16, c, r: int) -> f16 {
|
||||
return matrix3_determinant(cut_down)
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_minor_f32 :: proc "contextless" (m: Matrix4f32, c, r: int) -> f32 {
|
||||
matrix4_minor_f32 :: proc "contextless" (m: Matrix4f32, c, r: int) -> f32 #no_bounds_check {
|
||||
cut_down: Matrix3f32
|
||||
for i in 0..<3 {
|
||||
col := i if i < c else i+1
|
||||
@@ -1770,7 +1807,7 @@ matrix4_minor_f32 :: proc "contextless" (m: Matrix4f32, c, r: int) -> f32 {
|
||||
return matrix3_determinant(cut_down)
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_minor_f64 :: proc "contextless" (m: Matrix4f64, c, r: int) -> f64 {
|
||||
matrix4_minor_f64 :: proc "contextless" (m: Matrix4f64, c, r: int) -> f64 #no_bounds_check {
|
||||
cut_down: Matrix3f64
|
||||
for i in 0..<3 {
|
||||
col := i if i < c else i+1
|
||||
@@ -1817,7 +1854,7 @@ matrix4_cofactor :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_adjoint_f16 :: proc "contextless" (m: Matrix4f16) -> (adjoint: Matrix4f16) {
|
||||
matrix4_adjoint_f16 :: proc "contextless" (m: Matrix4f16) -> (adjoint: Matrix4f16) #no_bounds_check {
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
adjoint[i][j] = matrix4_cofactor(m, i, j)
|
||||
@@ -1826,7 +1863,7 @@ matrix4_adjoint_f16 :: proc "contextless" (m: Matrix4f16) -> (adjoint: Matrix4f1
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_adjoint_f32 :: proc "contextless" (m: Matrix4f32) -> (adjoint: Matrix4f32) {
|
||||
matrix4_adjoint_f32 :: proc "contextless" (m: Matrix4f32) -> (adjoint: Matrix4f32) #no_bounds_check {
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
adjoint[i][j] = matrix4_cofactor(m, i, j)
|
||||
@@ -1835,7 +1872,7 @@ matrix4_adjoint_f32 :: proc "contextless" (m: Matrix4f32) -> (adjoint: Matrix4f3
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_adjoint_f64 :: proc "contextless" (m: Matrix4f64) -> (adjoint: Matrix4f64) {
|
||||
matrix4_adjoint_f64 :: proc "contextless" (m: Matrix4f64) -> (adjoint: Matrix4f64) #no_bounds_check {
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
adjoint[i][j] = matrix4_cofactor(m, i, j)
|
||||
@@ -1851,7 +1888,7 @@ matrix4_adjoint :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_determinant_f16 :: proc "contextless" (m: Matrix4f16) -> (determinant: f16) {
|
||||
matrix4_determinant_f16 :: proc "contextless" (m: Matrix4f16) -> (determinant: f16) #no_bounds_check {
|
||||
adjoint := matrix4_adjoint(m)
|
||||
for i in 0..<4 {
|
||||
determinant += m[i][0] * adjoint[i][0]
|
||||
@@ -1859,7 +1896,7 @@ matrix4_determinant_f16 :: proc "contextless" (m: Matrix4f16) -> (determinant: f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_determinant_f32 :: proc "contextless" (m: Matrix4f32) -> (determinant: f32) {
|
||||
matrix4_determinant_f32 :: proc "contextless" (m: Matrix4f32) -> (determinant: f32) #no_bounds_check {
|
||||
adjoint := matrix4_adjoint(m)
|
||||
for i in 0..<4 {
|
||||
determinant += m[i][0] * adjoint[i][0]
|
||||
@@ -1867,7 +1904,7 @@ matrix4_determinant_f32 :: proc "contextless" (m: Matrix4f32) -> (determinant: f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_determinant_f64 :: proc "contextless" (m: Matrix4f64) -> (determinant: f64) {
|
||||
matrix4_determinant_f64 :: proc "contextless" (m: Matrix4f64) -> (determinant: f64) #no_bounds_check {
|
||||
adjoint := matrix4_adjoint(m)
|
||||
for i in 0..<4 {
|
||||
determinant += m[i][0] * adjoint[i][0]
|
||||
@@ -1882,7 +1919,7 @@ matrix4_determinant :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_inverse_transpose_f16 :: proc "contextless" (m: Matrix4f16) -> (inverse_transpose: Matrix4f16) {
|
||||
matrix4_inverse_transpose_f16 :: proc "contextless" (m: Matrix4f16) -> (inverse_transpose: Matrix4f16) #no_bounds_check {
|
||||
adjoint := matrix4_adjoint(m)
|
||||
determinant: f16 = 0
|
||||
for i in 0..<4 {
|
||||
@@ -1897,7 +1934,7 @@ matrix4_inverse_transpose_f16 :: proc "contextless" (m: Matrix4f16) -> (inverse_
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_inverse_transpose_f32 :: proc "contextless" (m: Matrix4f32) -> (inverse_transpose: Matrix4f32) {
|
||||
matrix4_inverse_transpose_f32 :: proc "contextless" (m: Matrix4f32) -> (inverse_transpose: Matrix4f32) #no_bounds_check {
|
||||
adjoint := matrix4_adjoint(m)
|
||||
determinant: f32 = 0
|
||||
for i in 0..<4 {
|
||||
@@ -1912,7 +1949,7 @@ matrix4_inverse_transpose_f32 :: proc "contextless" (m: Matrix4f32) -> (inverse_
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_inverse_transpose_f64 :: proc "contextless" (m: Matrix4f64) -> (inverse_transpose: Matrix4f64) {
|
||||
matrix4_inverse_transpose_f64 :: proc "contextless" (m: Matrix4f64) -> (inverse_transpose: Matrix4f64) #no_bounds_check {
|
||||
adjoint := matrix4_adjoint(m)
|
||||
determinant: f64 = 0
|
||||
for i in 0..<4 {
|
||||
@@ -1934,7 +1971,7 @@ matrix4_inverse_transpose :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_translate_f16 :: proc "contextless" (v: Vector3f16) -> Matrix4f16 {
|
||||
matrix4_translate_f16 :: proc "contextless" (v: Vector3f16) -> Matrix4f16 #no_bounds_check {
|
||||
m := MATRIX4F16_IDENTITY
|
||||
m[3][0] = v[0]
|
||||
m[3][1] = v[1]
|
||||
@@ -1942,7 +1979,7 @@ matrix4_translate_f16 :: proc "contextless" (v: Vector3f16) -> Matrix4f16 {
|
||||
return m
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_translate_f32 :: proc "contextless" (v: Vector3f32) -> Matrix4f32 {
|
||||
matrix4_translate_f32 :: proc "contextless" (v: Vector3f32) -> Matrix4f32 #no_bounds_check {
|
||||
m := MATRIX4F32_IDENTITY
|
||||
m[3][0] = v[0]
|
||||
m[3][1] = v[1]
|
||||
@@ -1950,7 +1987,7 @@ matrix4_translate_f32 :: proc "contextless" (v: Vector3f32) -> Matrix4f32 {
|
||||
return m
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_translate_f64 :: proc "contextless" (v: Vector3f64) -> Matrix4f64 {
|
||||
matrix4_translate_f64 :: proc "contextless" (v: Vector3f64) -> Matrix4f64 #no_bounds_check {
|
||||
m := MATRIX4F64_IDENTITY
|
||||
m[3][0] = v[0]
|
||||
m[3][1] = v[1]
|
||||
@@ -1965,7 +2002,7 @@ matrix4_translate :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) -> Matrix4f16 {
|
||||
matrix4_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) -> Matrix4f16 #no_bounds_check {
|
||||
c := math.cos(angle_radians)
|
||||
s := math.sin(angle_radians)
|
||||
|
||||
@@ -1992,7 +2029,7 @@ matrix4_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) ->
|
||||
return rot
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) -> Matrix4f32 {
|
||||
matrix4_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) -> Matrix4f32 #no_bounds_check {
|
||||
c := math.cos(angle_radians)
|
||||
s := math.sin(angle_radians)
|
||||
|
||||
@@ -2019,7 +2056,7 @@ matrix4_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) ->
|
||||
return rot
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_rotate_f64 :: proc "contextless" (angle_radians: f64, v: Vector3f64) -> Matrix4f64 {
|
||||
matrix4_rotate_f64 :: proc "contextless" (angle_radians: f64, v: Vector3f64) -> Matrix4f64 #no_bounds_check {
|
||||
c := math.cos(angle_radians)
|
||||
s := math.sin(angle_radians)
|
||||
|
||||
@@ -2053,7 +2090,7 @@ matrix4_rotate :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_scale_f16 :: proc "contextless" (v: Vector3f16) -> (m: Matrix4f16) {
|
||||
matrix4_scale_f16 :: proc "contextless" (v: Vector3f16) -> (m: Matrix4f16) #no_bounds_check {
|
||||
m[0][0] = v[0]
|
||||
m[1][1] = v[1]
|
||||
m[2][2] = v[2]
|
||||
@@ -2061,7 +2098,7 @@ matrix4_scale_f16 :: proc "contextless" (v: Vector3f16) -> (m: Matrix4f16) {
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_scale_f32 :: proc "contextless" (v: Vector3f32) -> (m: Matrix4f32) {
|
||||
matrix4_scale_f32 :: proc "contextless" (v: Vector3f32) -> (m: Matrix4f32) #no_bounds_check {
|
||||
m[0][0] = v[0]
|
||||
m[1][1] = v[1]
|
||||
m[2][2] = v[2]
|
||||
@@ -2069,7 +2106,7 @@ matrix4_scale_f32 :: proc "contextless" (v: Vector3f32) -> (m: Matrix4f32) {
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_scale_f64 :: proc "contextless" (v: Vector3f64) -> (m: Matrix4f64) {
|
||||
matrix4_scale_f64 :: proc "contextless" (v: Vector3f64) -> (m: Matrix4f64) #no_bounds_check {
|
||||
m[0][0] = v[0]
|
||||
m[1][1] = v[1]
|
||||
m[2][2] = v[2]
|
||||
@@ -2188,7 +2225,7 @@ matrix4_look_at_from_fru :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_perspective_f16 :: proc "contextless" (fovy, aspect, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
|
||||
matrix4_perspective_f16 :: proc "contextless" (fovy, aspect, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) #no_bounds_check {
|
||||
tan_half_fovy := math.tan(0.5 * fovy)
|
||||
m[0, 0] = 1 / (aspect*tan_half_fovy)
|
||||
m[1, 1] = 1 / (tan_half_fovy)
|
||||
@@ -2203,7 +2240,7 @@ matrix4_perspective_f16 :: proc "contextless" (fovy, aspect, near, far: f16, fli
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_perspective_f32 :: proc "contextless" (fovy, aspect, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) {
|
||||
matrix4_perspective_f32 :: proc "contextless" (fovy, aspect, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) #no_bounds_check {
|
||||
tan_half_fovy := math.tan(0.5 * fovy)
|
||||
m[0, 0] = 1 / (aspect*tan_half_fovy)
|
||||
m[1, 1] = 1 / (tan_half_fovy)
|
||||
@@ -2218,7 +2255,7 @@ matrix4_perspective_f32 :: proc "contextless" (fovy, aspect, near, far: f32, fli
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_perspective_f64 :: proc "contextless" (fovy, aspect, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) {
|
||||
matrix4_perspective_f64 :: proc "contextless" (fovy, aspect, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) #no_bounds_check {
|
||||
tan_half_fovy := math.tan(0.5 * fovy)
|
||||
m[0, 0] = 1 / (aspect*tan_half_fovy)
|
||||
m[1, 1] = 1 / (tan_half_fovy)
|
||||
@@ -2241,7 +2278,7 @@ matrix4_perspective :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix_ortho3d_f16 :: proc "contextless" (left, right, bottom, top, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
|
||||
matrix_ortho3d_f16 :: proc "contextless" (left, right, bottom, top, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) #no_bounds_check {
|
||||
m[0, 0] = +2 / (right - left)
|
||||
m[1, 1] = +2 / (top - bottom)
|
||||
m[2, 2] = +2 / (far - near)
|
||||
@@ -2257,7 +2294,7 @@ matrix_ortho3d_f16 :: proc "contextless" (left, right, bottom, top, near, far: f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix_ortho3d_f32 :: proc "contextless" (left, right, bottom, top, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) {
|
||||
matrix_ortho3d_f32 :: proc "contextless" (left, right, bottom, top, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) #no_bounds_check {
|
||||
m[0, 0] = +2 / (right - left)
|
||||
m[1, 1] = +2 / (top - bottom)
|
||||
m[2, 2] = +2 / (far - near)
|
||||
@@ -2273,7 +2310,7 @@ matrix_ortho3d_f32 :: proc "contextless" (left, right, bottom, top, near, far: f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix_ortho3d_f64 :: proc "contextless" (left, right, bottom, top, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) {
|
||||
matrix_ortho3d_f64 :: proc "contextless" (left, right, bottom, top, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) #no_bounds_check {
|
||||
m[0, 0] = +2 / (right - left)
|
||||
m[1, 1] = +2 / (top - bottom)
|
||||
m[2, 2] = +2 / (far - near)
|
||||
@@ -2297,7 +2334,7 @@ matrix_ortho3d :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_infinite_perspective_f16 :: proc "contextless" (fovy, aspect, near: f16, flip_z_axis := true) -> (m: Matrix4f16) {
|
||||
matrix4_infinite_perspective_f16 :: proc "contextless" (fovy, aspect, near: f16, flip_z_axis := true) -> (m: Matrix4f16) #no_bounds_check {
|
||||
tan_half_fovy := math.tan(0.5 * fovy)
|
||||
m[0, 0] = 1 / (aspect*tan_half_fovy)
|
||||
m[1, 1] = 1 / (tan_half_fovy)
|
||||
@@ -2312,7 +2349,7 @@ matrix4_infinite_perspective_f16 :: proc "contextless" (fovy, aspect, near: f16,
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_infinite_perspective_f32 :: proc "contextless" (fovy, aspect, near: f32, flip_z_axis := true) -> (m: Matrix4f32) {
|
||||
matrix4_infinite_perspective_f32 :: proc "contextless" (fovy, aspect, near: f32, flip_z_axis := true) -> (m: Matrix4f32) #no_bounds_check {
|
||||
tan_half_fovy := math.tan(0.5 * fovy)
|
||||
m[0, 0] = 1 / (aspect*tan_half_fovy)
|
||||
m[1, 1] = 1 / (tan_half_fovy)
|
||||
@@ -2327,7 +2364,7 @@ matrix4_infinite_perspective_f32 :: proc "contextless" (fovy, aspect, near: f32,
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_infinite_perspective_f64 :: proc "contextless" (fovy, aspect, near: f64, flip_z_axis := true) -> (m: Matrix4f64) {
|
||||
matrix4_infinite_perspective_f64 :: proc "contextless" (fovy, aspect, near: f64, flip_z_axis := true) -> (m: Matrix4f64) #no_bounds_check {
|
||||
tan_half_fovy := math.tan(0.5 * fovy)
|
||||
m[0, 0] = 1 / (aspect*tan_half_fovy)
|
||||
m[1, 1] = 1 / (tan_half_fovy)
|
||||
@@ -2350,19 +2387,19 @@ matrix4_infinite_perspective :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix2_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix2f16) {
|
||||
matrix2_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix2f16) #no_bounds_check {
|
||||
m[0, 0], m[1, 0] = f, 0
|
||||
m[0, 1], m[1, 1] = 0, f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix2f32) {
|
||||
matrix2_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix2f32) #no_bounds_check {
|
||||
m[0, 0], m[1, 0] = f, 0
|
||||
m[0, 1], m[1, 1] = 0, f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix2f64) {
|
||||
matrix2_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix2f64) #no_bounds_check {
|
||||
m[0, 0], m[1, 0] = f, 0
|
||||
m[0, 1], m[1, 1] = 0, f
|
||||
return
|
||||
@@ -2375,21 +2412,21 @@ matrix2_from_scalar :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix3_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix3f16) {
|
||||
matrix3_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix3f16) #no_bounds_check {
|
||||
m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
|
||||
m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
|
||||
m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix3f32) {
|
||||
matrix3_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix3f32) #no_bounds_check {
|
||||
m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
|
||||
m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
|
||||
m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix3f64) {
|
||||
matrix3_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix3f64) #no_bounds_check {
|
||||
m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
|
||||
m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
|
||||
m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
|
||||
@@ -2403,7 +2440,7 @@ matrix3_from_scalar :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix4f16) {
|
||||
matrix4_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix4f16) #no_bounds_check {
|
||||
m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
|
||||
m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
|
||||
m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
|
||||
@@ -2411,7 +2448,7 @@ matrix4_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix4f16) {
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix4f32) {
|
||||
matrix4_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix4f32) #no_bounds_check {
|
||||
m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
|
||||
m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
|
||||
m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
|
||||
@@ -2419,7 +2456,7 @@ matrix4_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix4f32) {
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix4f64) {
|
||||
matrix4_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix4f64) #no_bounds_check {
|
||||
m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
|
||||
m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
|
||||
m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
|
||||
@@ -2434,19 +2471,19 @@ matrix4_from_scalar :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix2_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix2f16) {
|
||||
matrix2_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix2f16) #no_bounds_check {
|
||||
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
|
||||
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix2f32) {
|
||||
matrix2_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix2f32) #no_bounds_check {
|
||||
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
|
||||
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix2f64) {
|
||||
matrix2_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix2f64) #no_bounds_check {
|
||||
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
|
||||
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
|
||||
return
|
||||
@@ -2459,19 +2496,19 @@ matrix2_from_matrix3 :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix2_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (r: Matrix2f16) {
|
||||
matrix2_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (r: Matrix2f16) #no_bounds_check {
|
||||
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
|
||||
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (r: Matrix2f32) {
|
||||
matrix2_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (r: Matrix2f32) #no_bounds_check {
|
||||
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
|
||||
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (r: Matrix2f64) {
|
||||
matrix2_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (r: Matrix2f64) #no_bounds_check {
|
||||
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
|
||||
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
|
||||
return
|
||||
@@ -2484,21 +2521,21 @@ matrix2_from_matrix4 :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix3_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix3f16) {
|
||||
matrix3_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix3f16) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
|
||||
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
|
||||
r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix3f32) {
|
||||
matrix3_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix3f32) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
|
||||
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
|
||||
r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_from_matrix2_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix3f64) {
|
||||
matrix3_from_matrix2_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix3f64) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
|
||||
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
|
||||
r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
|
||||
@@ -2512,21 +2549,21 @@ matrix3_from_matrix2 :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix3_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (r: Matrix3f16) {
|
||||
matrix3_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (r: Matrix3f16) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
|
||||
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
|
||||
r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (r: Matrix3f32) {
|
||||
matrix3_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (r: Matrix3f32) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
|
||||
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
|
||||
r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (r: Matrix3f64) {
|
||||
matrix3_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (r: Matrix3f64) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
|
||||
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
|
||||
r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
|
||||
@@ -2540,7 +2577,7 @@ matrix3_from_matrix4 :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix4f16) {
|
||||
matrix4_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix4f16) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
|
||||
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
|
||||
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
|
||||
@@ -2548,7 +2585,7 @@ matrix4_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix4f16
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix4f32) {
|
||||
matrix4_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix4f32) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
|
||||
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
|
||||
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
|
||||
@@ -2556,7 +2593,7 @@ matrix4_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix4f32
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_from_matrix2_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix4f64) {
|
||||
matrix4_from_matrix2_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix4f64) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
|
||||
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
|
||||
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
|
||||
@@ -2571,7 +2608,7 @@ matrix4_from_matrix2 :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix4_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix4f16) {
|
||||
matrix4_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix4f16) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
|
||||
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
|
||||
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
|
||||
@@ -2579,7 +2616,7 @@ matrix4_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix4f16
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix4f32) {
|
||||
matrix4_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix4f32) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
|
||||
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
|
||||
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
|
||||
@@ -2587,7 +2624,7 @@ matrix4_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix4f32
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix4_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix4f64) {
|
||||
matrix4_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix4f64) #no_bounds_check {
|
||||
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
|
||||
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
|
||||
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
|
||||
@@ -2673,7 +2710,8 @@ to_quaternion :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix2_orthonormalize_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix2f16) {
|
||||
matrix2_orthonormalize_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix2f16) #no_bounds_check {
|
||||
r = m
|
||||
r[0] = normalize(m[0])
|
||||
|
||||
d0 := dot(r[0], r[1])
|
||||
@@ -2683,7 +2721,8 @@ matrix2_orthonormalize_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix2f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_orthonormalize_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix2f32) {
|
||||
matrix2_orthonormalize_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix2f32) #no_bounds_check {
|
||||
r = m
|
||||
r[0] = normalize(m[0])
|
||||
|
||||
d0 := dot(r[0], r[1])
|
||||
@@ -2693,7 +2732,8 @@ matrix2_orthonormalize_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix2f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix2_orthonormalize_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix2f64) {
|
||||
matrix2_orthonormalize_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix2f64) #no_bounds_check {
|
||||
r = m
|
||||
r[0] = normalize(m[0])
|
||||
|
||||
d0 := dot(r[0], r[1])
|
||||
@@ -2710,7 +2750,8 @@ matrix2_orthonormalize :: proc{
|
||||
|
||||
|
||||
@(require_results)
|
||||
matrix3_orthonormalize_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix3f16) {
|
||||
matrix3_orthonormalize_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix3f16) #no_bounds_check {
|
||||
r = m
|
||||
r[0] = normalize(m[0])
|
||||
|
||||
d0 := dot(r[0], r[1])
|
||||
@@ -2725,7 +2766,8 @@ matrix3_orthonormalize_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix3f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_orthonormalize_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix3f32) {
|
||||
matrix3_orthonormalize_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix3f32) #no_bounds_check {
|
||||
r = m
|
||||
r[0] = normalize(m[0])
|
||||
|
||||
d0 := dot(r[0], r[1])
|
||||
@@ -2740,7 +2782,8 @@ matrix3_orthonormalize_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix3f
|
||||
return
|
||||
}
|
||||
@(require_results)
|
||||
matrix3_orthonormalize_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix3f64) {
|
||||
matrix3_orthonormalize_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix3f64) #no_bounds_check {
|
||||
r = m
|
||||
r[0] = normalize(m[0])
|
||||
|
||||
d0 := dot(r[0], r[1])
|
||||
|
||||
+164
-31
@@ -644,42 +644,175 @@ trunc :: proc{
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
round_f16 :: proc "contextless" (x: f16) -> f16 {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
|
||||
}
|
||||
@(require_results)
|
||||
round_f16le :: proc "contextless" (x: f16le) -> f16le {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
|
||||
}
|
||||
@(require_results)
|
||||
round_f16be :: proc "contextless" (x: f16be) -> f16be {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
|
||||
round_f16 :: proc "contextless" (x: f16) -> f16 {
|
||||
// origin: Go /src/math/floor.go
|
||||
//
|
||||
// Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "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 THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS 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.
|
||||
|
||||
mask :: F16_MASK
|
||||
shift :: F16_SHIFT
|
||||
bias :: F16_BIAS
|
||||
|
||||
bits := transmute(u16)x
|
||||
e := (bits >> shift) & mask
|
||||
|
||||
if e < bias {
|
||||
bits &= 0x8000
|
||||
if e == bias - 1 {
|
||||
bits |= transmute(u16)f16(1)
|
||||
}
|
||||
} else if e < bias + shift {
|
||||
half :: 1 << (shift - 1)
|
||||
mantissa :: (1 << shift) - 1
|
||||
e -= bias
|
||||
bits += half >> e
|
||||
bits &~= mantissa >> e
|
||||
}
|
||||
|
||||
return transmute(f16)bits
|
||||
}
|
||||
@(require_results) round_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(round_f16(f16(x))) }
|
||||
@(require_results) round_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(round_f16(f16(x))) }
|
||||
|
||||
@(require_results)
|
||||
round_f32 :: proc "contextless" (x: f32) -> f32 {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
|
||||
round_f32 :: proc "contextless" (x: f32) -> f32 {
|
||||
// origin: Go /src/math/floor.go
|
||||
//
|
||||
// Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "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 THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS 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.
|
||||
|
||||
mask :: F32_MASK
|
||||
shift :: F32_SHIFT
|
||||
bias :: F32_BIAS
|
||||
|
||||
bits := transmute(u32)x
|
||||
e := (bits >> shift) & mask
|
||||
|
||||
if e < bias {
|
||||
bits &= 0x8000_0000
|
||||
if e == bias - 1 {
|
||||
bits |= transmute(u32)f32(1)
|
||||
}
|
||||
} else if e < bias + shift {
|
||||
half :: 1 << (shift - 1)
|
||||
mantissa :: (1 << shift) - 1
|
||||
e -= bias
|
||||
bits += half >> e
|
||||
bits &~= mantissa >> e
|
||||
}
|
||||
|
||||
return transmute(f32)bits
|
||||
}
|
||||
@(require_results) round_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(round_f32(f32(x))) }
|
||||
@(require_results) round_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(round_f32(f32(x))) }
|
||||
|
||||
@(require_results)
|
||||
round_f32le :: proc "contextless" (x: f32le) -> f32le {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
|
||||
}
|
||||
@(require_results)
|
||||
round_f32be :: proc "contextless" (x: f32be) -> f32be {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
|
||||
}
|
||||
@(require_results)
|
||||
round_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
|
||||
}
|
||||
@(require_results)
|
||||
round_f64le :: proc "contextless" (x: f64le) -> f64le {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
|
||||
}
|
||||
@(require_results)
|
||||
round_f64be :: proc "contextless" (x: f64be) -> f64be {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
|
||||
round_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
// origin: Go /src/math/floor.go
|
||||
//
|
||||
// Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "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 THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS 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.
|
||||
|
||||
mask :: F64_MASK
|
||||
shift :: F64_SHIFT
|
||||
bias :: F64_BIAS
|
||||
|
||||
bits := transmute(u64)x
|
||||
e := (bits >> shift) & mask
|
||||
|
||||
if e < bias {
|
||||
bits &= 0x8000_0000_0000_0000
|
||||
if e == bias - 1 {
|
||||
bits |= transmute(u64)f64(1)
|
||||
}
|
||||
} else if e < bias + shift {
|
||||
half :: 1 << (shift - 1)
|
||||
mantissa :: (1 << shift) - 1
|
||||
e -= bias
|
||||
bits += half >> e
|
||||
bits &~= mantissa >> e
|
||||
}
|
||||
|
||||
return transmute(f64)bits
|
||||
}
|
||||
@(require_results) round_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(round_f64(f64(x))) }
|
||||
@(require_results) round_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(round_f64(f64(x))) }
|
||||
round :: proc{
|
||||
round_f16, round_f16le, round_f16be,
|
||||
round_f32, round_f32le, round_f32be,
|
||||
@@ -2353,4 +2486,4 @@ INF_F64 :: f64(0h7FF0_0000_0000_0000)
|
||||
NEG_INF_F64 :: f64(0hFFF0_0000_0000_0000)
|
||||
|
||||
SNAN_F64 :: f64(0h7FF0_0000_0000_0001)
|
||||
QNAN_F64 :: f64(0h7FF8_0000_0000_0001)
|
||||
QNAN_F64 :: f64(0h7FF8_0000_0000_0001)
|
||||
|
||||
@@ -834,3 +834,23 @@ choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
|
||||
}
|
||||
return array[int63_max(n, r)]
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
choice_enum :: proc($T: typeid, r: ^Rand = nil) -> T
|
||||
where
|
||||
intrinsics.type_is_enum(T),
|
||||
size_of(T) <= 8,
|
||||
len(T) == cap(T) /* Only allow contiguous enum types */
|
||||
{
|
||||
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
||||
u64(max(T)) > u64(max(i64)) {
|
||||
i := uint64(r) % u64(len(T))
|
||||
i += u64(min(T))
|
||||
return T(i)
|
||||
} else {
|
||||
i := int63_max(i64(len(T)), r)
|
||||
i += i64(min(T))
|
||||
return T(i)
|
||||
}
|
||||
}
|
||||
+15
-50
@@ -83,11 +83,7 @@ free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_locatio
|
||||
}
|
||||
|
||||
free_with_size :: proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
|
||||
return err
|
||||
return runtime.mem_free_with_size(ptr, byte_count, allocator, loc)
|
||||
}
|
||||
|
||||
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
@@ -130,19 +126,19 @@ query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_locatio
|
||||
|
||||
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(str), len(str), allocator, loc)
|
||||
return runtime.delete_string(str, allocator, loc)
|
||||
}
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free((^byte)(str), allocator, loc)
|
||||
return runtime.delete_cstring(str, allocator, loc)
|
||||
}
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
|
||||
return runtime.delete_dynamic_array(array, loc)
|
||||
}
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
|
||||
return runtime.delete_slice(array, allocator, loc)
|
||||
}
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
|
||||
return runtime.map_free_dynamic(transmute(Raw_Map)m, runtime.map_info(T), loc)
|
||||
return runtime.delete_map(m, loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -161,71 +157,40 @@ new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location)
|
||||
}
|
||||
@(require_results)
|
||||
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
|
||||
data := alloc_bytes(size_of(T), alignment, allocator, loc) or_return
|
||||
t = (^T)(raw_data(data))
|
||||
return
|
||||
return runtime.new_aligned(T, alignment, allocator, loc)
|
||||
}
|
||||
@(require_results)
|
||||
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
|
||||
backing := alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return
|
||||
t = (^T)(raw_data(backing))
|
||||
if t != nil {
|
||||
t^ = data
|
||||
return t, nil
|
||||
}
|
||||
return nil, .Out_Of_Memory
|
||||
return runtime.new_clone(data, allocator, loc)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) {
|
||||
runtime.make_slice_error_loc(loc, len)
|
||||
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return
|
||||
if data == nil && size_of(E) != 0 {
|
||||
return
|
||||
}
|
||||
slice = transmute(T)Raw_Slice{raw_data(data), len}
|
||||
return
|
||||
return runtime.make_aligned(T, len, alignment, allocator, loc)
|
||||
}
|
||||
@(require_results)
|
||||
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return make_aligned(T, len, align_of(E), allocator, loc)
|
||||
return runtime.make_slice(T, len, allocator, loc)
|
||||
}
|
||||
@(require_results)
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc)
|
||||
return runtime.make_dynamic_array(T, allocator, loc)
|
||||
}
|
||||
@(require_results)
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
|
||||
return runtime.make_dynamic_array(T, len, allocator, loc)
|
||||
}
|
||||
@(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) {
|
||||
runtime.make_dynamic_array_error_loc(loc, len, cap)
|
||||
data := 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 {
|
||||
s.len, s.cap = 0, 0
|
||||
}
|
||||
array = transmute(T)s
|
||||
return
|
||||
return runtime.make_dynamic_array(T, len, cap, allocator, loc)
|
||||
}
|
||||
@(require_results)
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1<<runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) {
|
||||
runtime.make_map_expr_error_loc(loc, cap)
|
||||
context.allocator = allocator
|
||||
|
||||
err = reserve_map(&m, cap, loc)
|
||||
return
|
||||
return runtime.make_map(T, cap, allocator, loc)
|
||||
}
|
||||
@(require_results)
|
||||
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
|
||||
runtime.make_slice_error_loc(loc, len)
|
||||
data := alloc_bytes(size_of(E)*len, align_of(E), allocator, loc) or_return
|
||||
if data == nil && size_of(E) != 0 {
|
||||
return
|
||||
}
|
||||
mp = cast(T)raw_data(data)
|
||||
return
|
||||
return runtime.make_multi_pointer(T, len, allocator, loc)
|
||||
}
|
||||
|
||||
make :: proc{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user