mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 11:11:37 -07:00
Compare commits
1689 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1676c643df | |||
| e2bfb024de | |||
| 4d06a54c0c | |||
| 04ae87eaef | |||
| b313d09c2c | |||
| c0d2359a91 | |||
| 51a2f09032 | |||
| f60e8031f2 | |||
| ea42613fec | |||
| 2cbb3d5a24 | |||
| 9e288b7ce8 | |||
| 776b48c10d | |||
| 199dae6cd5 | |||
| e58f45bef7 | |||
| abe122ecb7 | |||
| f8744d87b0 | |||
| 3e7f6b8751 | |||
| 6ffe814ca7 | |||
| eec9be71f6 | |||
| 888913c739 | |||
| 33d96fd28a | |||
| f1e8738af2 | |||
| 9c52a11b1b | |||
| d89c4606bd | |||
| c6903fbcd5 | |||
| 13c8149046 | |||
| 665db0f778 | |||
| 173286de65 | |||
| f2ecda8fec | |||
| 6f1222e9bf | |||
| 84a424f21e | |||
| 9b7710488b | |||
| 9f413862e9 | |||
| b8802d7df7 | |||
| b13dad02a4 | |||
| f045f8d805 | |||
| 30b7c8ad66 | |||
| 21e637d2b3 | |||
| 6c196931d2 | |||
| d7195b0798 | |||
| b40998de9e | |||
| 8c0c327df9 | |||
| 50cbb8a1fc | |||
| ff9d058392 | |||
| 1acc8f438b | |||
| c53426fcb4 | |||
| 5187db525f | |||
| a8bd340267 | |||
| ca1f419dc2 | |||
| fae60a6b88 | |||
| 108558ddfc | |||
| 65b8cfae82 | |||
| 4055c31cf0 | |||
| e88af4e458 | |||
| 79eb7b52d9 | |||
| 83b2bf44c4 | |||
| 670f18ad1b | |||
| 9a81716936 | |||
| f013499eea | |||
| d04f732e68 | |||
| 35fd8e7f68 | |||
| 38ff2a3ed9 | |||
| bd502d16bc | |||
| a11e17fbc3 | |||
| 8b3b659433 | |||
| f33228fd6e | |||
| df5b693de8 | |||
| ab98108441 | |||
| 6f80174f84 | |||
| 0ebe81fce2 | |||
| c75dd14308 | |||
| c166b6a21d | |||
| 6ed5cbee12 | |||
| 5b200ccdf8 | |||
| cf0f0c4b31 | |||
| 1399ecb41c | |||
| 339d6cfd41 | |||
| 7bded4f189 | |||
| c7269b9ef0 | |||
| 85688015aa | |||
| 3c6cc575c6 | |||
| 0564cb6483 | |||
| 4be92c7eb8 | |||
| 88e9eb7d0c | |||
| d19fc54c3d | |||
| 5d4291d9fa | |||
| b70cd03e9e | |||
| e91e5e1fe9 | |||
| ae57a49915 | |||
| bfcb527b42 | |||
| a343fb171d | |||
| fa2296a124 | |||
| a996cfc536 | |||
| c1d55b9296 | |||
| 5c18cca1ca | |||
| 78e6cd0c60 | |||
| 176954a6d8 | |||
| ad6b3bd95f | |||
| 762895bc45 | |||
| 592e9afa5f | |||
| a2e0373934 | |||
| fb49841b1d | |||
| 01ea0d6f1e | |||
| bb7f291f5f | |||
| 174fa9b490 | |||
| dda2ed290a | |||
| ee9908b09e | |||
| 66de1856e3 | |||
| ba5f7c4e2a | |||
| 487bd3d942 | |||
| 4fac7a8f27 | |||
| 25dae06b6a | |||
| a1f15c2c69 | |||
| 516f6647b4 | |||
| a7840d50e2 | |||
| cb10af08cb | |||
| 4e49d24df9 | |||
| 68222cb8ab | |||
| 912d29af83 | |||
| f3868ac932 | |||
| 5b42dd7707 | |||
| 51707032d1 | |||
| a0babefe55 | |||
| f3aefbc443 | |||
| a6c779b50e | |||
| 9fa41a97b9 | |||
| cef022539e | |||
| f6dfa33697 | |||
| bc3bf939e0 | |||
| f5e5eac3b9 | |||
| d50786bd30 | |||
| 0ccbea17aa | |||
| 136d50a745 | |||
| babfba5e8f | |||
| 846f8377b2 | |||
| 77d4409549 | |||
| 7f3540b7f5 | |||
| 3ad2cde833 | |||
| 910799cc5f | |||
| 6c0192083e | |||
| c60d7842cd | |||
| d7eaf0f87b | |||
| bb4329711c | |||
| 618d3bf62f | |||
| cf8a4b9812 | |||
| 4db533ff71 | |||
| f28e3276e7 | |||
| 026540040d | |||
| 8518d3b232 | |||
| 1c1f5e2231 | |||
| bdedfb1071 | |||
| 92ed9e0b94 | |||
| 5c10b35df7 | |||
| 0668811397 | |||
| 6bb6344208 | |||
| 2f7bd154a2 | |||
| 20c5033b38 | |||
| 20fe6d102a | |||
| 4e30a64d9f | |||
| c48ef7d70b | |||
| e079a7009d | |||
| f383bf3136 | |||
| 609ddf28b7 | |||
| 34f1bda57c | |||
| f137b927b6 | |||
| 2185dada56 | |||
| 0b08080119 | |||
| 432b2b19e9 | |||
| 952f294bce | |||
| c23274adb0 | |||
| 833f9dd037 | |||
| 1ff8b97dae | |||
| 70451f9335 | |||
| 1f438d4e6c | |||
| 421d45a7a7 | |||
| 20e7b5c88a | |||
| 7092273a8f | |||
| d0e8a735ba | |||
| 208226dba2 | |||
| f308f37ba1 | |||
| c2610cb75e | |||
| 59e9df2609 | |||
| 66b5a35ec3 | |||
| f3f6c12a7c | |||
| e331b0647e | |||
| 35502816c7 | |||
| 7ec0236fbf | |||
| 0fd43c1a0b | |||
| 06337129d8 | |||
| 337780497d | |||
| 10deb2e88b | |||
| b95ca80f85 | |||
| 83d880a94a | |||
| cde6a2f7a5 | |||
| c2f5cbdeb4 | |||
| 8e57511ffa | |||
| 12d19d21c4 | |||
| 7002c94a63 | |||
| 57e69ea392 | |||
| 09f936b04d | |||
| 140c00aa0c | |||
| 808ea30b48 | |||
| 63d6c08d90 | |||
| 10e4de3c01 | |||
| 8ac12886ed | |||
| 63cc8a80a0 | |||
| 1549d01bf7 | |||
| b168bf9460 | |||
| 0203bb657e | |||
| 53f0c6ef1a | |||
| 4c4480104d | |||
| 5c72974167 | |||
| f21e9ee712 | |||
| 81dd727f75 | |||
| 3b54015e80 | |||
| b032d5af87 | |||
| 209a155608 | |||
| d8e77cd738 | |||
| 95d4ce4aa3 | |||
| 39393cca92 | |||
| acadbe050c | |||
| 8fcf2f5dca | |||
| 831a86599e | |||
| 233b32fd3e | |||
| 3c5124ce68 | |||
| a8d78660ee | |||
| cc1df9591f | |||
| 54a326f046 | |||
| 3d9d85121d | |||
| a31d23a32a | |||
| 084f431aa5 | |||
| 7002f0a7d7 | |||
| 3ec70c5517 | |||
| d9f293b281 | |||
| 8c1499dbc2 | |||
| 7d2eedee73 | |||
| eba35a8f7d | |||
| e967f2ca2c | |||
| 438713af20 | |||
| 568869077e | |||
| f25a3f2a7d | |||
| 5609221831 | |||
| f3432e6bb5 | |||
| 43b350c590 | |||
| c2c66aad60 | |||
| d7681d5b06 | |||
| c902615192 | |||
| 2895830ce6 | |||
| 1eef9552b4 | |||
| 577fa2d29b | |||
| 72fcf16a39 | |||
| b9d523e0b2 | |||
| f3d225ca4f | |||
| d84d2f85e8 | |||
| 10f1d8c604 | |||
| 184d1c57b1 | |||
| dfbe68bcfe | |||
| 3049e07f72 | |||
| da54d0ec8c | |||
| b57edb89eb | |||
| e43eccbb91 | |||
| e48f41165c | |||
| 9eb4cbcbd2 | |||
| 2612f241c9 | |||
| 0f1153fae2 | |||
| b84561f2b8 | |||
| f7e78e2671 | |||
| d10a2bc5d5 | |||
| 94fda3d48d | |||
| 5cf4f565d6 | |||
| c20b5cbd10 | |||
| 115612620f | |||
| a5bf3b0bc5 | |||
| 5c647e2f61 | |||
| 06884da42b | |||
| 6e7179d8f3 | |||
| e85f1dd9fb | |||
| 9ac94e621b | |||
| db8d119cad | |||
| 836c325021 | |||
| 3bb31093fa | |||
| 214b43974d | |||
| 55556aea77 | |||
| 223897d224 | |||
| 542e45de26 | |||
| 1fa9488a4d | |||
| b1196bd659 | |||
| 57167be2a6 | |||
| 846930a07f | |||
| 0cc67ff5e3 | |||
| a86574da84 | |||
| 5a6836ab99 | |||
| 43432f92ec | |||
| d1499f3f78 | |||
| fff23e2bbb | |||
| 33895b6d92 | |||
| e10105a780 | |||
| 5451c9672d | |||
| 4eba2bb8d9 | |||
| 2a58bceb56 | |||
| fdcf08410c | |||
| 5142955f00 | |||
| 500150b12a | |||
| 50ddd8dd26 | |||
| 6c6de2a07d | |||
| 01912b6ba5 | |||
| be2c7b5c9b | |||
| ed60ed3bae | |||
| 23cb96de02 | |||
| a2c771876e | |||
| b5b329378f | |||
| f7b18cd86e | |||
| d74e4b427d | |||
| 22dc020647 | |||
| e8485ee7e7 | |||
| c516fb947f | |||
| 3aa0a733f3 | |||
| 4e080057fb | |||
| 9c1f270bd5 | |||
| e46d87b221 | |||
| 5bc866e420 | |||
| 5af7004f44 | |||
| 01e8e682c0 | |||
| 2ef6544ca2 | |||
| 9921ac01cc | |||
| 7057f5fc11 | |||
| f17a9dd5e7 | |||
| ec3394b8da | |||
| 0cca42a1f4 | |||
| 85edcf9cc2 | |||
| 3b842ffe29 | |||
| 6c0e2e2a53 | |||
| 42371f7aea | |||
| 286f782e5e | |||
| 58fc305b11 | |||
| 7e0c359f99 | |||
| f50399e394 | |||
| 7bc21c6691 | |||
| dd56c85e55 | |||
| 9e2a847ebc | |||
| daef39a206 | |||
| 5d496cdcda | |||
| f27f595549 | |||
| 536e0a8c29 | |||
| bc18310107 | |||
| 3fdb3dd767 | |||
| d224679619 | |||
| 2dd181e663 | |||
| f002857edc | |||
| 97739da85a | |||
| 6c14586fff | |||
| 0c45a46aab | |||
| d1fc9d3073 | |||
| 2fb351bf04 | |||
| dc832ad49f | |||
| eef44b11f3 | |||
| bb4f108487 | |||
| ccb38c3dc6 | |||
| cc81057d21 | |||
| 8b4b81fdeb | |||
| 4cb46f5631 | |||
| f4ad4c7aa6 | |||
| 37dda30c49 | |||
| 8fb718245a | |||
| a4cb6f96ea | |||
| 56e3b7cb7d | |||
| ae1f5d2181 | |||
| b4df272eb5 | |||
| dca2fbccff | |||
| d48d3bfa87 | |||
| 8559790bd8 | |||
| 37c6279031 | |||
| 0d4e710b96 | |||
| 205aa10b88 | |||
| 6f1cc3946b | |||
| 253ecd55a0 | |||
| c9e31dc90d | |||
| 3d06322d4a | |||
| 85e6efdf16 | |||
| 6b89ff43ea | |||
| 4cdc55af91 | |||
| 4b289f904c | |||
| 53c70da0b8 | |||
| 6f20b5bb59 | |||
| 96ab17ecfc | |||
| 18bde22b26 | |||
| e61aad925b | |||
| 5d190b15d7 | |||
| fe442d7c0c | |||
| 97d1a67871 | |||
| bac96cf2ad | |||
| 7e0cc0af25 | |||
| 1d29d9be25 | |||
| 0e91e63043 | |||
| 0cf37bde8b | |||
| 173799527a | |||
| 5931e2383b | |||
| e4743b15b1 | |||
| 9f95d6fa65 | |||
| 982a1aebb3 | |||
| cec049b7d3 | |||
| dc323cfa1d | |||
| 0afa226a93 | |||
| 1146604344 | |||
| 89c2e1a5fa | |||
| 971d498e79 | |||
| 6e7a50c02f | |||
| 6aaab4988e | |||
| d9b0c05acf | |||
| 47f637d23b | |||
| 59f55a2119 | |||
| 8bac82320f | |||
| 14bf20320a | |||
| b99940f33a | |||
| 81495068b9 | |||
| 6985181961 | |||
| 97717d65ef | |||
| 8023c8abc7 | |||
| 2d3f59d9a7 | |||
| be8de4a1ff | |||
| 18ad6c33ef | |||
| 0e27b27b81 | |||
| 10a311092b | |||
| 18463d68d4 | |||
| 74d3bcec05 | |||
| df233aee94 | |||
| 335b724209 | |||
| b2fdb881eb | |||
| 6ade9acc4d | |||
| 2081f8fcd6 | |||
| 964ab4814c | |||
| 7a032cf9f9 | |||
| 694c13fe86 | |||
| 8bd16c32f3 | |||
| be9b935953 | |||
| 9e69452327 | |||
| 234d529867 | |||
| dd8b71e353 | |||
| d24bebdb9e | |||
| 41a18f078d | |||
| 3978e7e1ca | |||
| b758c696f2 | |||
| d6a8216ce4 | |||
| 2720f64c06 | |||
| de2ebdd5cc | |||
| 4e39629a9a | |||
| 78a8da5fea | |||
| d5886c1572 | |||
| 7cc759a855 | |||
| dd6337224f | |||
| d2bac0c35e | |||
| 8c7f3fd1e6 | |||
| ae3deea153 | |||
| 40bea95fb0 | |||
| 0ad448f1c7 | |||
| 4911df9f99 | |||
| a223340c44 | |||
| 9c9c2b483c | |||
| 819345caa6 | |||
| 11ceb3973d | |||
| 36263399a0 | |||
| ff0f0c447f | |||
| aa681932a9 | |||
| 09e1c0fa27 | |||
| 957ef8e8fe | |||
| 2e11a8da5b | |||
| e9cfcf9ecc | |||
| 789ab99c4d | |||
| 0297db6f2e | |||
| 9ce64916e6 | |||
| 1289c96e2c | |||
| ba23bfb7b9 | |||
| 2fae6eda23 | |||
| e53ba3b116 | |||
| 1ed84a064b | |||
| 79019c7a09 | |||
| a1002e6960 | |||
| 62139cb5a4 | |||
| 127b0ba65e | |||
| 80878264b6 | |||
| 9fcba99ca2 | |||
| 03c9212600 | |||
| 5650087aa3 | |||
| 67689ecb21 | |||
| cd13dedb36 | |||
| 10cd294cf2 | |||
| d6cfb60506 | |||
| df0df73540 | |||
| 33f1418dec | |||
| 305510bea0 | |||
| beb698f31d | |||
| 6df21d6a9f | |||
| c5982e52d5 | |||
| bd73b2845b | |||
| da0f722aad | |||
| 904f0407f8 | |||
| fbbb0d7610 | |||
| 3a9b0a22e7 | |||
| c4e0d1efa1 | |||
| 9349dfba8f | |||
| 9692496989 | |||
| 5bc8164274 | |||
| a6cef2e50e | |||
| d262eda91c | |||
| 40f0f5ad8d | |||
| 1c03e68057 | |||
| f1c1cfb6d2 | |||
| ba5e33bc35 | |||
| 80df9fbc65 | |||
| b68ab0dd6d | |||
| 9cf7a31068 | |||
| 5e11ad2e1e | |||
| 07d1a42768 | |||
| ec8221cb5d | |||
| a5342a0126 | |||
| c81fd2e5dd | |||
| 3bd7122959 | |||
| 530401e5ee | |||
| a412d34574 | |||
| ee67a0b9a1 | |||
| ca6a1db757 | |||
| 1fb76ad768 | |||
| e01662c139 | |||
| 63331ef731 | |||
| a40a53b104 | |||
| 9f8d90f466 | |||
| 3d2856db31 | |||
| f4723aea4c | |||
| 76d48b38d3 | |||
| 3cab2592c3 | |||
| 5422a3b17e | |||
| b44b6e7e50 | |||
| 849efff070 | |||
| b022167df1 | |||
| ac9a358c65 | |||
| e799476f90 | |||
| b4f8efcbe6 | |||
| f026753692 | |||
| 71b1cce517 | |||
| d8f0da164b | |||
| 591732f347 | |||
| eee97f7f62 | |||
| 3dd9da1b66 | |||
| e8c0be23f2 | |||
| a30b9b17b3 | |||
| 29b2c04766 | |||
| d869ba7bcd | |||
| 581255bf23 | |||
| b51358a01c | |||
| 323e7a2d02 | |||
| 7654afc2db | |||
| ded8342f3f | |||
| 240fb9b953 | |||
| 4997a43763 | |||
| aa4eb35671 | |||
| d99ba9c073 | |||
| fdd24f787f | |||
| b6abd691f4 | |||
| 8d370fabdd | |||
| df4a0c62ad | |||
| e3e3309a9b | |||
| 7428e52264 | |||
| 20b70c3b7b | |||
| 4247ba67ed | |||
| e738e93da0 | |||
| b78f3a8069 | |||
| 939973acd7 | |||
| ed4cb72b19 | |||
| 70cbffd58b | |||
| 6d0ba8d189 | |||
| b6f3fa6ee1 | |||
| 91037766d2 | |||
| 8bf73950fa | |||
| 4f4793817c | |||
| 0a0440a6e8 | |||
| b9dc81d808 | |||
| 8e7ddccf00 | |||
| a5773f1657 | |||
| 44316401c9 | |||
| b05fbaacda | |||
| 1b4d5b73ab | |||
| d3fbf36df7 | |||
| de819cff94 | |||
| cfae39c29d | |||
| 989641a616 | |||
| fc3f62e3ed | |||
| 6b7c04e046 | |||
| cfeb16f917 | |||
| 9a2d9002e6 | |||
| ea0b02d9b9 | |||
| 0d621511e5 | |||
| e53c858855 | |||
| 8a9f7fc684 | |||
| 51db46551e | |||
| 600b79276a | |||
| a040be957f | |||
| f6fa553572 | |||
| 8310436350 | |||
| f92ffe60e7 | |||
| de72754d7a | |||
| bf712e9355 | |||
| ab9457346d | |||
| 15b440c4f1 | |||
| 1a2c36e482 | |||
| 56737c1431 | |||
| 9ae566adcc | |||
| f2f1330238 | |||
| c4a7739d13 | |||
| 8a8b5c753f | |||
| ad90f416a5 | |||
| 698fcb7813 | |||
| aadb4db211 | |||
| 426a6a9528 | |||
| 50b9c48609 | |||
| 767ed21bfe | |||
| bb9165edd2 | |||
| ad0a413b40 | |||
| 7f6c6945ae | |||
| ca549939f3 | |||
| cdb003bf23 | |||
| a4d2ff05a9 | |||
| 48012ec73c | |||
| e7dc00b758 | |||
| ef1fbbbce6 | |||
| 2a59aebe5b | |||
| 59025b75ba | |||
| 2289b7a33d | |||
| 79ec172797 | |||
| 2e6ad2a711 | |||
| 6be05f315d | |||
| b5aa50aaa4 | |||
| ab91fa6ad5 | |||
| 376327c87b | |||
| f8f91e52e0 | |||
| f4daf46ff4 | |||
| e80bee6867 | |||
| d10d54710c | |||
| 1ec997461d | |||
| ec5fc10988 | |||
| a232c0888c | |||
| cb5a6b531a | |||
| c930a3b4c8 | |||
| 4c14e92952 | |||
| 850d4a1e1b | |||
| 88de3a1c06 | |||
| dc012ed6dd | |||
| c21c993646 | |||
| c3a292a8c7 | |||
| 4044a577cc | |||
| 581d53b96b | |||
| 2bc89260f1 | |||
| c78b83f142 | |||
| e28525e28c | |||
| 73f9d12d47 | |||
| b21cf05d44 | |||
| 107bede9fd | |||
| 75cbb09744 | |||
| 76cf667a29 | |||
| 78ee97ec74 | |||
| c686133172 | |||
| bfcd7a35bf | |||
| 4484a3433d | |||
| 0c4f905d82 | |||
| 77de7ebde5 | |||
| 2ec3fa93b4 | |||
| 9f2d710c35 | |||
| 22b961ea53 | |||
| 9ea45d35db | |||
| 06e8476efc | |||
| 94dbac9a64 | |||
| 97a183f412 | |||
| b2f5b73532 | |||
| 1eac3482a6 | |||
| 6636376a81 | |||
| ed6bf28004 | |||
| 6bc0c611ab | |||
| ba1930eb01 | |||
| 203382461b | |||
| 6456618891 | |||
| 4eb4ae6305 | |||
| 72ae061769 | |||
| 46161f7e19 | |||
| 0c55596f0f | |||
| 5f3bfa66c5 | |||
| 561b725b0e | |||
| 3a4630e6b4 | |||
| abf0fd7efc | |||
| a632db3618 | |||
| a3c81374be | |||
| 6a3ec5eb36 | |||
| 740ba6ad47 | |||
| df32b5b46c | |||
| 085fa199ea | |||
| 412c9a99d5 | |||
| 6e701ef36d | |||
| 24c48d22bc | |||
| 3cb8bb6672 | |||
| b1c2c0ea7a | |||
| 2c498c132e | |||
| 880d330cca | |||
| a2a503847a | |||
| 58f4d533b7 | |||
| d2ff6f424d | |||
| 92f985abd5 | |||
| 3ce17607c6 | |||
| 76277f83c1 | |||
| 2b7529977e | |||
| f4125d2d88 | |||
| 87e50e5e4d | |||
| 86a1c34c3a | |||
| 3f3cc342b4 | |||
| 3bf820cf99 | |||
| f2b4087d80 | |||
| 3b6d72bb94 | |||
| 9080fa4a9d | |||
| 5616ff9a40 | |||
| 73b81184fa | |||
| f8d3f86d8b | |||
| 2f9a410a45 | |||
| 8661457512 | |||
| 5d7b92d391 | |||
| 1d8bc3e917 | |||
| a2ad16b609 | |||
| a3b1ac3133 | |||
| e7b96cf286 | |||
| 01181517dc | |||
| f702c782f1 | |||
| 7203560b06 | |||
| 1baeb9406f | |||
| 17e36bd5e1 | |||
| b6b3377786 | |||
| 13cb894b30 | |||
| 3f935bea25 | |||
| 3e66eec735 | |||
| 277e0ac124 | |||
| 2ccfaa7d4e | |||
| 4bd5de34ea | |||
| 374e71e9b0 | |||
| 07bb93bb5d | |||
| e252d3bedf | |||
| b9efd09d17 | |||
| 507b718cb3 | |||
| 82f9cbecf8 | |||
| a8ac59a6e7 | |||
| 3d389ee028 | |||
| 10c5825715 | |||
| f89ebce807 | |||
| 64601ac439 | |||
| edce27812f | |||
| 193822b45d | |||
| 43640a8b59 | |||
| 0446d9721b | |||
| bae13b6387 | |||
| e48c0eee74 | |||
| 47e9857eb7 | |||
| 559fcfa291 | |||
| 84cee5d9d5 | |||
| 6d354524e2 | |||
| ae6441182d | |||
| a68f0b2d72 | |||
| fdbbf24271 | |||
| df233f72a9 | |||
| bff5a67f79 | |||
| 4f9df50dc1 | |||
| 34187424b8 | |||
| 50503cb405 | |||
| 5e04ddd653 | |||
| 4f5203e661 | |||
| d9ca4eb4d6 | |||
| 5534c031b3 | |||
| 19dc84e300 | |||
| a932168f50 | |||
| 36c22393a4 | |||
| 6d6e840bc2 | |||
| 4b1822ade8 | |||
| b21e7e4518 | |||
| 1f4e5e919f | |||
| c293e88f2e | |||
| 1d147ba993 | |||
| 6ea9ba16e7 | |||
| 286549693e | |||
| 34727f99e3 | |||
| ffe6d81ecd | |||
| 8605833781 | |||
| 4474144c24 | |||
| ef3f448861 | |||
| a882260db6 | |||
| 633157f4f8 | |||
| 9fa69c3d3b | |||
| 743a461aa9 | |||
| fc0291d745 | |||
| 77eaf8e1e4 | |||
| a7adb2fb6e | |||
| 036900da51 | |||
| ed4c9335db | |||
| ca67cf032c | |||
| f907516cbd | |||
| c12c7d5370 | |||
| f7c8b40ea2 | |||
| 15f9795ab0 | |||
| 8982ae34e3 | |||
| e6d3e893a5 | |||
| e008b5a160 | |||
| 3da8fa9b27 | |||
| 32ba5e7ad2 | |||
| 52df80dccd | |||
| 7f845bb165 | |||
| 0e6de5673b | |||
| 7a7b87181d | |||
| c6dc517004 | |||
| 0b61215f7b | |||
| b91c0ec715 | |||
| bad295cf69 | |||
| d2bc41a2df | |||
| 7dbcaf792d | |||
| 2652c2d7a5 | |||
| a2250a5d49 | |||
| 7f8a9587e0 | |||
| 1306c53fb1 | |||
| 3bd1ac4c82 | |||
| dc8d28c383 | |||
| 7adaa4dc2b | |||
| 6d1a91f5b3 | |||
| 17eebf338c | |||
| c543ecd64c | |||
| 34a9f55f37 | |||
| 9aea990184 | |||
| d5b0632e4f | |||
| db169a4334 | |||
| f5cc8bd7bf | |||
| 005d52cab7 | |||
| d1477bcfa7 | |||
| 3092fb2ff3 | |||
| 5eebdebec8 | |||
| 8e4d6b3e5d | |||
| ea9c2fed57 | |||
| ba412fd87b | |||
| 0278ac85a0 | |||
| ff60b752bd | |||
| 832003dd4b | |||
| 9848e883c7 | |||
| 64705ddd1d | |||
| 2a41814985 | |||
| 26ffec845b | |||
| 52e60526ef | |||
| 76b10b5f5d | |||
| b94a7a87fa | |||
| 2b43387a9d | |||
| e76a5d8e12 | |||
| 6d7217f37a | |||
| 17dab04422 | |||
| 29e660b16f | |||
| 31959b0751 | |||
| 1f19610fd6 | |||
| 8f897de267 | |||
| 2855ff6df3 | |||
| deed20dea6 | |||
| a6c5143993 | |||
| 758d1e2a03 | |||
| ce057ff755 | |||
| ad719e7c3a | |||
| bff3426d25 | |||
| 4315033220 | |||
| 1cd89b2da3 | |||
| 658a605c75 | |||
| 7e8b9862b9 | |||
| 07062324d7 | |||
| 2e8f2e6dbc | |||
| 1abd95094d | |||
| 913d802e33 | |||
| bee475c38a | |||
| b4ca99ead9 | |||
| dfe2c0a600 | |||
| fad851d80c | |||
| 832961d539 | |||
| 499c657ffa | |||
| e51bb4ef12 | |||
| 8c6f39a68d | |||
| 09f5713cf8 | |||
| 26c0c6a525 | |||
| 6d9f84ba03 | |||
| 8af08f2153 | |||
| 2944969ca0 | |||
| bd1b54e0db | |||
| fcab5508be | |||
| 0b05650366 | |||
| 96e36c7c39 | |||
| 16c6dbcbe5 | |||
| 92a78c83d9 | |||
| 6b2302fa8b | |||
| ee28945e09 | |||
| 3dcea60f5b | |||
| f126e05034 | |||
| 68b74eb7c7 | |||
| 3b4199a669 | |||
| 562901aedf | |||
| 793117ed63 | |||
| 5b783d6376 | |||
| d3f3528d1d | |||
| 3145935d6b | |||
| 25430333ba | |||
| 2ca2b32dd0 | |||
| a5dde78f08 | |||
| fd415f0b45 | |||
| 507722954c | |||
| 524d23d45d | |||
| e9ee6f5291 | |||
| 7e5342f41f | |||
| 18607e53cb | |||
| ed933b3f21 | |||
| 49fecbdc5e | |||
| f971126183 | |||
| d4ccb69ccc | |||
| 37b4e0de6c | |||
| 737f440c7f | |||
| cba3f1e374 | |||
| a70dde34da | |||
| 410b85b5c7 | |||
| 0ae012ba08 | |||
| fc4eb4152c | |||
| 4f3b5d8dcb | |||
| f76f70c7cf | |||
| 15d783e920 | |||
| 09e4fff5b1 | |||
| 2d89faa17c | |||
| dd9843aa21 | |||
| 882116e358 | |||
| f3adbae1ed | |||
| 278e239973 | |||
| fda803b46a | |||
| 01162e08b5 | |||
| a7ae197a55 | |||
| 3c72cb67d3 | |||
| 37bba4c0a6 | |||
| ab9d1f99fd | |||
| 45124e4d5c | |||
| 7681c43b14 | |||
| 7e43cd7d97 | |||
| c223fc1766 | |||
| 04297bb680 | |||
| 553292ffd0 | |||
| 57862846a2 | |||
| 8e270d3a99 | |||
| ae5cb09041 | |||
| 093b2288c3 | |||
| ffe17a471d | |||
| e9f901b82d | |||
| ed3004f8a0 | |||
| d97df080f9 | |||
| 0e5c7e08fc | |||
| 83523badb7 | |||
| 0a90994403 | |||
| 376906e0ae | |||
| 47c79a2f25 | |||
| 32988b0363 | |||
| 00c138ce9f | |||
| 5676c9e7eb | |||
| 3a469dc13e | |||
| d3c70f2206 | |||
| 14f1793b3e | |||
| 8cecb6b9f5 | |||
| 4a66c3c420 | |||
| c3c88633a5 | |||
| aeaf1199ec | |||
| d4f62f52db | |||
| 384fb76a1b | |||
| 4a04a32e0a | |||
| 196bd735d4 | |||
| 493bc653b5 | |||
| 3d209798c9 | |||
| dd0d61e97c | |||
| 4b9324ff76 | |||
| e81ed9a960 | |||
| 83f7a887b7 | |||
| ad2f1ac24e | |||
| 62d232d798 | |||
| 8906a0120c | |||
| cad753e398 | |||
| fd627dc13b | |||
| 70e8d97ee1 | |||
| cdecb0ccc3 | |||
| 460b5149af | |||
| b6dc253d8b | |||
| e7be9493ba | |||
| ea34f321ed | |||
| 2b5bc1d558 | |||
| e6a7b85da4 | |||
| 6145185478 | |||
| 6630d703f8 | |||
| 9c3cdc4620 | |||
| 345032f804 | |||
| 2abba6e057 | |||
| db5a1b0c78 | |||
| 14cb19c2df | |||
| 46bcd18946 | |||
| 3e5c60f746 | |||
| 0fa487f468 | |||
| 1bec9e5331 | |||
| ba61d911da | |||
| 3b69c6b204 | |||
| d7eabf571c | |||
| ddf9c4a65b | |||
| b3d797598e | |||
| 31c7945444 | |||
| 276e014d18 | |||
| 27f206784c | |||
| 54a6637d38 | |||
| 23be56af59 | |||
| 71df46456a | |||
| cd89d8a3c4 | |||
| 4c62a32b04 | |||
| 5f8137025d | |||
| 1843d52217 | |||
| 454c92dc64 | |||
| 7e33a86d54 | |||
| 197b832992 | |||
| 8f13724a4b | |||
| 746d5fc322 | |||
| ffc45e8cc2 | |||
| bea2f36443 | |||
| f138f71fa6 | |||
| 89b7a3f7ac | |||
| 75e15b05b0 | |||
| 78eb388110 | |||
| df23cf47c6 | |||
| 002ac6a1b7 | |||
| 40e4536887 | |||
| 536bf61323 | |||
| c76bdced55 | |||
| 459ea5f4f6 | |||
| 8e8a075a22 | |||
| db6bd9b358 | |||
| 42ad54c28e | |||
| 1857bc7b02 | |||
| e011d812ca | |||
| 0738822dda | |||
| 2213722776 | |||
| 65dedbb1ca | |||
| 0e69993d39 | |||
| 135091ddbe | |||
| d64e3b672c | |||
| a3bcacee27 | |||
| 855e7beab1 | |||
| edc13c29df | |||
| 21864d8d51 | |||
| d45ff0694d | |||
| d695a8a526 | |||
| 0380a288a9 | |||
| 1d4d0a3e1a | |||
| 9e98494fff | |||
| 86d334282c | |||
| 9cf937fef0 | |||
| f5697dd7f2 | |||
| a23ee1edc1 | |||
| 9dcb5c075a | |||
| 803648be89 | |||
| 3ab5db8297 | |||
| 3380ece4a1 | |||
| 0e5928ff39 | |||
| e6e04fc6c8 | |||
| 753cceea82 | |||
| 32c7e81745 | |||
| 03aec70287 | |||
| e69738c079 | |||
| 1afa7967f2 | |||
| 643e36b87b | |||
| 3d2405ac2c | |||
| 7392a3047a | |||
| 60f4d8f1ec | |||
| 7b42cbea20 | |||
| 4cc597f4df | |||
| 934e66ab3b | |||
| b755609438 | |||
| 83d63e572a | |||
| 5212f62f54 | |||
| 4d0fd4cf19 | |||
| e47953f7ca | |||
| 47f3773146 | |||
| 31c6ecad34 | |||
| af6e53c05c | |||
| 153140eb8f | |||
| 6fef44c041 | |||
| a88d149903 | |||
| f9fc488399 | |||
| bef806bef4 | |||
| 95e9bbf99f | |||
| 5936fa8871 | |||
| 02646b789c | |||
| 09e9dca869 | |||
| 9a43c0672e | |||
| 83a6169463 | |||
| debe2de5fe | |||
| ff7d591ebf | |||
| 7386ca9272 | |||
| fd8b2e0b88 | |||
| c34ae884ad | |||
| 2e7157ae9c | |||
| 441365b388 | |||
| f561147190 | |||
| 2958c1d6aa | |||
| 9dc83bc1b3 | |||
| 88b1b2c629 | |||
| 23bc643a81 | |||
| 41854bacf5 | |||
| cf528431f5 | |||
| e59064dd59 | |||
| 8966294823 | |||
| b8479ea79d | |||
| e2aa8f426d | |||
| 39a0f8c96a | |||
| b647b45ba5 | |||
| 5fe9aa919b | |||
| ff5d6a994b | |||
| ae3b95b194 | |||
| acaae1357c | |||
| f8afda3b22 | |||
| 31f544c258 | |||
| 1c57d1c019 | |||
| 251edf7bc7 | |||
| f77cd5533d | |||
| 416413bebf | |||
| c3809d7b84 | |||
| 42a1c58a80 | |||
| b6abaf739c | |||
| ef98e92e8d | |||
| 768c2684d0 | |||
| 5f2514db63 | |||
| b95ade40c0 | |||
| 340838c878 | |||
| c5d348515d | |||
| 05dd3d490d | |||
| 0cc40db565 | |||
| 546faab0cb | |||
| 83e9a6b417 | |||
| 30bb2382aa | |||
| 61a0b4ec5a | |||
| accb35506f | |||
| 817bc7434d | |||
| 3c2ed3bb69 | |||
| 9cbf46e689 | |||
| ad6ea3d6aa | |||
| cda9fd5271 | |||
| 0c16f27814 | |||
| 19aec13a10 | |||
| e896956275 | |||
| c59c6e98a5 | |||
| 8b1100bf2b | |||
| a3d99765cc | |||
| a724573bb3 | |||
| 25769f139a | |||
| 3edf638cc6 | |||
| de7e612186 | |||
| a571153458 | |||
| ada58c66fa | |||
| 697f8c7ee6 | |||
| b6ebfe4b2c | |||
| bccbdefde9 | |||
| 445ca70521 | |||
| c6ab8f82c8 | |||
| 67ce0ec29f | |||
| 23c3573c30 | |||
| a4308e7246 | |||
| 3439139b1c | |||
| cf246f65ff | |||
| dd84b61cc8 | |||
| b8c4bf2afb | |||
| e870041fe6 | |||
| 6418ec3b21 | |||
| 2bcc7b0064 | |||
| 97be867103 | |||
| fb710f8cbf | |||
| 1553137c23 | |||
| d5384c5aa4 | |||
| 3a81f2ab89 | |||
| b54fc96b1e | |||
| 48af78e469 | |||
| abb26e0bea | |||
| 76edfae0e0 | |||
| a5298e17ec | |||
| cf9f3d5e2d | |||
| 35c90fe124 | |||
| df8bdac33f | |||
| b4f7a527c2 | |||
| 35533a7baa | |||
| 2c9ed7464f | |||
| e190c024fd | |||
| e250475bf9 | |||
| 5db603ded2 | |||
| 78815778ee | |||
| a04d849e30 | |||
| 8c9505505a | |||
| eac74631ec | |||
| 85706d559d | |||
| 67ba05cb7c | |||
| 2f1aeaf757 | |||
| 14a17fb36f | |||
| 1a9ec776cb | |||
| da1edac56d | |||
| 44ec95a983 | |||
| 1502066303 | |||
| 35a826a0fd | |||
| ebb8ca7c26 | |||
| 763de44853 | |||
| 62cc752066 | |||
| 965b962b29 | |||
| 2f3c5336d9 | |||
| 3824937295 | |||
| fc8ddcef5c | |||
| 3165b7cf95 | |||
| 5eea23cf76 | |||
| 2aa783179e | |||
| 24e7356825 | |||
| 2fcba25e50 | |||
| 28bc274449 | |||
| ff5e036773 | |||
| 4dc29d141f | |||
| 8ecee32e1c | |||
| 16786aac78 | |||
| 32b37f3429 | |||
| 5808793cae | |||
| 7e11f3cc4b | |||
| 714ab516c5 | |||
| 498f68c06b | |||
| 070b450768 | |||
| 74174eb4ae | |||
| b190404b21 | |||
| 081a5a52a6 | |||
| fb86c23dbd | |||
| cb6a4ebf60 | |||
| 1bf8328606 | |||
| 6a7f39453b | |||
| 515fd2a228 | |||
| dd3322ac1f | |||
| f16f1d932e | |||
| a3e7b2baa1 | |||
| fadf9b5309 | |||
| f6a087775e | |||
| d5f7e181a0 | |||
| 65f8722afc | |||
| c0479f1564 | |||
| fe0b5bf4e2 | |||
| f20105ddfe | |||
| 6a7d821fcc | |||
| 42ab882db4 | |||
| dcc9e61362 | |||
| 2554c72bb2 | |||
| 49872e40dc | |||
| 849fe01e70 | |||
| 1243b1a58c | |||
| ab3bae5c02 | |||
| 540c5400a0 | |||
| d269dbcd40 | |||
| 18e639f59b | |||
| 6ad262c2df | |||
| 10b97a1b39 | |||
| 56b4e0a3c3 | |||
| 27dbe84f79 | |||
| 0711d4e5fe | |||
| 1e46537959 | |||
| a5e1693774 | |||
| 01e29bf27e | |||
| 63771bc6e8 | |||
| 8516e2e7e3 | |||
| b3c3e41706 | |||
| 59f3a009fa | |||
| 9bc5b84c4d | |||
| f9265c14bf | |||
| 9c1e1a63a2 | |||
| 4dc5839e3d | |||
| fdcb9deaff | |||
| fe6539fad9 | |||
| 0e06383620 | |||
| 6223f48c3f | |||
| f2f20def37 | |||
| 77b91352ae | |||
| 3d7d347192 | |||
| e5868e3205 | |||
| cfbc1a447b | |||
| 1b23dd2257 | |||
| b612edba5a | |||
| d39c05b183 | |||
| 3a3cb521ab | |||
| 5b97ff0b48 | |||
| 2b918ada4b | |||
| b5754b6ed9 | |||
| 07ee23f817 | |||
| ecdaac9921 | |||
| 5ff82fc113 | |||
| 28a816ef25 | |||
| 6bdb210ad8 | |||
| db08847f9a | |||
| 841c428273 | |||
| 6b830f42b6 | |||
| fb01dfe048 | |||
| c7a9c8274f | |||
| cafb6e5587 | |||
| e9ae6e20e8 | |||
| 2ca2dbcc92 | |||
| 0d4642825f | |||
| 8eda756714 | |||
| c85ac955f7 | |||
| 97922406fe | |||
| 76ccce2942 | |||
| 686dbb4421 | |||
| cd6898439e | |||
| 96d7c4ffdf | |||
| 95620aaf2a | |||
| 1d293749c2 | |||
| 2d35a5c1af | |||
| d4ea02a877 | |||
| f19325cbe0 | |||
| 2a325b3da0 | |||
| d57ec4a11d | |||
| f0529535e0 | |||
| 3f59c45740 | |||
| 29ebe0c3c9 | |||
| 6c48670819 | |||
| 51dcbc80c3 | |||
| 9ecbadd457 | |||
| 79f32d7b71 | |||
| 7501cc2f17 | |||
| a390ef41f8 | |||
| bb9c2f7aad | |||
| 6aa80ee8e4 | |||
| 0741bc37cc | |||
| c6ed3fa4b5 | |||
| e277102947 | |||
| 6cf5371d7d | |||
| e15f714660 | |||
| 4f77151ebc | |||
| 9a46463078 | |||
| a0816bb581 | |||
| b33ca6651e | |||
| 315a08f33f | |||
| 50668fa7a6 | |||
| ee260986a9 | |||
| c9bc759624 | |||
| 80f175cdb0 | |||
| 8f03811842 | |||
| 3def94505e | |||
| e30f16b1f3 | |||
| 7df93ea504 | |||
| 6209b02bf9 | |||
| 75b7f2b9fe | |||
| f1521aa980 | |||
| fb0a3ab7c1 | |||
| 8eaafd5242 | |||
| 774951e8c0 | |||
| 5ec93677a0 | |||
| 7e4067c44c | |||
| f2f6c3c67d | |||
| 847b05013f | |||
| d308473075 | |||
| 4334dbe69a | |||
| 8f91e9307c | |||
| 32ec1162bf | |||
| 7cc265e14c | |||
| 6f3e450c50 | |||
| cb1080d56c | |||
| 80bd1eb615 | |||
| fb53402914 | |||
| 731853ce78 | |||
| f0260e9771 | |||
| af612bc7e9 | |||
| d76dd95c0b | |||
| 1cff72ad62 | |||
| 773cfac449 | |||
| b02f2953ac | |||
| 566a750899 | |||
| 1d1d684cbc | |||
| 7a14acaa01 | |||
| 057174497a | |||
| 8c9597b24b | |||
| 72862ce30d | |||
| d0f4cb1de4 | |||
| 17613185e7 | |||
| 00ff1fbca2 | |||
| f15bb0b424 | |||
| f818d0feb1 | |||
| 8ff6f95571 | |||
| 68e5f57e27 | |||
| 38e5e13b3f | |||
| defc1672c3 | |||
| 12f459b5fb | |||
| e6b8f7e77a | |||
| 236b08cb49 | |||
| e4f28de3de | |||
| 6543491148 | |||
| 3cbf9c3719 | |||
| 50188f0308 | |||
| a60b9735a2 | |||
| a032a2ef32 | |||
| f364ac60c2 | |||
| 43763ddfda | |||
| 70ed280c5a | |||
| 0d7cb02386 | |||
| bdf66bb1e1 | |||
| 9b5cfe2677 | |||
| 42033ea808 | |||
| c7ff296bef | |||
| 750ee4ecdb | |||
| ed742846cb | |||
| ed8b20da78 | |||
| c987b84292 | |||
| a9b17b5a37 | |||
| a66f859fb4 | |||
| c46e7eda1d | |||
| 865d88dd56 | |||
| 92e70b9a58 | |||
| 822da9d12d | |||
| b0817d136b | |||
| 53e30e4621 | |||
| dbf42d2469 | |||
| 36c61aeacf | |||
| 289b0422bd | |||
| 78359f0c16 | |||
| 3f8c6a6745 | |||
| 5d653a9d8e | |||
| 7f61a90ea1 | |||
| 982ec1e58b | |||
| 86f831ddd1 | |||
| 069c05669f | |||
| 516065d7c2 | |||
| 6f370fdbf2 | |||
| a60667e900 | |||
| 6889cb6fe2 | |||
| 9b2fe56d14 | |||
| 5d80e24224 | |||
| eec61c3f6f | |||
| dce120258f | |||
| 5752a374ab | |||
| 8dbeed8a9f | |||
| 84d774c7b4 | |||
| e2b36c4004 | |||
| 8453a6cbdb | |||
| 3e465c7e84 | |||
| 92ce7defb1 | |||
| a48317deee | |||
| ebdb3ab43a | |||
| 29ca6ee420 | |||
| 0548db4230 | |||
| aba6d2e52c | |||
| 4ebdb6740e | |||
| d0240b8981 | |||
| 4423bc0706 | |||
| 8c72813b85 | |||
| 08a081ed45 | |||
| b7c78da1fb | |||
| 3257454209 | |||
| 938744b276 | |||
| 84b84d9f7d | |||
| 85f8c8df91 | |||
| c889591333 | |||
| e2f53ee107 | |||
| c771ea9794 | |||
| 94bad4d786 | |||
| 1d7c9cf872 | |||
| 1e17d5d86f | |||
| 1e9b30666f | |||
| b2b79b86f0 | |||
| 3d85013aba | |||
| c94098c2ab | |||
| 9d4fe90356 | |||
| 6e61abc7d0 | |||
| a7138b22a5 | |||
| 7ec88d2430 | |||
| d7200f6144 | |||
| d65d6edb0e | |||
| 3d72e80ccf | |||
| 2dd67dba89 | |||
| 5807214406 | |||
| 23baf56c87 | |||
| beff90e1d1 | |||
| ec63d0bbd2 | |||
| 32eab04d66 | |||
| 682783a2aa | |||
| 46a4927aca | |||
| 9b2e67df67 | |||
| b5c828fe4e | |||
| 6ce5608003 | |||
| db42a2db47 | |||
| cecca96f3d | |||
| f1a126e162 | |||
| 9f0a30e36e | |||
| 517c8ff1dd | |||
| 2b07afaf70 | |||
| 6616882708 | |||
| c9c197ba08 | |||
| 7876660d8c | |||
| db9326f31d | |||
| 27106dd9ae | |||
| 33dc12a61a | |||
| ffd7ca57f1 | |||
| a4ba91a554 | |||
| 44897b5eac | |||
| 8255481204 | |||
| 1e453cf1d7 | |||
| c34a331696 | |||
| cf390bf8b9 | |||
| 07ec93bfeb | |||
| 994ee5a559 | |||
| 50057b0696 | |||
| 00597127dd | |||
| 70d4bc8573 | |||
| bc775afccb | |||
| 504ea7deeb | |||
| 5e2280a787 | |||
| 84e03421d3 | |||
| 0a87ffe0e6 | |||
| e5f961b48f | |||
| 5db505c42f | |||
| 275241f9b4 | |||
| 9246e89c4a | |||
| b56964e465 | |||
| 2e89585c8c | |||
| e230b7110c | |||
| a55f0cfb63 | |||
| de435c9318 | |||
| f40f12d480 | |||
| 8a2c829e07 | |||
| 42b9ce636f | |||
| ca6951d05e | |||
| 446f1f6183 | |||
| d424c84bf9 | |||
| 56d2bbc5b9 | |||
| 2c7bf87998 | |||
| daebaa8b50 | |||
| 9320a31f4d | |||
| 3e04b45106 | |||
| acd5878d66 | |||
| 4439d59105 | |||
| 12c1291805 | |||
| 61bc963e92 | |||
| ae59f214ee | |||
| 6bafa21bee | |||
| 61c581baeb | |||
| 6c4c9aef61 | |||
| 7bed317636 | |||
| 4647081f49 | |||
| 64db286582 | |||
| 1a7a6a9116 | |||
| d1e76ee4f2 | |||
| 9be0d18e5d | |||
| e877525073 | |||
| f09638318f | |||
| bb7703fcec | |||
| 1b28226a67 | |||
| 2b546a598c | |||
| b530ca9a5e | |||
| d232796149 | |||
| e721f26a76 | |||
| 91408cb21f | |||
| eb8b0d7a03 | |||
| 880af47ae7 | |||
| 91949b0992 | |||
| 6a101e69a2 | |||
| 1823b0cead | |||
| 1ec0b79345 | |||
| e814a3693f | |||
| f55fc4cd08 | |||
| f47311f2f6 | |||
| 3f038428a7 | |||
| b9701340b8 | |||
| 82110bf487 | |||
| a75dc9d86d | |||
| bfa23f1352 | |||
| c430a82721 | |||
| cc316a473e | |||
| c213274607 | |||
| c4a2580dfd | |||
| 8a547b5922 | |||
| c67c0789eb | |||
| cefe312ba1 | |||
| d8b1523161 | |||
| 61b02adc50 | |||
| 989ddbd688 | |||
| 96b670af49 | |||
| 359e02bad7 | |||
| 8aadcacc0b | |||
| 615efc7c86 | |||
| dd88104a81 | |||
| 5cb23725ae | |||
| 8c5c45a04c | |||
| 4a552e6326 | |||
| 1f0758708f | |||
| b8dec4268d | |||
| fc920a630f | |||
| ffeac8895d | |||
| cef9632607 | |||
| 76054dddb7 | |||
| 9dc8753a14 | |||
| a805d9a721 | |||
| 6c306f7633 | |||
| 05a86d5296 | |||
| 9422fd311f | |||
| 80360f3f51 | |||
| 321d93bff1 | |||
| 600d19c51b | |||
| ed933bca19 | |||
| a9ea590d24 | |||
| 275d39b59b | |||
| c24454ae70 | |||
| fbc38c78eb | |||
| b0db90de96 | |||
| eb96f9677e | |||
| 0a3b75c5f5 | |||
| 50562440bf | |||
| ce90c3c9ee | |||
| d4bdcd55e1 | |||
| 3f90faf0c9 | |||
| 3d35c5ceb1 | |||
| a674e842d0 | |||
| 23f0fbc376 | |||
| c63f4d68c8 | |||
| 518460af66 | |||
| 39f652de47 | |||
| 483afe462b | |||
| 1296fabe2c | |||
| dc2edd3e79 | |||
| e9c903f1ea | |||
| 83be954efd | |||
| f84bdee1ba | |||
| 5b074ceee5 | |||
| 40eed29527 | |||
| 3d3785a7f1 | |||
| 5df15b5724 | |||
| ee259e4229 | |||
| 36985f8da0 | |||
| eb0faf9602 | |||
| 899cc71990 | |||
| 7be18b4a80 | |||
| 0c9bb9d920 | |||
| 26e3daf5ad | |||
| 0af69f8cda | |||
| 86e26c9a44 | |||
| 541beb615b | |||
| 6646348e1a | |||
| c38d6dc959 | |||
| 924faa58b4 | |||
| 6be104e521 | |||
| e95204908a | |||
| e963fc4d6a | |||
| 1a75a71403 | |||
| 439fc86740 | |||
| 0010e882a7 | |||
| a022f18015 | |||
| cee9561259 | |||
| 3d0cd6f0dc | |||
| adb5928767 | |||
| 23c74bc67b | |||
| a22120fe94 | |||
| ae25eaf10c | |||
| adcfca966e | |||
| d8e34bd9b7 | |||
| 68046d0c08 | |||
| bc2bf1caeb | |||
| d551144841 | |||
| 84540d7aa2 | |||
| 57eedfc4f4 | |||
| 2718ade2bc | |||
| 95f36d4fa5 | |||
| 3accf4048e | |||
| eb05879148 | |||
| a882118c56 | |||
| 57d15ac6e7 | |||
| e3cfdf6982 | |||
| 017fe10762 | |||
| 7bb7a741c6 | |||
| 14351c5bf2 | |||
| 7ef3c87dbb | |||
| b2a2aa15c2 | |||
| 1ec2f8d537 | |||
| 6ded538546 | |||
| 0d1bc05419 | |||
| db2d7a4fdb | |||
| 3fa7dabaa8 | |||
| 1980f32bd6 | |||
| 9ab71ca0da | |||
| 3d06dddb72 | |||
| 9896205a06 | |||
| 8a626ef564 | |||
| 8429943569 | |||
| edd12d505d | |||
| 69f978f22b | |||
| 229c98309e | |||
| c2665462e5 | |||
| 73648bb2d8 | |||
| ba0daaa706 | |||
| dcc5697a48 |
@@ -1,3 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: odin-lang
|
||||
patreon: gingerbill
|
||||
|
||||
@@ -11,8 +11,8 @@ assignees: ''
|
||||
|
||||
Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
|
||||
|
||||
* Operating System:
|
||||
* Please paste `odin version` output:
|
||||
* Operating System & Odin Version:
|
||||
* Please paste `odin report` output:
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
|
||||
+88
-16
@@ -6,10 +6,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM
|
||||
run: sudo apt-get install llvm-11 clang-11 llvm
|
||||
- name: Download LLVM, botan
|
||||
run: sudo apt-get install llvm-11 clang-11 libbotan-2-dev botan
|
||||
- name: build odin
|
||||
run: make release
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
@@ -17,31 +17,53 @@ jobs:
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
run: ./odin check examples/demo -vet
|
||||
timeout-minutes: 10
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
run: ./odin run examples/demo
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo/demo.odin -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: Vendor library tests
|
||||
run: |
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
run: |
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
timeout-minutes: 10
|
||||
- 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 FreeBSD amd64
|
||||
run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for OpenBSD amd64
|
||||
run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
|
||||
timeout-minutes: 10
|
||||
build_macOS:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
- name: Download LLVM, botan and setup PATH
|
||||
run: |
|
||||
brew install llvm@11
|
||||
brew install llvm@11 botan
|
||||
echo "/usr/local/opt/llvm@11/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
- name: build odin
|
||||
run: make release
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
@@ -49,16 +71,40 @@ jobs:
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
run: ./odin check examples/demo -vet
|
||||
timeout-minutes: 10
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
run: ./odin run examples/demo
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo/demo.odin -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: Vendor library tests
|
||||
run: |
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
run: |
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Darwin arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Linux arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
|
||||
timeout-minutes: 10
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: build Odin
|
||||
@@ -76,19 +122,25 @@ jobs:
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/demo/demo.odin -vet
|
||||
odin check examples/demo -vet
|
||||
timeout-minutes: 10
|
||||
- name: Odin run
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo/demo.odin
|
||||
odin run examples/demo
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo/demo.odin -debug
|
||||
odin run examples/demo -debug
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/all -strict-style
|
||||
timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
shell: cmd
|
||||
@@ -97,6 +149,13 @@ jobs:
|
||||
cd tests\core
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Vendor library tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\vendor
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: core:math/big tests
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -104,3 +163,16 @@ jobs:
|
||||
cd tests\core\math\big
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\issues
|
||||
call run.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Windows 32bits
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/all -strict-style -target:windows_i386
|
||||
timeout-minutes: 10
|
||||
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: build Odin
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo/demo.odin
|
||||
odin run examples/demo
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
rm bin/llvm/windows/LLVM-C.lib
|
||||
@@ -41,11 +41,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: (Linux) Download LLVM
|
||||
run: sudo apt-get install llvm-11 clang-11 llvm
|
||||
run: sudo apt-get install llvm-11 clang-11
|
||||
- name: build odin
|
||||
run: make nightly
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
run: ./odin run examples/demo
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
mkdir dist
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
- name: build odin
|
||||
run: make nightly
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
run: ./odin run examples/demo
|
||||
- name: Copy artifacts
|
||||
run: |
|
||||
mkdir dist
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
run: |
|
||||
echo Authorizing B2 account
|
||||
b2 authorize-account "$APPID" "$APPKEY"
|
||||
|
||||
|
||||
echo Uploading artifcates to B2
|
||||
chmod +x ./ci/upload_create_nightly.sh
|
||||
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
|
||||
@@ -141,7 +141,7 @@ jobs:
|
||||
|
||||
echo Creating nightly.json
|
||||
python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
|
||||
|
||||
|
||||
echo Uploading nightly.json
|
||||
b2 upload-file "$BUCKET" nightly.json nightly.json
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# For macOS
|
||||
.DS_Store
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
@@ -266,6 +269,8 @@ bin/
|
||||
# - Linux/MacOS
|
||||
odin
|
||||
odin.dSYM
|
||||
*.bin
|
||||
demo.bin
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
@@ -276,3 +281,5 @@ shared/
|
||||
*.ll
|
||||
|
||||
*.sublime-workspace
|
||||
examples/bug/
|
||||
build.sh
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016-2021 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2022 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:
|
||||
|
||||
@@ -1,58 +1,19 @@
|
||||
GIT_SHA=$(shell git rev-parse --short HEAD)
|
||||
DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value
|
||||
LDFLAGS=-pthread -ldl -lm -lstdc++
|
||||
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
|
||||
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
|
||||
CC=clang
|
||||
|
||||
OS=$(shell uname)
|
||||
|
||||
ifeq ($(OS), Darwin)
|
||||
LLVM_CONFIG=llvm-config
|
||||
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
||||
LLVM_CONFIG=llvm-config
|
||||
else
|
||||
$(error "Requirement: llvm-config must be version 11")
|
||||
endif
|
||||
|
||||
LDFLAGS:=$(LDFLAGS) -liconv
|
||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||
LDFLAGS:=$(LDFLAGS) -lLLVM-C
|
||||
endif
|
||||
ifeq ($(OS), Linux)
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
ifneq ($(shell which llvm-config-11 2>/dev/null),)
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
else ifneq ($(shell which llvm-config-11-64 2>/dev/null),)
|
||||
LLVM_CONFIG=llvm-config-11-64
|
||||
else
|
||||
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
||||
LLVM_CONFIG=llvm-config
|
||||
else
|
||||
$(error "Requirement: llvm-config must be version 11")
|
||||
endif
|
||||
endif
|
||||
|
||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
|
||||
endif
|
||||
|
||||
all: debug demo
|
||||
all: debug
|
||||
|
||||
demo:
|
||||
./odin run examples/demo/demo.odin
|
||||
./odin run examples/demo/demo.odin -file
|
||||
|
||||
report:
|
||||
./odin report
|
||||
|
||||
debug:
|
||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
|
||||
./build_odin.sh debug
|
||||
|
||||
release:
|
||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 $(LDFLAGS) -o odin
|
||||
./build_odin.sh release
|
||||
|
||||
release_native:
|
||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
|
||||
./build_odin.sh release-native
|
||||
|
||||
nightly:
|
||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
|
||||
./build_odin.sh nightly
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://discord.gg/hnwN2Rj">
|
||||
<a href="https://discord.gg/odinlang">
|
||||
<img src="https://img.shields.io/discord/568138951836172421?logo=discord">
|
||||
</a>
|
||||
<a href="https://github.com/odin-lang/odin/actions">
|
||||
@@ -58,6 +58,10 @@ main :: proc() {
|
||||
|
||||
Instructions for downloading and installing the Odin compiler and libraries.
|
||||
|
||||
#### [Nightly Builds](https://odin-lang.org/docs/nightly/)
|
||||
|
||||
Get the latest nightly builds of Odin.
|
||||
|
||||
### Learning Odin
|
||||
|
||||
#### [Overview of Odin](https://odin-lang.org/docs/overview)
|
||||
@@ -68,6 +72,10 @@ An overview of the Odin programming language.
|
||||
|
||||
Answers to common questions about Odin.
|
||||
|
||||
#### [Packages](https://pkg.odin-lang.org/)
|
||||
|
||||
Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
|
||||
|
||||
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
|
||||
|
||||
A wiki maintained by the Odin community.
|
||||
@@ -76,15 +84,9 @@ A wiki maintained by the Odin community.
|
||||
|
||||
Get live support and talk with other odiners on the Odin Discord.
|
||||
|
||||
### References
|
||||
|
||||
#### [Language Specification](https://odin-lang.org/docs/spec/)
|
||||
|
||||
The official Odin Language specification.
|
||||
|
||||
### Articles
|
||||
|
||||
#### [The Odin Blog](https://odin-lang.org/blog)
|
||||
#### [The Odin Blog](https://odin-lang.org/news/)
|
||||
|
||||
The official blog of the Odin programming language, featuring announcements, news, and in-depth articles by the Odin team and guests.
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
NASM is now licensed under the 2-clause BSD license, also known as the
|
||||
simplified BSD license.
|
||||
|
||||
Copyright 1996-2010 the NASM 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.
|
||||
|
||||
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.
|
||||
Binary file not shown.
Binary file not shown.
@@ -79,4 +79,4 @@ if %release_mode% EQU 0 odin run examples/demo
|
||||
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
:end_of_build
|
||||
:end_of_build
|
||||
Executable
+150
@@ -0,0 +1,150 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
GIT_SHA=$(git rev-parse --short HEAD)
|
||||
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
||||
LDFLAGS="-pthread -lm -lstdc++"
|
||||
CFLAGS="-std=c++14 -DGIT_SHA=\"$GIT_SHA\""
|
||||
CFLAGS="$CFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
|
||||
CC=clang
|
||||
OS=$(uname)
|
||||
|
||||
panic() {
|
||||
printf "%s\n" "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
version() { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
|
||||
|
||||
config_darwin() {
|
||||
ARCH=$(uname -m)
|
||||
LLVM_CONFIG=llvm-config
|
||||
|
||||
# allow for arm only llvm's with version 13
|
||||
if [ ARCH == arm64 ]; then
|
||||
MIN_LLVM_VERSION=("13.0.0")
|
||||
else
|
||||
# allow for x86 / amd64 all llvm versions begining from 11
|
||||
MIN_LLVM_VERSION=("11.1.0")
|
||||
fi
|
||||
|
||||
if [ $(version $($LLVM_CONFIG --version)) -lt $(version $MIN_LLVM_VERSION) ]; then
|
||||
if [ ARCH == arm64 ]; then
|
||||
panic "Requirement: llvm-config must be base version 13 for arm64"
|
||||
else
|
||||
panic "Requirement: llvm-config must be base version greater than 11 for amd64/x86"
|
||||
fi
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv -ldl"
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS -lLLVM-C"
|
||||
}
|
||||
|
||||
config_freebsd() {
|
||||
LLVM_CONFIG=/usr/local/bin/llvm-config11
|
||||
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
}
|
||||
|
||||
config_openbsd() {
|
||||
LLVM_CONFIG=/usr/local/bin/llvm-config
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv"
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
}
|
||||
|
||||
config_linux() {
|
||||
if which llvm-config > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config
|
||||
elif which llvm-config-11 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
elif which llvm-config-11-64 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config-11-64
|
||||
else
|
||||
panic "Unable to find LLVM-config"
|
||||
fi
|
||||
|
||||
MIN_LLVM_VERSION=("11.0.0")
|
||||
if [ $(version $($LLVM_CONFIG --version)) -lt $(version $MIN_LLVM_VERSION) ]; then
|
||||
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
|
||||
panic "Requirement: llvm-config must be base version greater than 11"
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -ldl"
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
}
|
||||
|
||||
build_odin() {
|
||||
case $1 in
|
||||
debug)
|
||||
EXTRAFLAGS="-g"
|
||||
;;
|
||||
release)
|
||||
EXTRAFLAGS="-O3"
|
||||
;;
|
||||
release-native)
|
||||
EXTRAFLAGS="-O3 -march=native"
|
||||
;;
|
||||
nightly)
|
||||
EXTRAFLAGS="-DNIGHTLY -O3"
|
||||
;;
|
||||
*)
|
||||
panic "Build mode unsupported!"
|
||||
esac
|
||||
|
||||
set -x
|
||||
$CC src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CFLAGS $EXTRAFLAGS $LDFLAGS -o odin
|
||||
set +x
|
||||
}
|
||||
|
||||
run_demo() {
|
||||
./odin run examples/demo/demo.odin -file
|
||||
}
|
||||
|
||||
case $OS in
|
||||
Linux)
|
||||
config_linux
|
||||
;;
|
||||
Darwin)
|
||||
config_darwin
|
||||
;;
|
||||
OpenBSD)
|
||||
config_openbsd
|
||||
;;
|
||||
FreeBSD)
|
||||
config_freebsd
|
||||
;;
|
||||
*)
|
||||
panic "Platform unsupported!"
|
||||
esac
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
build_odin debug
|
||||
run_demo
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ $# -eq 1 ]]; then
|
||||
case $1 in
|
||||
report)
|
||||
if [[ ! -f "./odin" ]]; then
|
||||
build_odin debug
|
||||
fi
|
||||
|
||||
./odin report
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
build_odin $1
|
||||
;;
|
||||
esac
|
||||
|
||||
run_demo
|
||||
exit 0
|
||||
else
|
||||
panic "Too many arguments!"
|
||||
fi
|
||||
@@ -8,6 +8,7 @@ import "core:intrinsics"
|
||||
|
||||
// Extra errors returns by scanning procedures
|
||||
Scanner_Extra_Error :: enum i32 {
|
||||
None,
|
||||
Negative_Advance,
|
||||
Advanced_Too_Far,
|
||||
Bad_Read_Count,
|
||||
@@ -15,7 +16,7 @@ Scanner_Extra_Error :: enum i32 {
|
||||
Too_Short,
|
||||
}
|
||||
|
||||
Scanner_Error :: union {
|
||||
Scanner_Error :: union #shared_nil {
|
||||
io.Error,
|
||||
Scanner_Extra_Error,
|
||||
}
|
||||
@@ -68,7 +69,7 @@ scanner_destroy :: proc(s: ^Scanner) {
|
||||
// Returns the first non-EOF error that was encounted by the scanner
|
||||
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
||||
switch s._err {
|
||||
case .EOF, .None:
|
||||
case .EOF, nil:
|
||||
return nil
|
||||
}
|
||||
return s._err
|
||||
@@ -93,10 +94,6 @@ scanner_text :: proc(s: ^Scanner) -> string {
|
||||
// scanner_scan advances the scanner
|
||||
scanner_scan :: proc(s: ^Scanner) -> bool {
|
||||
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
|
||||
err := err
|
||||
if err == .None {
|
||||
err = nil
|
||||
}
|
||||
switch s._err {
|
||||
case nil, .EOF:
|
||||
s._err = err
|
||||
|
||||
+66
-66
@@ -1,90 +1,90 @@
|
||||
// This is purely for documentation
|
||||
package builtin
|
||||
|
||||
nil :: nil;
|
||||
false :: 0!==0;
|
||||
true :: 0==0;
|
||||
nil :: nil
|
||||
false :: 0!=0
|
||||
true :: 0==0
|
||||
|
||||
ODIN_OS :: ODIN_OS;
|
||||
ODIN_ARCH :: ODIN_ARCH;
|
||||
ODIN_ENDIAN :: ODIN_ENDIAN;
|
||||
ODIN_VENDOR :: ODIN_VENDOR;
|
||||
ODIN_VERSION :: ODIN_VERSION;
|
||||
ODIN_ROOT :: ODIN_ROOT;
|
||||
ODIN_DEBUG :: ODIN_DEBUG;
|
||||
ODIN_OS :: ODIN_OS
|
||||
ODIN_ARCH :: ODIN_ARCH
|
||||
ODIN_ENDIAN :: ODIN_ENDIAN
|
||||
ODIN_VENDOR :: ODIN_VENDOR
|
||||
ODIN_VERSION :: ODIN_VERSION
|
||||
ODIN_ROOT :: ODIN_ROOT
|
||||
ODIN_DEBUG :: ODIN_DEBUG
|
||||
|
||||
byte :: u8; // alias
|
||||
byte :: u8 // alias
|
||||
|
||||
bool :: bool;
|
||||
b8 :: b8;
|
||||
b16 :: b16;
|
||||
b32 :: b32;
|
||||
b64 :: b64;
|
||||
bool :: bool
|
||||
b8 :: b8
|
||||
b16 :: b16
|
||||
b32 :: b32
|
||||
b64 :: b64
|
||||
|
||||
i8 :: i8;
|
||||
u8 :: u8;
|
||||
i16 :: i16;
|
||||
u16 :: u16;
|
||||
i32 :: i32;
|
||||
u32 :: u32;
|
||||
i64 :: i64;
|
||||
u64 :: u64;
|
||||
i8 :: i8
|
||||
u8 :: u8
|
||||
i16 :: i16
|
||||
u16 :: u16
|
||||
i32 :: i32
|
||||
u32 :: u32
|
||||
i64 :: i64
|
||||
u64 :: u64
|
||||
|
||||
i128 :: i128;
|
||||
u128 :: u128;
|
||||
i128 :: i128
|
||||
u128 :: u128
|
||||
|
||||
rune :: rune;
|
||||
rune :: rune
|
||||
|
||||
f16 :: f16;
|
||||
f32 :: f32;
|
||||
f64 :: f64;
|
||||
f16 :: f16
|
||||
f32 :: f32
|
||||
f64 :: f64
|
||||
|
||||
complex32 :: complex32;
|
||||
complex64 :: complex64;
|
||||
complex128 :: complex128;
|
||||
complex32 :: complex32
|
||||
complex64 :: complex64
|
||||
complex128 :: complex128
|
||||
|
||||
quaternion64 :: quaternion64;
|
||||
quaternion128 :: quaternion128;
|
||||
quaternion256 :: quaternion256;
|
||||
quaternion64 :: quaternion64
|
||||
quaternion128 :: quaternion128
|
||||
quaternion256 :: quaternion256
|
||||
|
||||
int :: int;
|
||||
uint :: uint;
|
||||
uintptr :: uintptr;
|
||||
int :: int
|
||||
uint :: uint
|
||||
uintptr :: uintptr
|
||||
|
||||
rawptr :: rawptr;
|
||||
string :: string;
|
||||
cstring :: cstring;
|
||||
any :: any;
|
||||
rawptr :: rawptr
|
||||
string :: string
|
||||
cstring :: cstring
|
||||
any :: any
|
||||
|
||||
typeid :: typeid;
|
||||
typeid :: typeid
|
||||
|
||||
// Endian Specific Types
|
||||
i16le :: i16le;
|
||||
u16le :: u16le;
|
||||
i32le :: i32le;
|
||||
u32le :: u32le;
|
||||
i64le :: i64le;
|
||||
u64le :: u64le;
|
||||
i128le :: i128le;
|
||||
u128le :: u128le;
|
||||
i16le :: i16le
|
||||
u16le :: u16le
|
||||
i32le :: i32le
|
||||
u32le :: u32le
|
||||
i64le :: i64le
|
||||
u64le :: u64le
|
||||
i128le :: i128le
|
||||
u128le :: u128le
|
||||
|
||||
i16be :: i16be;
|
||||
u16be :: u16be;
|
||||
i32be :: i32be;
|
||||
u32be :: u32be;
|
||||
i64be :: i64be;
|
||||
u64be :: u64be;
|
||||
i128be :: i128be;
|
||||
u128be :: u128be;
|
||||
i16be :: i16be
|
||||
u16be :: u16be
|
||||
i32be :: i32be
|
||||
u32be :: u32be
|
||||
i64be :: i64be
|
||||
u64be :: u64be
|
||||
i128be :: i128be
|
||||
u128be :: u128be
|
||||
|
||||
|
||||
f16le :: f16le;
|
||||
f32le :: f32le;
|
||||
f64le :: f64le;
|
||||
f16le :: f16le
|
||||
f32le :: f32le
|
||||
f64le :: f64le
|
||||
|
||||
f16be :: f16be;
|
||||
f32be :: f32be;
|
||||
f64be :: f64be;
|
||||
f16be :: f16be
|
||||
f32be :: f32be
|
||||
f64be :: f64be
|
||||
|
||||
|
||||
|
||||
|
||||
+58
-39
@@ -5,13 +5,19 @@ import "core:unicode"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
|
||||
c := make([]byte, len(s)+1, allocator, loc)
|
||||
c := make([]byte, len(s), allocator, loc)
|
||||
copy(c, s)
|
||||
c[len(s)] = 0
|
||||
return c[:len(s)]
|
||||
}
|
||||
|
||||
ptr_from_slice :: proc(str: []byte) -> ^byte {
|
||||
clone_safe :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
c := make([]byte, len(s), allocator, loc) or_return
|
||||
copy(c, s)
|
||||
return c[:len(s)], nil
|
||||
}
|
||||
|
||||
ptr_from_slice :: ptr_from_bytes
|
||||
ptr_from_bytes :: proc(str: []byte) -> ^byte {
|
||||
d := transmute(mem.Raw_String)str
|
||||
return d.data
|
||||
}
|
||||
@@ -135,6 +141,25 @@ join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte
|
||||
return b
|
||||
}
|
||||
|
||||
join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
if len(a) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
n := len(sep) * (len(a) - 1)
|
||||
for s in a {
|
||||
n += len(s)
|
||||
}
|
||||
|
||||
b := make([]byte, n, allocator) or_return
|
||||
i := copy(b, a[0])
|
||||
for s in a[1:] {
|
||||
i += copy(b[i:], sep)
|
||||
i += copy(b[i:], s)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
if len(a) == 0 {
|
||||
return nil
|
||||
@@ -152,6 +177,24 @@ concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
concatenate_safe :: proc(a: [][]byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
if len(a) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
n := 0
|
||||
for s in a {
|
||||
n += len(s)
|
||||
}
|
||||
b := make([]byte, n, allocator) or_return
|
||||
i := 0
|
||||
for s in a {
|
||||
i += copy(b[i:], s)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
|
||||
@private
|
||||
_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator) -> [][]byte {
|
||||
s, n := s, n
|
||||
@@ -219,61 +262,37 @@ split_after_n :: proc(s, sep: []byte, n: int, allocator := context.allocator) ->
|
||||
|
||||
|
||||
@private
|
||||
_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save, n: int) -> (res: []byte, ok: bool) {
|
||||
s, n := s, n
|
||||
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if sep == nil {
|
||||
_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save: int) -> (res: []byte, ok: bool) {
|
||||
if len(sep) == 0 {
|
||||
res = s[:]
|
||||
ok = true
|
||||
s^ = s[len(s):]
|
||||
return
|
||||
}
|
||||
|
||||
if n < 0 {
|
||||
n = count(s^, sep) + 1
|
||||
}
|
||||
|
||||
n -= 1
|
||||
|
||||
i := 0
|
||||
for ; i < n; i += 1 {
|
||||
m := index(s^, sep)
|
||||
if m < 0 {
|
||||
break
|
||||
}
|
||||
m := index(s^, sep)
|
||||
if m < 0 {
|
||||
// not found
|
||||
res = s[:]
|
||||
ok = len(res) != 0
|
||||
s^ = s[len(s):]
|
||||
} else {
|
||||
res = s[:m+sep_save]
|
||||
ok = true
|
||||
s^ = s[m+len(sep):]
|
||||
return
|
||||
}
|
||||
res = s[:]
|
||||
ok = res != nil
|
||||
s^ = s[len(s):]
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
split_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
|
||||
return _split_iterator(s, sep, 0, -1)
|
||||
}
|
||||
|
||||
split_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) {
|
||||
return _split_iterator(s, sep, 0, n)
|
||||
return _split_iterator(s, sep, 0)
|
||||
}
|
||||
|
||||
split_after_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
|
||||
return _split_iterator(s, sep, len(sep), -1)
|
||||
return _split_iterator(s, sep, len(sep))
|
||||
}
|
||||
|
||||
split_after_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) {
|
||||
return _split_iterator(s, sep, len(sep), n)
|
||||
}
|
||||
|
||||
|
||||
|
||||
index_byte :: proc(s: []byte, c: byte) -> int {
|
||||
for i := 0; i < len(s); i += 1 {
|
||||
@@ -1143,7 +1162,7 @@ fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.alloc
|
||||
}
|
||||
|
||||
if start >= 0 {
|
||||
append(&subslices, s[start : end])
|
||||
append(&subslices, s[start : len(s)])
|
||||
}
|
||||
|
||||
return subslices[:]
|
||||
|
||||
+6
-4
@@ -3,22 +3,24 @@ package c
|
||||
import builtin "core:builtin"
|
||||
|
||||
char :: builtin.u8 // assuming -funsigned-char
|
||||
|
||||
schar :: builtin.i8
|
||||
short :: builtin.i16
|
||||
int :: builtin.i32
|
||||
long :: builtin.i32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.i64
|
||||
long :: builtin.i32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.i64
|
||||
longlong :: builtin.i64
|
||||
|
||||
uchar :: builtin.u8
|
||||
ushort :: builtin.u16
|
||||
uint :: builtin.u32
|
||||
ulong :: builtin.u32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.u64
|
||||
ulong :: builtin.u32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.u64
|
||||
ulonglong :: builtin.u64
|
||||
|
||||
bool :: builtin.bool
|
||||
|
||||
size_t :: builtin.uint
|
||||
ssize_t :: builtin.int
|
||||
wchar_t :: builtin.u16 when (ODIN_OS == "windows") else builtin.u32
|
||||
wchar_t :: builtin.u16 when (ODIN_OS == .Windows) else builtin.u32
|
||||
|
||||
float :: builtin.f32
|
||||
double :: builtin.f64
|
||||
@@ -46,7 +48,7 @@ int_least64_t :: builtin.i64
|
||||
uint_least64_t :: builtin.u64
|
||||
|
||||
// Same on Windows, Linux, and FreeBSD
|
||||
when ODIN_ARCH == "386" || ODIN_ARCH == "amd64" {
|
||||
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||
int_fast8_t :: builtin.i8
|
||||
uint_fast8_t :: builtin.u8
|
||||
int_fast16_t :: builtin.i32
|
||||
|
||||
@@ -956,7 +956,7 @@ substitute_token :: proc(cpp: ^Preprocessor, tok: ^Token, args: ^Macro_Arg) -> ^
|
||||
continue
|
||||
}
|
||||
|
||||
if tok.lit == "__VA__OPT__" && tok.next.lit == "(" {
|
||||
if tok.lit == "__VA_OPT__" && tok.next.lit == "(" {
|
||||
opt_arg := read_macro_arg_one(cpp, &tok, tok.next.next, true)
|
||||
if has_varargs(args) {
|
||||
for t := opt_arg.tok; t.kind != .EOF; t = t.next {
|
||||
@@ -1276,7 +1276,7 @@ preprocess_internal :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Token {
|
||||
if start.file != nil {
|
||||
dir = filepath.dir(start.file.name)
|
||||
}
|
||||
path := filepath.join(dir, filename)
|
||||
path := filepath.join({dir, filename})
|
||||
if os.exists(path) {
|
||||
tok = include_file(cpp, tok, path, start.next.next)
|
||||
continue
|
||||
|
||||
@@ -2,8 +2,10 @@ package libc
|
||||
|
||||
// 7.3 Complex arithmetic
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
+32
-3
@@ -2,8 +2,10 @@ package libc
|
||||
|
||||
// 7.5 Errors
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -12,7 +14,7 @@ when ODIN_OS == "windows" {
|
||||
// EDOM,
|
||||
// EILSEQ
|
||||
// ERANGE
|
||||
when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@@ -25,7 +27,20 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .OpenBSD {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__errno")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
EDOM :: 33
|
||||
EILSEQ :: 84
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@@ -38,6 +53,20 @@ when ODIN_OS == "windows" {
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__error")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
// Unknown
|
||||
EDOM :: 33
|
||||
EILSEQ :: 92
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
+13
-11
@@ -4,8 +4,10 @@ package libc
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -209,19 +211,19 @@ _signbitf :: #force_inline proc(x: float) -> int {
|
||||
return int(transmute(uint32_t)x >> 31)
|
||||
}
|
||||
|
||||
isfinite :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isfinite :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) == FP_INFINITE
|
||||
}
|
||||
|
||||
isinf :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isinf :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) > FP_INFINITE
|
||||
}
|
||||
|
||||
isnan :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isnan :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) == FP_NAN
|
||||
}
|
||||
|
||||
isnormal :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isnormal :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) == FP_NORMAL
|
||||
}
|
||||
|
||||
@@ -229,27 +231,27 @@ isnormal :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
// implemented as the relational comparisons, as that would produce an invalid
|
||||
// "sticky" state that propagates and affects maths results. These need
|
||||
// to be implemented natively in Odin assuming isunordered to prevent that.
|
||||
isgreater :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isgreater :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x > y
|
||||
}
|
||||
|
||||
isgreaterequal :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isgreaterequal :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x >= y
|
||||
}
|
||||
|
||||
isless :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isless :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x < y
|
||||
}
|
||||
|
||||
islessequal :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
islessequal :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x <= y
|
||||
}
|
||||
|
||||
islessgreater :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
islessgreater :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x <= y
|
||||
}
|
||||
|
||||
isunordered :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isunordered :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
if isnan(x) {
|
||||
// Force evaluation of y to propagate exceptions for ordering semantics.
|
||||
// To ensure correct semantics of IEEE 754 this cannot be compiled away.
|
||||
|
||||
@@ -2,13 +2,14 @@ package libc
|
||||
|
||||
// 7.13 Nonlocal jumps
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.13.1 Save calling environment
|
||||
|
||||
+18
-3
@@ -2,8 +2,10 @@ package libc
|
||||
|
||||
// 7.14 Signal handling
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -19,7 +21,7 @@ foreign libc {
|
||||
raise :: proc(sig: int) -> int ---
|
||||
}
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
SIG_ERR :: rawptr(~uintptr(0))
|
||||
SIG_DFL :: rawptr(uintptr(0))
|
||||
SIG_IGN :: rawptr(uintptr(1))
|
||||
@@ -32,7 +34,20 @@ when ODIN_OS == "windows" {
|
||||
SIGTERM :: 15
|
||||
}
|
||||
|
||||
when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
||||
SIG_ERR :: rawptr(~uintptr(0))
|
||||
SIG_DFL :: rawptr(uintptr(0))
|
||||
SIG_IGN :: rawptr(uintptr(1))
|
||||
|
||||
SIGABRT :: 6
|
||||
SIGFPE :: 8
|
||||
SIGILL :: 4
|
||||
SIGINT :: 2
|
||||
SIGSEGV :: 11
|
||||
SIGTERM :: 15
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
SIG_ERR :: rawptr(~uintptr(0))
|
||||
SIG_DFL :: rawptr(uintptr(0))
|
||||
SIG_IGN :: rawptr(uintptr(1))
|
||||
|
||||
+111
-146
@@ -47,29 +47,30 @@ kill_dependency :: #force_inline proc(value: $T) -> T {
|
||||
|
||||
// 7.17.4 Fences
|
||||
atomic_thread_fence :: #force_inline proc(order: memory_order) {
|
||||
switch (order) {
|
||||
case .relaxed:
|
||||
return
|
||||
case .consume:
|
||||
intrinsics.atomic_fence_acq()
|
||||
case .acquire:
|
||||
intrinsics.atomic_fence_acq()
|
||||
case .release:
|
||||
intrinsics.atomic_fence_rel()
|
||||
case .acq_rel:
|
||||
intrinsics.atomic_fence_acqrel()
|
||||
case .seq_cst:
|
||||
intrinsics.atomic_fence_acqrel()
|
||||
assert(order != .relaxed)
|
||||
assert(order != .consume)
|
||||
#partial switch order {
|
||||
case .acquire: intrinsics.atomic_thread_fence(.Acquire)
|
||||
case .release: intrinsics.atomic_thread_fence(.Release)
|
||||
case .acq_rel: intrinsics.atomic_thread_fence(.Acq_Rel)
|
||||
case .seq_cst: intrinsics.atomic_thread_fence(.Seq_Cst)
|
||||
}
|
||||
}
|
||||
|
||||
atomic_signal_fence :: #force_inline proc(order: memory_order) {
|
||||
atomic_thread_fence(order)
|
||||
assert(order != .relaxed)
|
||||
assert(order != .consume)
|
||||
#partial switch order {
|
||||
case .acquire: intrinsics.atomic_signal_fence(.Acquire)
|
||||
case .release: intrinsics.atomic_signal_fence(.Release)
|
||||
case .acq_rel: intrinsics.atomic_signal_fence(.Acq_Rel)
|
||||
case .seq_cst: intrinsics.atomic_signal_fence(.Seq_Cst)
|
||||
}
|
||||
}
|
||||
|
||||
// 7.17.5 Lock-free property
|
||||
atomic_is_lock_free :: #force_inline proc(obj: ^$T) -> bool {
|
||||
return size_of(T) <= 8 && (intrinsics.type_is_integer(T) || intrinsics.type_is_pointer(T))
|
||||
return intrinsics.atomic_type_is_lock_free(T)
|
||||
}
|
||||
|
||||
// 7.17.6 Atomic integer types
|
||||
@@ -121,13 +122,10 @@ atomic_store_explicit :: #force_inline proc(object: ^$T, desired: T, order: memo
|
||||
assert(order != .acquire)
|
||||
assert(order != .acq_rel)
|
||||
|
||||
#partial switch (order) {
|
||||
case .relaxed:
|
||||
intrinsics.atomic_store_relaxed(object, desired)
|
||||
case .release:
|
||||
intrinsics.atomic_store_rel(object, desired)
|
||||
case .seq_cst:
|
||||
intrinsics.atomic_store(object, desired)
|
||||
#partial switch order {
|
||||
case .relaxed: intrinsics.atomic_store_explicit(object, desired, .Relaxed)
|
||||
case .release: intrinsics.atomic_store_explicit(object, desired, .Release)
|
||||
case .seq_cst: intrinsics.atomic_store_explicit(object, desired, .Seq_Cst)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,36 +137,26 @@ atomic_load_explicit :: #force_inline proc(object: ^$T, order: memory_order) {
|
||||
assert(order != .release)
|
||||
assert(order != .acq_rel)
|
||||
|
||||
#partial switch (order) {
|
||||
case .relaxed:
|
||||
return intrinsics.atomic_load_relaxed(object)
|
||||
case .consume:
|
||||
return intrinsics.atomic_load_acq(object)
|
||||
case .acquire:
|
||||
return intrinsics.atomic_load_acq(object)
|
||||
case .seq_cst:
|
||||
return intrinsics.atomic_load(object)
|
||||
#partial switch order {
|
||||
case .relaxed: return intrinsics.atomic_load_explicit(object, .Relaxed)
|
||||
case .consume: return intrinsics.atomic_load_explicit(object, .Consume)
|
||||
case .acquire: return intrinsics.atomic_load_explicit(object, .Acquire)
|
||||
case .seq_cst: return intrinsics.atomic_load_explicit(object, .Seq_Cst)
|
||||
}
|
||||
}
|
||||
|
||||
atomic_exchange :: #force_inline proc(object: ^$T, desired: T) -> T {
|
||||
return intrinsics.atomic_xchg(object, desired)
|
||||
return intrinsics.atomic_exchange(object, desired)
|
||||
}
|
||||
|
||||
atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) -> T {
|
||||
switch (order) {
|
||||
case .relaxed:
|
||||
return intrinsics.atomic_xchg_relaxed(object, desired)
|
||||
case .consume:
|
||||
return intrinsics.atomic_xchg_acq(object, desired)
|
||||
case .acquire:
|
||||
return intrinsics.atomic_xchg_acq(object, desired)
|
||||
case .release:
|
||||
return intrinsics.atomic_xchg_rel(object, desired)
|
||||
case .acq_rel:
|
||||
return intrinsics.atomic_xchg_acqrel(object, desired)
|
||||
case .seq_cst:
|
||||
return intrinsics.atomic_xchg(object, desired)
|
||||
switch order {
|
||||
case .relaxed: return intrinsics.atomic_exchange_explicit(object, desired, .Relaxed)
|
||||
case .consume: return intrinsics.atomic_exchange_explicit(object, desired, .Consume)
|
||||
case .acquire: return intrinsics.atomic_exchange_explicit(object, desired, .Acquire)
|
||||
case .release: return intrinsics.atomic_exchange_explicit(object, desired, .Release)
|
||||
case .acq_rel: return intrinsics.atomic_exchange_explicit(object, desired, .Acq_Rel)
|
||||
case .seq_cst: return intrinsics.atomic_exchange_explicit(object, desired, .Seq_Cst)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -189,102 +177,104 @@ atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: m
|
||||
// [success = seq_cst, failure = acquire] => failacq
|
||||
// [success = acquire, failure = relaxed] => acq_failrelaxed
|
||||
// [success = acq_rel, failure = relaxed] => acqrel_failrelaxed
|
||||
atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) {
|
||||
value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
|
||||
atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
|
||||
value, ok := intrinsics.atomic_compare_exchange_strong(object, expected^, desired)
|
||||
if !ok { expected^ = value }
|
||||
return ok
|
||||
}
|
||||
|
||||
atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) {
|
||||
atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool {
|
||||
assert(failure != .release)
|
||||
assert(failure != .acq_rel)
|
||||
|
||||
value: T; ok: bool
|
||||
#partial switch (failure) {
|
||||
#partial switch failure {
|
||||
case .seq_cst:
|
||||
assert(success != .relaxed)
|
||||
#partial switch (success) {
|
||||
#partial switch success {
|
||||
case .seq_cst:
|
||||
value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
|
||||
case .acquire:
|
||||
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
|
||||
case .consume:
|
||||
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Seq_Cst)
|
||||
case .release:
|
||||
value, ok := intrinsics.atomic_cxchg_rel(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Release, .Seq_Cst)
|
||||
case .acq_rel:
|
||||
value, ok := intrinsics.atomic_cxchg_acqrel(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
|
||||
}
|
||||
case .relaxed:
|
||||
assert(success != .release)
|
||||
#partial switch (success) {
|
||||
#partial switch success {
|
||||
case .relaxed:
|
||||
value, ok := intrinsics.atomic_cxchg_relaxed(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Relaxed, .Relaxed)
|
||||
case .seq_cst:
|
||||
value, ok := intrinsics.atomic_cxchg_failrelaxed(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
|
||||
case .acquire:
|
||||
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Relaxed)
|
||||
case .consume:
|
||||
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Relaxed)
|
||||
case .acq_rel:
|
||||
value, ok := intrinsics.atomic_cxchg_acqrel_failrelaxed(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
|
||||
}
|
||||
case .consume:
|
||||
fallthrough
|
||||
assert(success == .seq_cst)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Consume)
|
||||
case .acquire:
|
||||
assert(success == .seq_cst)
|
||||
value, ok := intrinsics.atomic_cxchg_failacq(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
|
||||
|
||||
}
|
||||
if !ok { expected^ = value }
|
||||
return ok
|
||||
}
|
||||
|
||||
atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) {
|
||||
value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
|
||||
atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
|
||||
value, ok := intrinsics.atomic_compare_exchange_weak(object, expected^, desired)
|
||||
if !ok { expected^ = value }
|
||||
return ok
|
||||
}
|
||||
|
||||
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) {
|
||||
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool {
|
||||
assert(failure != .release)
|
||||
assert(failure != .acq_rel)
|
||||
|
||||
value: T; ok: bool
|
||||
#partial switch (failure) {
|
||||
#partial switch failure {
|
||||
case .seq_cst:
|
||||
assert(success != .relaxed)
|
||||
#partial switch (success) {
|
||||
#partial switch success {
|
||||
case .seq_cst:
|
||||
value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
|
||||
case .acquire:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
|
||||
case .consume:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Seq_Cst)
|
||||
case .release:
|
||||
value, ok := intrinsics.atomic_cxchgweak_rel(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Release, .Seq_Cst)
|
||||
case .acq_rel:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acqrel(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
|
||||
}
|
||||
case .relaxed:
|
||||
assert(success != .release)
|
||||
#partial switch (success) {
|
||||
#partial switch success {
|
||||
case .relaxed:
|
||||
value, ok := intrinsics.atomic_cxchgweak_relaxed(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Relaxed, .Relaxed)
|
||||
case .seq_cst:
|
||||
value, ok := intrinsics.atomic_cxchgweak_failrelaxed(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
|
||||
case .acquire:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Relaxed)
|
||||
case .consume:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Relaxed)
|
||||
case .acq_rel:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acqrel_failrelaxed(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
|
||||
}
|
||||
case .consume:
|
||||
fallthrough
|
||||
assert(success == .seq_cst)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Consume)
|
||||
case .acquire:
|
||||
assert(success == .seq_cst)
|
||||
value, ok := intrinsics.atomic_cxchgweak_failacq(object, expected^, desired)
|
||||
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
|
||||
|
||||
}
|
||||
if !ok { expected^ = value }
|
||||
@@ -297,19 +287,14 @@ atomic_fetch_add :: #force_inline proc(object: ^$T, operand: T) -> T {
|
||||
}
|
||||
|
||||
atomic_fetch_add_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
||||
switch (order) {
|
||||
case .relaxed:
|
||||
return intrinsics.atomic_add_relaxed(object, operand)
|
||||
case .consume:
|
||||
return intrinsics.atomic_add_acq(object, operand)
|
||||
case .acquire:
|
||||
return intrinsics.atomic_add_acq(object, operand)
|
||||
case .release:
|
||||
return intrinsics.atomic_add_rel(object, operand)
|
||||
case .acq_rel:
|
||||
return intrinsics.atomic_add_acqrel(object, operand)
|
||||
case .seq_cst:
|
||||
return intrinsics.atomic_add(object, operand)
|
||||
switch order {
|
||||
case .relaxed: return intrinsics.atomic_add_explicit(object, operand, .Relaxed)
|
||||
case .consume: return intrinsics.atomic_add_explicit(object, operand, .Consume)
|
||||
case .acquire: return intrinsics.atomic_add_explicit(object, operand, .Acquire)
|
||||
case .release: return intrinsics.atomic_add_explicit(object, operand, .Release)
|
||||
case .acq_rel: return intrinsics.atomic_add_explicit(object, operand, .Acq_Rel)
|
||||
case: fallthrough
|
||||
case .seq_cst: return intrinsics.atomic_add_explicit(object, operand, .Seq_Cst)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,19 +303,14 @@ atomic_fetch_sub :: #force_inline proc(object: ^$T, operand: T) -> T {
|
||||
}
|
||||
|
||||
atomic_fetch_sub_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
||||
switch (order) {
|
||||
case .relaxed:
|
||||
return intrinsics.atomic_sub_relaxed(object, operand)
|
||||
case .consume:
|
||||
return intrinsics.atomic_sub_acq(object, operand)
|
||||
case .acquire:
|
||||
return intrinsics.atomic_sub_acq(object, operand)
|
||||
case .release:
|
||||
return intrinsics.atomic_sub_rel(object, operand)
|
||||
case .acq_rel:
|
||||
return intrinsics.atomic_sub_acqrel(object, operand)
|
||||
case .seq_cst:
|
||||
return intrinsics.atomic_sub(object, operand)
|
||||
switch order {
|
||||
case .relaxed: return intrinsics.atomic_sub_explicit(object, operand, .Relaxed)
|
||||
case .consume: return intrinsics.atomic_sub_explicit(object, operand, .Consume)
|
||||
case .acquire: return intrinsics.atomic_sub_explicit(object, operand, .Acquire)
|
||||
case .release: return intrinsics.atomic_sub_explicit(object, operand, .Release)
|
||||
case .acq_rel: return intrinsics.atomic_sub_explicit(object, operand, .Acq_Rel)
|
||||
case: fallthrough
|
||||
case .seq_cst: return intrinsics.atomic_sub_explicit(object, operand, .Seq_Cst)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,19 +319,14 @@ atomic_fetch_or :: #force_inline proc(object: ^$T, operand: T) -> T {
|
||||
}
|
||||
|
||||
atomic_fetch_or_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
||||
switch (order) {
|
||||
case .relaxed:
|
||||
return intrinsics.atomic_or_relaxed(object, operand)
|
||||
case .consume:
|
||||
return intrinsics.atomic_or_acq(object, operand)
|
||||
case .acquire:
|
||||
return intrinsics.atomic_or_acq(object, operand)
|
||||
case .release:
|
||||
return intrinsics.atomic_or_rel(object, operand)
|
||||
case .acq_rel:
|
||||
return intrinsics.atomic_or_acqrel(object, operand)
|
||||
case .seq_cst:
|
||||
return intrinsics.atomic_or(object, operand)
|
||||
switch order {
|
||||
case .relaxed: return intrinsics.atomic_or_explicit(object, operand, .Relaxed)
|
||||
case .consume: return intrinsics.atomic_or_explicit(object, operand, .Consume)
|
||||
case .acquire: return intrinsics.atomic_or_explicit(object, operand, .Acquire)
|
||||
case .release: return intrinsics.atomic_or_explicit(object, operand, .Release)
|
||||
case .acq_rel: return intrinsics.atomic_or_explicit(object, operand, .Acq_Rel)
|
||||
case: fallthrough
|
||||
case .seq_cst: return intrinsics.atomic_or_explicit(object, operand, .Seq_Cst)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,19 +335,14 @@ atomic_fetch_xor :: #force_inline proc(object: ^$T, operand: T) -> T {
|
||||
}
|
||||
|
||||
atomic_fetch_xor_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
||||
switch (order) {
|
||||
case .relaxed:
|
||||
return intrinsics.atomic_xor_relaxed(object, operand)
|
||||
case .consume:
|
||||
return intrinsics.atomic_xor_acq(object, operand)
|
||||
case .acquire:
|
||||
return intrinsics.atomic_xor_acq(object, operand)
|
||||
case .release:
|
||||
return intrinsics.atomic_xor_rel(object, operand)
|
||||
case .acq_rel:
|
||||
return intrinsics.atomic_xor_acqrel(object, operand)
|
||||
case .seq_cst:
|
||||
return intrinsics.atomic_xor(object, operand)
|
||||
switch order {
|
||||
case .relaxed: return intrinsics.atomic_xor_explicit(object, operand, .Relaxed)
|
||||
case .consume: return intrinsics.atomic_xor_explicit(object, operand, .Consume)
|
||||
case .acquire: return intrinsics.atomic_xor_explicit(object, operand, .Acquire)
|
||||
case .release: return intrinsics.atomic_xor_explicit(object, operand, .Release)
|
||||
case .acq_rel: return intrinsics.atomic_xor_explicit(object, operand, .Acq_Rel)
|
||||
case: fallthrough
|
||||
case .seq_cst: return intrinsics.atomic_xor_explicit(object, operand, .Seq_Cst)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,19 +350,14 @@ atomic_fetch_and :: #force_inline proc(object: ^$T, operand: T) -> T {
|
||||
return intrinsics.atomic_and(object, operand)
|
||||
}
|
||||
atomic_fetch_and_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
|
||||
switch (order) {
|
||||
case .relaxed:
|
||||
return intrinsics.atomic_and_relaxed(object, operand)
|
||||
case .consume:
|
||||
return intrinsics.atomic_and_acq(object, operand)
|
||||
case .acquire:
|
||||
return intrinsics.atomic_and_acq(object, operand)
|
||||
case .release:
|
||||
return intrinsics.atomic_and_rel(object, operand)
|
||||
case .acq_rel:
|
||||
return intrinsics.atomic_and_acqrel(object, operand)
|
||||
case .seq_cst:
|
||||
return intrinsics.atomic_and(object, operand)
|
||||
switch order {
|
||||
case .relaxed: return intrinsics.atomic_and_explicit(object, operand, .Relaxed)
|
||||
case .consume: return intrinsics.atomic_and_explicit(object, operand, .Consume)
|
||||
case .acquire: return intrinsics.atomic_and_explicit(object, operand, .Acquire)
|
||||
case .release: return intrinsics.atomic_and_explicit(object, operand, .Release)
|
||||
case .acq_rel: return intrinsics.atomic_and_explicit(object, operand, .Acq_Rel)
|
||||
case: fallthrough
|
||||
case .seq_cst: return intrinsics.atomic_and_explicit(object, operand, .Seq_Cst)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+87
-5
@@ -1,7 +1,9 @@
|
||||
package libc
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
@@ -11,7 +13,7 @@ when ODIN_OS == "windows" {
|
||||
FILE :: struct {}
|
||||
|
||||
// MSVCRT compatible.
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
_IOFBF :: 0x0000
|
||||
_IONBF :: 0x0004
|
||||
_IOLBF :: 0x0040
|
||||
@@ -46,7 +48,7 @@ when ODIN_OS == "windows" {
|
||||
}
|
||||
|
||||
// GLIBC and MUSL compatible.
|
||||
when ODIN_OS == "linux" {
|
||||
when ODIN_OS == .Linux {
|
||||
fpos_t :: struct #raw_union { _: [16]char, _: longlong, _: double, }
|
||||
|
||||
_IOFBF :: 0
|
||||
@@ -67,7 +69,7 @@ when ODIN_OS == "linux" {
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
TMP_MAX :: 10000
|
||||
TMP_MAX :: 308915776
|
||||
|
||||
foreign libc {
|
||||
stderr: ^FILE
|
||||
@@ -76,6 +78,86 @@ when ODIN_OS == "linux" {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .OpenBSD {
|
||||
fpos_t :: distinct i64
|
||||
|
||||
_IOFBF :: 0
|
||||
_IOLBF :: 1
|
||||
_IONBF :: 1
|
||||
|
||||
BUFSIZ :: 1024
|
||||
|
||||
EOF :: int(-1)
|
||||
|
||||
FOPEN_MAX :: 20
|
||||
FILENAME_MAX :: 1024
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
foreign libc {
|
||||
stderr: ^FILE
|
||||
stdin: ^FILE
|
||||
stdout: ^FILE
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .FreeBSD {
|
||||
fpos_t :: distinct i64
|
||||
|
||||
_IOFBF :: 0
|
||||
_IOLBF :: 1
|
||||
_IONBF :: 1
|
||||
|
||||
BUFSIZ :: 1024
|
||||
|
||||
EOF :: int(-1)
|
||||
|
||||
FOPEN_MAX :: 20
|
||||
FILENAME_MAX :: 1024
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
foreign libc {
|
||||
stderr: ^FILE
|
||||
stdin: ^FILE
|
||||
stdout: ^FILE
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
fpos_t :: distinct i64
|
||||
|
||||
_IOFBF :: 0
|
||||
_IOLBF :: 1
|
||||
_IONBF :: 2
|
||||
|
||||
BUFSIZ :: 1024
|
||||
|
||||
EOF :: int(-1)
|
||||
|
||||
FOPEN_MAX :: 20
|
||||
|
||||
FILENAME_MAX :: 1024
|
||||
|
||||
L_tmpnam :: 1024
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
TMP_MAX :: 308915776
|
||||
|
||||
foreign libc {
|
||||
@(link_name="__stderrp") stderr: ^FILE
|
||||
@(link_name="__stdinp") stdin: ^FILE
|
||||
@(link_name="__stdoutp") stdout: ^FILE
|
||||
}
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.21.4 Operations on files
|
||||
@@ -117,7 +199,7 @@ foreign libc {
|
||||
putchar :: proc() -> int ---
|
||||
puts :: proc(s: cstring) -> int ---
|
||||
ungetc :: proc(c: int, stream: ^FILE) -> int ---
|
||||
fread :: proc(ptr: rawptr, size: size_t, stream: ^FILE) -> size_t ---
|
||||
fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
||||
fwrite :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
||||
|
||||
// 7.21.9 File positioning functions
|
||||
|
||||
+22
-4
@@ -2,13 +2,15 @@ package libc
|
||||
|
||||
// 7.22 General utilities
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
RAND_MAX :: 0x7fff
|
||||
|
||||
@(private="file")
|
||||
@@ -22,7 +24,7 @@ when ODIN_OS == "windows" {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == "linux" {
|
||||
when ODIN_OS == .Linux {
|
||||
RAND_MAX :: 0x7fffffff
|
||||
|
||||
// GLIBC and MUSL only
|
||||
@@ -33,7 +35,23 @@ when ODIN_OS == "linux" {
|
||||
}
|
||||
|
||||
MB_CUR_MAX :: #force_inline proc() -> size_t {
|
||||
return __ctype_get_mb_cur_max()
|
||||
return size_t(__ctype_get_mb_cur_max())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
RAND_MAX :: 0x7fffffff
|
||||
|
||||
// GLIBC and MUSL only
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
___mb_cur_max :: proc() -> int ---
|
||||
}
|
||||
|
||||
MB_CUR_MAX :: #force_inline proc() -> size_t {
|
||||
return size_t(___mb_cur_max())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ import "core:runtime"
|
||||
|
||||
// 7.24 String handling
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ package libc
|
||||
thrd_start_t :: proc "c" (rawptr) -> int
|
||||
tss_dtor_t :: proc "c" (rawptr)
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc {
|
||||
"system:libucrt.lib",
|
||||
"system:msvcprt.lib"
|
||||
"system:msvcprt.lib",
|
||||
}
|
||||
|
||||
thrd_success :: 0 // _Thrd_success
|
||||
@@ -74,10 +74,10 @@ when ODIN_OS == "windows" {
|
||||
}
|
||||
|
||||
// GLIBC and MUSL compatible constants and types.
|
||||
when ODIN_OS == "linux" {
|
||||
when ODIN_OS == .Linux {
|
||||
foreign import libc {
|
||||
"system:c",
|
||||
"system:pthread"
|
||||
"system:pthread",
|
||||
}
|
||||
|
||||
thrd_success :: 0
|
||||
@@ -136,3 +136,8 @@ when ODIN_OS == "linux" {
|
||||
tss_set :: proc(key: tss_t, val: rawptr) -> int ---
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
// TODO: find out what this is meant to be!
|
||||
}
|
||||
|
||||
+13
-6
@@ -2,15 +2,17 @@ package libc
|
||||
|
||||
// 7.27 Date and time
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
// We enforce 64-bit time_t and timespec as there is no reason to use 32-bit as
|
||||
// we approach the 2038 problem. Windows has defaulted to this since VC8 (2005).
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign libc {
|
||||
// 7.27.2 Time manipulation functions
|
||||
clock :: proc() -> clock_t ---
|
||||
@@ -43,7 +45,7 @@ when ODIN_OS == "windows" {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.27.2 Time manipulation functions
|
||||
@@ -61,7 +63,12 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
||||
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
|
||||
}
|
||||
|
||||
CLOCKS_PER_SEC :: 1000000
|
||||
when ODIN_OS == .OpenBSD {
|
||||
CLOCKS_PER_SEC :: 100
|
||||
} else {
|
||||
CLOCKS_PER_SEC :: 1000000
|
||||
}
|
||||
|
||||
TIME_UTC :: 1
|
||||
|
||||
time_t :: distinct i64
|
||||
@@ -75,7 +82,7 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
|
||||
|
||||
tm :: struct {
|
||||
tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst: int,
|
||||
_: long,
|
||||
_: rawptr,
|
||||
tm_gmtoff: long,
|
||||
tm_zone: rawptr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package libc
|
||||
import "core:c"
|
||||
|
||||
char :: c.char // assuming -funsigned-char
|
||||
|
||||
schar :: c.schar
|
||||
short :: c.short
|
||||
int :: c.int
|
||||
long :: c.long
|
||||
|
||||
@@ -2,8 +2,10 @@ package libc
|
||||
|
||||
// 7.28 Unicode utilities
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ package libc
|
||||
|
||||
// 7.29 Extended multibyte and wide character utilities
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
+19
-5
@@ -2,20 +2,34 @@ package libc
|
||||
|
||||
// 7.30 Wide character classification and mapping utilities
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
when ODIN_OS == "windows" {
|
||||
when ODIN_OS == .Windows {
|
||||
wctrans_t :: distinct wchar_t
|
||||
wctype_t :: distinct ushort
|
||||
}
|
||||
|
||||
when ODIN_OS == "linux" {
|
||||
wctrans_t :: distinct rawptr
|
||||
} else when ODIN_OS == .Linux {
|
||||
wctrans_t :: distinct intptr_t
|
||||
wctype_t :: distinct ulong
|
||||
|
||||
} else when ODIN_OS == .Darwin {
|
||||
wctrans_t :: distinct int
|
||||
wctype_t :: distinct u32
|
||||
|
||||
} else when ODIN_OS == .OpenBSD {
|
||||
wctrans_t :: distinct rawptr
|
||||
wctype_t :: distinct rawptr
|
||||
|
||||
} else when ODIN_OS == .FreeBSD {
|
||||
wctrans_t :: distinct int
|
||||
wctype_t :: distinct ulong
|
||||
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
*/
|
||||
|
||||
|
||||
// package compress is a collection of utilities to aid with other compression packages
|
||||
package compress
|
||||
|
||||
import "core:io"
|
||||
@@ -44,7 +47,7 @@ when size_of(uintptr) == 8 {
|
||||
}
|
||||
|
||||
|
||||
Error :: union {
|
||||
Error :: union #shared_nil {
|
||||
General_Error,
|
||||
Deflate_Error,
|
||||
ZLIB_Error,
|
||||
@@ -55,6 +58,7 @@ Error :: union {
|
||||
}
|
||||
|
||||
General_Error :: enum {
|
||||
None = 0,
|
||||
File_Not_Found,
|
||||
Cannot_Open_File,
|
||||
File_Too_Short,
|
||||
@@ -73,6 +77,7 @@ General_Error :: enum {
|
||||
}
|
||||
|
||||
GZIP_Error :: enum {
|
||||
None = 0,
|
||||
Invalid_GZIP_Signature,
|
||||
Reserved_Flag_Set,
|
||||
Invalid_Extra_Data,
|
||||
@@ -97,6 +102,7 @@ GZIP_Error :: enum {
|
||||
}
|
||||
|
||||
ZIP_Error :: enum {
|
||||
None = 0,
|
||||
Invalid_ZIP_File_Signature,
|
||||
Unexpected_Signature,
|
||||
Insert_Next_Disk,
|
||||
@@ -104,6 +110,7 @@ ZIP_Error :: enum {
|
||||
}
|
||||
|
||||
ZLIB_Error :: enum {
|
||||
None = 0,
|
||||
Unsupported_Window_Size,
|
||||
FDICT_Unsupported,
|
||||
Unsupported_Compression_Level,
|
||||
@@ -111,6 +118,7 @@ ZLIB_Error :: enum {
|
||||
}
|
||||
|
||||
Deflate_Error :: enum {
|
||||
None = 0,
|
||||
Huffman_Bad_Sizes,
|
||||
Huffman_Bad_Code_Lengths,
|
||||
Inflate_Error,
|
||||
@@ -120,7 +128,6 @@ Deflate_Error :: enum {
|
||||
BType_3,
|
||||
}
|
||||
|
||||
|
||||
// General I/O context for ZLIB, LZW, etc.
|
||||
Context_Memory_Input :: struct #packed {
|
||||
input_data: []u8,
|
||||
@@ -136,7 +143,12 @@ Context_Memory_Input :: struct #packed {
|
||||
size_packed: i64,
|
||||
size_unpacked: i64,
|
||||
}
|
||||
#assert(size_of(Context_Memory_Input) == 64)
|
||||
when size_of(rawptr) == 8 {
|
||||
#assert(size_of(Context_Memory_Input) == 64)
|
||||
} else {
|
||||
// e.g. `-target:windows_i386`
|
||||
#assert(size_of(Context_Memory_Input) == 52)
|
||||
}
|
||||
|
||||
Context_Stream_Input :: struct #packed {
|
||||
input_data: []u8,
|
||||
@@ -171,8 +183,6 @@ Context_Stream_Input :: struct #packed {
|
||||
This simplifies end-of-stream handling where bits may be left in the bit buffer.
|
||||
*/
|
||||
|
||||
// TODO: Make these return compress.Error errors.
|
||||
|
||||
input_size_from_memory :: proc(z: ^Context_Memory_Input) -> (res: i64, err: Error) {
|
||||
return i64(len(z.input_data)), nil
|
||||
}
|
||||
@@ -470,4 +480,4 @@ discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
||||
consume_bits_lsb(z, discard)
|
||||
}
|
||||
|
||||
discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream};
|
||||
discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream}
|
||||
|
||||
@@ -45,7 +45,7 @@ main :: proc() {
|
||||
|
||||
if len(args) < 2 {
|
||||
stderr("No input file specified.\n")
|
||||
err := load(slice=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
if err == nil {
|
||||
stdout("Displaying test vector: ")
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
|
||||
@@ -66,7 +66,8 @@ OS :: enum u8 {
|
||||
_Unknown = 14,
|
||||
Unknown = 255,
|
||||
}
|
||||
OS_Name :: #partial [OS]string{
|
||||
OS_Name :: #sparse[OS]string{
|
||||
._Unknown = "",
|
||||
.FAT = "FAT",
|
||||
.Amiga = "Amiga",
|
||||
.VMS = "VMS/OpenVMS",
|
||||
@@ -99,9 +100,9 @@ E_GZIP :: compress.GZIP_Error
|
||||
E_ZLIB :: compress.ZLIB_Error
|
||||
E_Deflate :: compress.Deflate_Error
|
||||
|
||||
GZIP_MAX_PAYLOAD_SIZE :: int(max(u32le))
|
||||
GZIP_MAX_PAYLOAD_SIZE :: i64(max(u32le))
|
||||
|
||||
load :: proc{load_from_slice, load_from_file, load_from_context}
|
||||
load :: proc{load_from_bytes, load_from_file, load_from_context}
|
||||
|
||||
load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
@@ -111,16 +112,16 @@ load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_siz
|
||||
|
||||
err = E_General.File_Not_Found
|
||||
if ok {
|
||||
err = load_from_slice(data, buf, len(data), expected_output_size)
|
||||
err = load_from_bytes(data, buf, len(data), expected_output_size)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
load_from_slice :: proc(slice: []u8, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
load_from_bytes :: proc(data: []byte, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
buf := buf
|
||||
|
||||
z := &compress.Context_Memory_Input{
|
||||
input_data = slice,
|
||||
input_data = data,
|
||||
output = buf,
|
||||
}
|
||||
return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator)
|
||||
@@ -135,7 +136,7 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp
|
||||
|
||||
z.output = buf
|
||||
|
||||
if expected_output_size > GZIP_MAX_PAYLOAD_SIZE {
|
||||
if i64(expected_output_size) > i64(GZIP_MAX_PAYLOAD_SIZE) {
|
||||
return E_GZIP.Payload_Size_Exceeds_Max_Payload
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
This file was generated, so don't edit this by hand.
|
||||
Transliterated from https://github.com/Ed-von-Schleck/shoco/blob/master/shoco_model.h,
|
||||
which is an English word model.
|
||||
*/
|
||||
|
||||
// package shoco is an implementation of the shoco short string compressor
|
||||
package shoco
|
||||
|
||||
DEFAULT_MODEL :: Shoco_Model {
|
||||
min_char = 39,
|
||||
max_char = 122,
|
||||
characters_by_id = {
|
||||
'e', 'a', 'i', 'o', 't', 'h', 'n', 'r', 's', 'l', 'u', 'c', 'w', 'm', 'd', 'b', 'p', 'f', 'g', 'v', 'y', 'k', '-', 'H', 'M', 'T', '\'', 'B', 'x', 'I', 'W', 'L',
|
||||
},
|
||||
ids_by_character = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, -1, -1, -1, -1, -1, 23, 29, -1, -1, 31, 24, -1, -1, -1, -1, -1, -1, 25, -1, -1, 30, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 15, 11, 14, 0, 17, 18, 5, 2, -1, 21, 9, 13, 6, 3, 16, -1, 7, 8, 4, 10, 19, 12, 28, 20, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
},
|
||||
successors_by_bigram = {
|
||||
7, 4, 12, -1, 6, -1, 1, 0, 3, 5, -1, 9, -1, 8, 2, -1, 15, 14, -1, 10, 11, -1, -1, -1, -1, -1, -1, -1, 13, -1, -1, -1,
|
||||
1, -1, 6, -1, 1, -1, 0, 3, 2, 4, 15, 11, -1, 9, 5, 10, 13, -1, 12, 8, 7, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
9, 11, -1, 4, 2, -1, 0, 8, 1, 5, -1, 6, -1, 3, 7, 15, -1, 12, 10, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, 14, 7, 5, -1, 1, 2, 8, 9, 0, 15, 6, 4, 11, -1, 12, 3, -1, 10, -1, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
2, 4, 3, 1, 5, 0, -1, 6, 10, 9, 7, 12, 11, -1, -1, -1, -1, 13, -1, -1, 8, -1, 15, -1, -1, -1, 14, -1, -1, -1, -1, -1,
|
||||
0, 1, 2, 3, 4, -1, -1, 5, 9, 10, 6, -1, -1, 8, 15, 11, -1, 14, -1, -1, 7, -1, 13, -1, -1, -1, 12, -1, -1, -1, -1, -1,
|
||||
2, 8, 7, 4, 3, -1, 9, -1, 6, 11, -1, 5, -1, -1, 0, -1, -1, 14, 1, 15, 10, 12, -1, -1, -1, -1, 13, -1, -1, -1, -1, -1,
|
||||
0, 3, 1, 2, 6, -1, 9, 8, 4, 12, 13, 10, -1, 11, 7, -1, -1, 15, 14, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 6, 3, 4, 1, 2, -1, -1, 5, 10, 7, 9, 11, 12, -1, -1, 8, 14, -1, -1, 15, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 6, 2, 5, 9, -1, -1, -1, 10, 1, 8, -1, 12, 14, 4, -1, 15, 7, -1, 13, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
8, 10, 9, 15, 1, -1, 4, 0, 3, 2, -1, 6, -1, 12, 11, 13, 7, 14, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
1, 3, 6, 0, 4, 2, -1, 7, 13, 8, 9, 11, -1, -1, 15, -1, -1, -1, -1, -1, 10, 5, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
3, 0, 1, 4, -1, 2, 5, 6, 7, 8, -1, 14, -1, -1, 9, 15, -1, 12, -1, -1, -1, 10, 11, -1, -1, -1, 13, -1, -1, -1, -1, -1,
|
||||
0, 1, 3, 2, 15, -1, 12, -1, 7, 14, 4, -1, -1, 9, -1, 8, 5, 10, -1, -1, 6, -1, 13, -1, -1, -1, 11, -1, -1, -1, -1, -1,
|
||||
0, 3, 1, 2, -1, -1, 12, 6, 4, 9, 7, -1, -1, 14, 8, -1, -1, 15, 11, 13, 5, -1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 5, 7, 2, 10, 13, -1, 6, 8, 1, 3, -1, -1, 14, 15, 11, -1, -1, -1, 12, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 2, 6, 3, 7, 10, -1, 1, 9, 4, 8, -1, -1, 15, -1, 12, 5, -1, -1, -1, 11, -1, 13, -1, -1, -1, 14, -1, -1, -1, -1, -1,
|
||||
1, 3, 4, 0, 7, -1, 12, 2, 11, 8, 6, 13, -1, -1, -1, -1, -1, 5, -1, -1, 10, 15, 9, -1, -1, -1, 14, -1, -1, -1, -1, -1,
|
||||
1, 3, 5, 2, 13, 0, 9, 4, 7, 6, 8, -1, -1, 15, -1, 11, -1, -1, 10, -1, 14, -1, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 2, 1, 3, -1, -1, -1, 6, -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
1, 11, 4, 0, 3, -1, 13, 12, 2, 7, -1, -1, 15, 10, 5, 8, 14, -1, -1, -1, -1, -1, 9, -1, -1, -1, 6, -1, -1, -1, -1, -1,
|
||||
0, 9, 2, 14, 15, 4, 1, 13, 3, 5, -1, -1, 10, -1, -1, -1, -1, 6, 12, -1, 7, -1, 8, -1, -1, -1, 11, -1, -1, -1, -1, -1,
|
||||
-1, 2, 14, -1, 1, 5, 8, 7, 4, 12, -1, 6, 9, 11, 13, 3, 10, 15, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
0, 1, 3, 2, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
4, 3, 1, 5, -1, -1, -1, 0, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
2, 8, 4, 1, -1, 0, -1, 6, -1, -1, 5, -1, 7, -1, -1, -1, -1, -1, -1, -1, 10, -1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
12, 5, -1, -1, 1, -1, -1, 7, 0, 3, -1, 2, -1, 4, 6, -1, -1, -1, -1, 8, -1, -1, 15, -1, 13, 9, -1, -1, -1, -1, -1, 11,
|
||||
1, 3, 2, 4, -1, -1, -1, 5, -1, 7, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1,
|
||||
5, 3, 4, 12, 1, 6, -1, -1, -1, -1, 8, 2, -1, -1, -1, -1, 0, 9, -1, -1, 11, -1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, 0, -1, 1, 12, 3, -1, -1, -1, -1, 5, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, 6, -1, 10,
|
||||
2, 3, 1, 4, -1, 0, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1,
|
||||
5, 1, 3, 0, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 9, -1, -1, 6, -1, 7,
|
||||
},
|
||||
successors_reversed = {
|
||||
's', 't', 'c', 'l', 'm', 'a', 'd', 'r', 'v', 'T', 'A', 'L', 'e', 'M', 'Y', '-',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'-', 't', 'a', 'b', 's', 'h', 'c', 'r', 'n', 'w', 'p', 'm', 'l', 'd', 'i', 'f',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'u', 'e', 'i', 'a', 'o', 'r', 'y', 'l', 'I', 'E', 'R', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'e', 'a', 'o', 'i', 'u', 'A', 'y', 'E', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
't', 'n', 'f', 's', '\'', 'm', 'I', 'N', 'A', 'E', 'L', 'Z', 'r', 'V', 'R', 'C',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'o', 'a', 'y', 'i', 'u', 'e', 'I', 'L', 'D', '\'', 'E', 'Y', '\x00', '\x00', '\x00', '\x00',
|
||||
'r', 'i', 'y', 'a', 'e', 'o', 'u', 'Y', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'h', 'o', 'e', 'E', 'i', 'u', 'r', 'w', 'a', 'H', 'y', 'R', 'Z', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'h', 'i', 'e', 'a', 'o', 'r', 'I', 'y', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'n', 't', 's', 'r', 'l', 'd', 'i', 'y', 'v', 'm', 'b', 'c', 'g', 'p', 'k', 'u',
|
||||
'e', 'l', 'o', 'u', 'y', 'a', 'r', 'i', 's', 'j', 't', 'b', 'v', 'h', 'm', 'd',
|
||||
'o', 'e', 'h', 'a', 't', 'k', 'i', 'r', 'l', 'u', 'y', 'c', 'q', 's', '-', 'd',
|
||||
'e', 'i', 'o', 'a', 's', 'y', 'r', 'u', 'd', 'l', '-', 'g', 'n', 'v', 'm', 'f',
|
||||
'r', 'n', 'd', 's', 'a', 'l', 't', 'e', 'm', 'c', 'v', 'y', 'i', 'x', 'f', 'p',
|
||||
'o', 'e', 'r', 'a', 'i', 'f', 'u', 't', 'l', '-', 'y', 's', 'n', 'c', '\'', 'k',
|
||||
'h', 'e', 'o', 'a', 'r', 'i', 'l', 's', 'u', 'n', 'g', 'b', '-', 't', 'y', 'm',
|
||||
'e', 'a', 'i', 'o', 't', 'r', 'u', 'y', 'm', 's', 'l', 'b', '\'', '-', 'f', 'd',
|
||||
'n', 's', 't', 'm', 'o', 'l', 'c', 'd', 'r', 'e', 'g', 'a', 'f', 'v', 'z', 'b',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'e', 'n', 'i', 's', 'h', 'l', 'f', 'y', '-', 'a', 'w', '\'', 'g', 'r', 'o', 't',
|
||||
'e', 'l', 'i', 'y', 'd', 'o', 'a', 'f', 'u', 't', 's', 'k', 'w', 'v', 'm', 'p',
|
||||
'e', 'a', 'o', 'i', 'u', 'p', 'y', 's', 'b', 'm', 'f', '\'', 'n', '-', 'l', 't',
|
||||
'd', 'g', 'e', 't', 'o', 'c', 's', 'i', 'a', 'n', 'y', 'l', 'k', '\'', 'f', 'v',
|
||||
'u', 'n', 'r', 'f', 'm', 't', 'w', 'o', 's', 'l', 'v', 'd', 'p', 'k', 'i', 'c',
|
||||
'e', 'r', 'a', 'o', 'l', 'p', 'i', 't', 'u', 's', 'h', 'y', 'b', '-', '\'', 'm',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'e', 'i', 'o', 'a', 's', 'y', 't', 'd', 'r', 'n', 'c', 'm', 'l', 'u', 'g', 'f',
|
||||
'e', 't', 'h', 'i', 'o', 's', 'a', 'u', 'p', 'c', 'l', 'w', 'm', 'k', 'f', 'y',
|
||||
'h', 'o', 'e', 'i', 'a', 't', 'r', 'u', 'y', 'l', 's', 'w', 'c', 'f', '\'', '-',
|
||||
'r', 't', 'l', 's', 'n', 'g', 'c', 'p', 'e', 'i', 'a', 'd', 'm', 'b', 'f', 'o',
|
||||
'e', 'i', 'a', 'o', 'y', 'u', 'r', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'a', 'i', 'h', 'e', 'o', 'n', 'r', 's', 'l', 'd', 'k', '-', 'f', '\'', 'c', 'b',
|
||||
'p', 't', 'c', 'a', 'i', 'e', 'h', 'q', 'u', 'f', '-', 'y', 'o', '\x00', '\x00', '\x00',
|
||||
'o', 'e', 's', 't', 'i', 'd', '\'', 'l', 'b', '-', 'm', 'a', 'r', 'n', 'p', 'w',
|
||||
},
|
||||
|
||||
character_count = 32,
|
||||
successor_count = 16,
|
||||
|
||||
max_successor_n = 7,
|
||||
packs = {
|
||||
{ 0x80000000, 1, 2, { 26, 24, 24, 24, 24, 24, 24, 24 }, { 15, 3, 0, 0, 0, 0, 0, 0 }, 0xc0, 0x80 },
|
||||
{ 0xc0000000, 2, 4, { 25, 22, 19, 16, 16, 16, 16, 16 }, { 15, 7, 7, 7, 0, 0, 0, 0 }, 0xe0, 0xc0 },
|
||||
{ 0xe0000000, 4, 8, { 23, 19, 15, 11, 8, 5, 2, 0 }, { 31, 15, 15, 15, 7, 7, 7, 3 }, 0xf0, 0xe0 },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
|
||||
An implementation of [shoco](https://github.com/Ed-von-Schleck/shoco) by Christian Schramm.
|
||||
*/
|
||||
|
||||
// package shoco is an implementation of the shoco short string compressor
|
||||
package shoco
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:compress"
|
||||
|
||||
Shoco_Pack :: struct {
|
||||
word: u32,
|
||||
bytes_packed: i8,
|
||||
bytes_unpacked: i8,
|
||||
offsets: [8]u16,
|
||||
masks: [8]i16,
|
||||
header_mask: u8,
|
||||
header: u8,
|
||||
}
|
||||
|
||||
Shoco_Model :: struct {
|
||||
min_char: u8,
|
||||
max_char: u8,
|
||||
characters_by_id: []u8,
|
||||
ids_by_character: [256]i16,
|
||||
successors_by_bigram: []i8,
|
||||
successors_reversed: []u8,
|
||||
|
||||
character_count: u8,
|
||||
successor_count: u8,
|
||||
max_successor_n: i8,
|
||||
packs: []Shoco_Pack,
|
||||
}
|
||||
|
||||
compress_bound :: proc(uncompressed_size: int) -> (worst_case_compressed_size: int) {
|
||||
// Worst case compression happens when input is non-ASCII (128-255)
|
||||
// Encoded as 0x00 + the byte in question.
|
||||
return uncompressed_size * 2
|
||||
}
|
||||
|
||||
decompress_bound :: proc(compressed_size: int, model := DEFAULT_MODEL) -> (maximum_decompressed_size: int) {
|
||||
// Best case compression is 2:1
|
||||
most: f64
|
||||
for pack in model.packs {
|
||||
val := f64(compressed_size) / f64(pack.bytes_packed) * f64(pack.bytes_unpacked)
|
||||
most = max(most, val)
|
||||
}
|
||||
return int(most)
|
||||
}
|
||||
|
||||
find_best_encoding :: proc(indices: []i16, n_consecutive: i8, model := DEFAULT_MODEL) -> (res: int) {
|
||||
for p := len(model.packs); p > 0; p -= 1 {
|
||||
pack := model.packs[p - 1]
|
||||
if n_consecutive >= pack.bytes_unpacked {
|
||||
have_index := true
|
||||
for i := 0; i < int(pack.bytes_unpacked); i += 1 {
|
||||
if indices[i] > pack.masks[i] {
|
||||
have_index = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if have_index {
|
||||
return p - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
validate_model :: proc(model: Shoco_Model) -> (int, compress.Error) {
|
||||
if len(model.characters_by_id) != int(model.character_count) {
|
||||
return 0, .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
if len(model.successors_by_bigram) != int(model.character_count) * int(model.character_count) {
|
||||
return 0, .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
if len(model.successors_reversed) != int(model.successor_count) * int(model.max_char - model.min_char) {
|
||||
return 0, .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
// Model seems legit.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Decompresses into provided buffer.
|
||||
decompress_slice_to_output_buffer :: proc(input: []u8, output: []u8, model := DEFAULT_MODEL) -> (size: int, err: compress.Error) {
|
||||
inp, inp_end := 0, len(input)
|
||||
out, out_end := 0, len(output)
|
||||
|
||||
validate_model(model) or_return
|
||||
|
||||
for inp < inp_end {
|
||||
val := transmute(i8)input[inp]
|
||||
mark := int(-1)
|
||||
|
||||
for val < 0 {
|
||||
val <<= 1
|
||||
mark += 1
|
||||
}
|
||||
|
||||
if mark > len(model.packs) {
|
||||
return out, .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
if mark < 0 {
|
||||
if out >= out_end {
|
||||
return out, .Output_Too_Short
|
||||
}
|
||||
|
||||
// Ignore the sentinel value for non-ASCII chars
|
||||
if input[inp] == 0x00 {
|
||||
inp += 1
|
||||
if inp >= inp_end {
|
||||
return out, .Stream_Too_Short
|
||||
}
|
||||
}
|
||||
output[out] = input[inp]
|
||||
inp, out = inp + 1, out + 1
|
||||
|
||||
} else {
|
||||
pack := model.packs[mark]
|
||||
|
||||
if out + int(pack.bytes_unpacked) > out_end {
|
||||
return out, .Output_Too_Short
|
||||
} else if inp + int(pack.bytes_packed) > inp_end {
|
||||
return out, .Stream_Too_Short
|
||||
}
|
||||
|
||||
code := intrinsics.unaligned_load((^u32)(&input[inp]))
|
||||
when ODIN_ENDIAN == .Little {
|
||||
code = intrinsics.byte_swap(code)
|
||||
}
|
||||
|
||||
// Unpack the leading char
|
||||
offset := pack.offsets[0]
|
||||
mask := pack.masks[0]
|
||||
|
||||
last_chr := model.characters_by_id[(code >> offset) & u32(mask)]
|
||||
output[out] = last_chr
|
||||
|
||||
// Unpack the successor chars
|
||||
for i := 1; i < int(pack.bytes_unpacked); i += 1 {
|
||||
offset = pack.offsets[i]
|
||||
mask = pack.masks[i]
|
||||
|
||||
index_major := u32(last_chr - model.min_char) * u32(model.successor_count)
|
||||
index_minor := (code >> offset) & u32(mask)
|
||||
|
||||
last_chr = model.successors_reversed[index_major + index_minor]
|
||||
|
||||
output[out + i] = last_chr
|
||||
}
|
||||
|
||||
out += int(pack.bytes_unpacked)
|
||||
inp += int(pack.bytes_packed)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
decompress_slice_to_string :: proc(input: []u8, model := DEFAULT_MODEL, allocator := context.allocator) -> (res: string, err: compress.Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
if len(input) == 0 {
|
||||
return "", .Stream_Too_Short
|
||||
}
|
||||
|
||||
max_output_size := decompress_bound(len(input), model)
|
||||
|
||||
buf: [dynamic]u8
|
||||
if !resize(&buf, max_output_size) {
|
||||
return "", .Out_Of_Memory
|
||||
}
|
||||
|
||||
length, result := decompress_slice_to_output_buffer(input, buf[:])
|
||||
resize(&buf, length)
|
||||
return string(buf[:]), result
|
||||
}
|
||||
decompress :: proc{decompress_slice_to_output_buffer, decompress_slice_to_string}
|
||||
|
||||
compress_string_to_buffer :: proc(input: string, output: []u8, model := DEFAULT_MODEL, allocator := context.allocator) -> (size: int, err: compress.Error) {
|
||||
inp, inp_end := 0, len(input)
|
||||
out, out_end := 0, len(output)
|
||||
output := output
|
||||
|
||||
validate_model(model) or_return
|
||||
|
||||
indices := make([]i16, model.max_successor_n + 1)
|
||||
defer delete(indices)
|
||||
|
||||
last_resort := false
|
||||
|
||||
encode: for inp < inp_end {
|
||||
if last_resort {
|
||||
last_resort = false
|
||||
|
||||
if input[inp] & 0x80 == 0x80 {
|
||||
// Non-ASCII case
|
||||
if out + 2 > out_end {
|
||||
return out, .Output_Too_Short
|
||||
}
|
||||
|
||||
// Put in a sentinel byte
|
||||
output[out] = 0x00
|
||||
out += 1
|
||||
} else {
|
||||
// An ASCII byte
|
||||
if out + 1 > out_end {
|
||||
return out, .Output_Too_Short
|
||||
}
|
||||
}
|
||||
output[out] = input[inp]
|
||||
out, inp = out + 1, inp + 1
|
||||
} else {
|
||||
// Find the longest string of known successors
|
||||
indices[0] = model.ids_by_character[input[inp]]
|
||||
last_chr_index := indices[0]
|
||||
|
||||
if last_chr_index < 0 {
|
||||
last_resort = true
|
||||
continue encode
|
||||
}
|
||||
|
||||
rest := inp_end - inp
|
||||
n_consecutive: i8 = 1
|
||||
for ; n_consecutive <= model.max_successor_n; n_consecutive += 1 {
|
||||
if inp_end > 0 && int(n_consecutive) == rest {
|
||||
break
|
||||
}
|
||||
|
||||
current_index := model.ids_by_character[input[inp + int(n_consecutive)]]
|
||||
if current_index < 0 { // '\0' is always -1
|
||||
break
|
||||
}
|
||||
|
||||
successor_index := model.successors_by_bigram[last_chr_index * i16(model.character_count) + current_index]
|
||||
if successor_index < 0 {
|
||||
break
|
||||
}
|
||||
|
||||
indices[n_consecutive] = i16(successor_index)
|
||||
last_chr_index = current_index
|
||||
}
|
||||
|
||||
if n_consecutive < 2 {
|
||||
last_resort = true
|
||||
continue encode
|
||||
}
|
||||
|
||||
pack_n := find_best_encoding(indices, n_consecutive)
|
||||
if pack_n >= 0 {
|
||||
if out + int(model.packs[pack_n].bytes_packed) > out_end {
|
||||
return out, .Output_Too_Short
|
||||
}
|
||||
|
||||
pack := model.packs[pack_n]
|
||||
code := pack.word
|
||||
|
||||
for i := 0; i < int(pack.bytes_unpacked); i += 1 {
|
||||
code |= u32(indices[i]) << pack.offsets[i]
|
||||
}
|
||||
|
||||
// In the little-endian world, we need to swap what's in the register to match the memory representation.
|
||||
when ODIN_ENDIAN == .Little {
|
||||
code = intrinsics.byte_swap(code)
|
||||
}
|
||||
out_ptr := raw_data(output[out:])
|
||||
|
||||
switch pack.bytes_packed {
|
||||
case 4:
|
||||
intrinsics.unaligned_store(transmute(^u32)out_ptr, code)
|
||||
case 2:
|
||||
intrinsics.unaligned_store(transmute(^u16)out_ptr, u16(code))
|
||||
case 1:
|
||||
intrinsics.unaligned_store(transmute(^u8)out_ptr, u8(code))
|
||||
case:
|
||||
return out, .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
out += int(pack.bytes_packed)
|
||||
inp += int(pack.bytes_unpacked)
|
||||
} else {
|
||||
last_resort = true
|
||||
continue encode
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
compress_string :: proc(input: string, model := DEFAULT_MODEL, allocator := context.allocator) -> (output: []u8, err: compress.Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
if len(input) == 0 {
|
||||
return {}, .Stream_Too_Short
|
||||
}
|
||||
|
||||
max_output_size := compress_bound(len(input))
|
||||
|
||||
buf: [dynamic]u8
|
||||
if !resize(&buf, max_output_size) {
|
||||
return {}, .Out_Of_Memory
|
||||
}
|
||||
|
||||
length, result := compress_string_to_buffer(input, buf[:])
|
||||
resize(&buf, length)
|
||||
return buf[:length], result
|
||||
}
|
||||
compress :: proc{compress_string_to_buffer, compress_string}
|
||||
@@ -47,10 +47,10 @@ Options :: struct {
|
||||
level: u8,
|
||||
}
|
||||
|
||||
Error :: compress.Error
|
||||
E_General :: compress.General_Error
|
||||
E_ZLIB :: compress.ZLIB_Error
|
||||
E_Deflate :: compress.Deflate_Error
|
||||
Error :: compress.Error
|
||||
General_Error :: compress.General_Error
|
||||
ZLIB_Error :: compress.ZLIB_Error
|
||||
Deflate_Error :: compress.Deflate_Error
|
||||
|
||||
DEFLATE_MAX_CHUNK_SIZE :: 65535
|
||||
DEFLATE_MAX_LITERAL_SIZE :: 65535
|
||||
@@ -111,9 +111,9 @@ ZFAST_MASK :: ((1 << ZFAST_BITS) - 1)
|
||||
*/
|
||||
Huffman_Table :: struct {
|
||||
fast: [1 << ZFAST_BITS]u16,
|
||||
firstcode: [16]u16,
|
||||
firstcode: [17]u16,
|
||||
maxcode: [17]int,
|
||||
firstsymbol: [16]u16,
|
||||
firstsymbol: [17]u16,
|
||||
size: [288]u8,
|
||||
value: [288]u16,
|
||||
}
|
||||
@@ -244,7 +244,7 @@ allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_T
|
||||
@(optimization_mode="speed")
|
||||
build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
||||
sizes: [HUFFMAN_MAX_BITS+1]int
|
||||
next_code: [HUFFMAN_MAX_BITS]int
|
||||
next_code: [HUFFMAN_MAX_BITS+1]int
|
||||
|
||||
k := int(0)
|
||||
|
||||
@@ -256,21 +256,21 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
||||
}
|
||||
sizes[0] = 0
|
||||
|
||||
for i in 1..<(HUFFMAN_MAX_BITS+1) {
|
||||
for i in 1 ..< HUFFMAN_MAX_BITS {
|
||||
if sizes[i] > (1 << uint(i)) {
|
||||
return E_Deflate.Huffman_Bad_Sizes
|
||||
return .Huffman_Bad_Sizes
|
||||
}
|
||||
}
|
||||
code := int(0)
|
||||
|
||||
for i in 1..<HUFFMAN_MAX_BITS {
|
||||
for i in 1 ..= HUFFMAN_MAX_BITS {
|
||||
next_code[i] = code
|
||||
z.firstcode[i] = u16(code)
|
||||
z.firstsymbol[i] = u16(k)
|
||||
code = code + sizes[i]
|
||||
if sizes[i] != 0 {
|
||||
if code - 1 >= (1 << u16(i)) {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
||||
return .Huffman_Bad_Code_Lengths
|
||||
}
|
||||
}
|
||||
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i))
|
||||
@@ -314,15 +314,15 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
|
||||
s += 1
|
||||
}
|
||||
if s >= 16 {
|
||||
return 0, E_Deflate.Bad_Huffman_Code
|
||||
return 0, .Bad_Huffman_Code
|
||||
}
|
||||
// code size is s, so:
|
||||
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s])
|
||||
if b >= size_of(t.size) {
|
||||
return 0, E_Deflate.Bad_Huffman_Code
|
||||
return 0, .Bad_Huffman_Code
|
||||
}
|
||||
if t.size[b] != s {
|
||||
return 0, E_Deflate.Bad_Huffman_Code
|
||||
return 0, .Bad_Huffman_Code
|
||||
}
|
||||
|
||||
compress.consume_bits_lsb(z, s)
|
||||
@@ -335,11 +335,11 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
|
||||
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||
if z.num_bits < 16 {
|
||||
if z.num_bits > 63 {
|
||||
return 0, E_ZLIB.Code_Buffer_Malformed
|
||||
return 0, .Code_Buffer_Malformed
|
||||
}
|
||||
compress.refill_lsb(z)
|
||||
if z.num_bits > 63 {
|
||||
return 0, E_General.Stream_Too_Short
|
||||
return 0, .Stream_Too_Short
|
||||
}
|
||||
}
|
||||
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]
|
||||
@@ -361,7 +361,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
|
||||
if value < 256 {
|
||||
e := write_byte(z, u8(value))
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short
|
||||
return .Output_Too_Short
|
||||
}
|
||||
} else {
|
||||
if value == 256 {
|
||||
@@ -377,7 +377,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
|
||||
|
||||
value, e = decode_huffman(z, z_offset)
|
||||
if e != nil {
|
||||
return E_Deflate.Bad_Huffman_Code
|
||||
return .Bad_Huffman_Code
|
||||
}
|
||||
|
||||
distance := Z_DIST_BASE[value]
|
||||
@@ -387,7 +387,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
|
||||
|
||||
if z.bytes_written < i64(distance) {
|
||||
// Distance is longer than we've decoded so far.
|
||||
return E_Deflate.Bad_Distance
|
||||
return .Bad_Distance
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -405,14 +405,14 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
|
||||
c := z.output.buf[z.bytes_written - i64(distance)]
|
||||
e := repl_byte(z, length, c)
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short
|
||||
return .Output_Too_Short
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if length > 0 {
|
||||
e := repl_bytes(z, length, distance)
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short
|
||||
return .Output_Too_Short
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -432,25 +432,25 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
|
||||
if !raw {
|
||||
size, size_err := compress.input_size(ctx)
|
||||
if size < 6 || size_err != nil {
|
||||
return E_General.Stream_Too_Short
|
||||
return .Stream_Too_Short
|
||||
}
|
||||
|
||||
cmf, _ := compress.read_u8(ctx)
|
||||
|
||||
method := Compression_Method(cmf & 0xf)
|
||||
if method != .DEFLATE {
|
||||
return E_General.Unknown_Compression_Method
|
||||
return .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
if cinfo := (cmf >> 4) & 0xf; cinfo > 7 {
|
||||
return E_ZLIB.Unsupported_Window_Size
|
||||
return .Unsupported_Window_Size
|
||||
}
|
||||
flg, _ := compress.read_u8(ctx)
|
||||
|
||||
fcheck := flg & 0x1f
|
||||
fcheck_computed := (cmf << 8 | flg) & 0x1f
|
||||
if fcheck != fcheck_computed {
|
||||
return E_General.Checksum_Failed
|
||||
return .Checksum_Failed
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -458,7 +458,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
|
||||
They're application specific and PNG doesn't use them.
|
||||
*/
|
||||
if fdict := (flg >> 5) & 1; fdict != 0 {
|
||||
return E_ZLIB.FDICT_Unsupported
|
||||
return .FDICT_Unsupported
|
||||
}
|
||||
|
||||
// flevel := Compression_Level((flg >> 6) & 3);
|
||||
@@ -485,7 +485,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
|
||||
output_hash := hash.adler32(ctx.output.buf[:])
|
||||
|
||||
if output_hash != u32(adler) {
|
||||
return E_General.Checksum_Failed
|
||||
return .Checksum_Failed
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -538,23 +538,24 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
||||
final = compress.read_bits_lsb(z, 1)
|
||||
type = compress.read_bits_lsb(z, 2)
|
||||
|
||||
// fmt.printf("Final: %v | Type: %v\n", final, type);
|
||||
// fmt.printf("Final: %v | Type: %v\n", final, type)
|
||||
|
||||
switch type {
|
||||
case 0:
|
||||
// fmt.printf("Method 0: STORED\n")
|
||||
// Uncompressed block
|
||||
|
||||
// Discard bits until next byte boundary
|
||||
compress.discard_to_next_byte_lsb(z)
|
||||
|
||||
uncompressed_len := i16(compress.read_bits_lsb(z, 16))
|
||||
length_check := i16(compress.read_bits_lsb(z, 16))
|
||||
uncompressed_len := u16(compress.read_bits_lsb(z, 16))
|
||||
length_check := u16(compress.read_bits_lsb(z, 16))
|
||||
|
||||
// fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check);
|
||||
// fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check)
|
||||
|
||||
|
||||
if ~uncompressed_len != length_check {
|
||||
return E_Deflate.Len_Nlen_Mismatch
|
||||
return .Len_Nlen_Mismatch
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -567,10 +568,12 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
||||
write_byte(z, u8(lit))
|
||||
uncompressed_len -= 1
|
||||
}
|
||||
assert(uncompressed_len == 0)
|
||||
|
||||
case 3:
|
||||
return E_Deflate.BType_3
|
||||
return .BType_3
|
||||
case:
|
||||
// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
|
||||
// fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type)
|
||||
if type == 1 {
|
||||
// Use fixed code lengths.
|
||||
build_huffman(z_repeat, Z_FIXED_LENGTH[:]) or_return
|
||||
@@ -601,7 +604,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
||||
c = decode_huffman(z, codelength_ht) or_return
|
||||
|
||||
if c < 0 || c >= 19 {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
||||
return .Huffman_Bad_Code_Lengths
|
||||
}
|
||||
if c < 16 {
|
||||
lencodes[n] = u8(c)
|
||||
@@ -613,7 +616,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
||||
case 16:
|
||||
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3)
|
||||
if n == 0 {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
||||
return .Huffman_Bad_Code_Lengths
|
||||
}
|
||||
fill = lencodes[n - 1]
|
||||
case 17:
|
||||
@@ -621,11 +624,11 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
||||
case 18:
|
||||
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11)
|
||||
case:
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
||||
return .Huffman_Bad_Code_Lengths
|
||||
}
|
||||
|
||||
if ntot - n < u32(c) {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
||||
return .Huffman_Bad_Code_Lengths
|
||||
}
|
||||
|
||||
nc := n + u32(c)
|
||||
@@ -636,7 +639,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
|
||||
}
|
||||
|
||||
if n != ntot {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
||||
return .Huffman_Bad_Code_Lengths
|
||||
}
|
||||
|
||||
build_huffman(z_repeat, lencodes[:hlit]) or_return
|
||||
@@ -674,4 +677,4 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals
|
||||
return inflate_raw(z=&ctx, expected_output_size=expected_output_size)
|
||||
}
|
||||
|
||||
inflate :: proc{inflate_from_context, inflate_from_byte_array};
|
||||
inflate :: proc{inflate_from_context, inflate_from_byte_array}
|
||||
|
||||
@@ -1,216 +0,0 @@
|
||||
package container
|
||||
|
||||
import "core:mem"
|
||||
import "core:runtime"
|
||||
|
||||
Array :: struct($T: typeid) {
|
||||
data: ^T,
|
||||
len: int,
|
||||
cap: int,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
ARRAY_DEFAULT_CAPACITY :: 16
|
||||
|
||||
/*
|
||||
array_init :: proc {
|
||||
array_init_none,
|
||||
array_init_len,
|
||||
array_init_len_cap,
|
||||
}
|
||||
array_init
|
||||
array_delete
|
||||
array_len
|
||||
array_cap
|
||||
array_space
|
||||
array_slice
|
||||
array_get
|
||||
array_get_ptr
|
||||
array_set
|
||||
array_reserve
|
||||
array_resize
|
||||
array_push = array_append :: proc{
|
||||
array_push_back,
|
||||
array_push_back_elems,
|
||||
}
|
||||
array_push_front
|
||||
array_pop_back
|
||||
array_pop_front
|
||||
array_consume
|
||||
array_trim
|
||||
array_clear
|
||||
array_clone
|
||||
array_set_capacity
|
||||
array_grow
|
||||
*/
|
||||
|
||||
|
||||
array_init_none :: proc(a: ^$A/Array, allocator := context.allocator) {
|
||||
array_init_len_cap(a, 0, ARRAY_DEFAULT_CAPACITY, allocator)
|
||||
}
|
||||
array_init_len :: proc(a: ^$A/Array, len: int, allocator := context.allocator) {
|
||||
array_init_len_cap(a, len, len, allocator)
|
||||
}
|
||||
array_init_len_cap :: proc(a: ^$A/Array($T), len: int, cap: int, allocator := context.allocator) {
|
||||
a.allocator = allocator
|
||||
a.data = (^T)(mem.alloc(size_of(T)*cap, align_of(T), a.allocator))
|
||||
a.len = len
|
||||
a.cap = cap
|
||||
}
|
||||
|
||||
array_init :: proc{array_init_none, array_init_len, array_init_len_cap}
|
||||
|
||||
array_delete :: proc(a: $A/Array) {
|
||||
mem.free(a.data, a.allocator)
|
||||
}
|
||||
|
||||
array_len :: proc(a: $A/Array) -> int {
|
||||
return a.len
|
||||
}
|
||||
|
||||
array_cap :: proc(a: $A/Array) -> int {
|
||||
return a.cap
|
||||
}
|
||||
|
||||
array_space :: proc(a: $A/Array) -> int {
|
||||
return a.cap - a.len
|
||||
}
|
||||
|
||||
array_slice :: proc(a: $A/Array($T)) -> []T {
|
||||
s := mem.Raw_Slice{a.data, a.len}
|
||||
return transmute([]T)s
|
||||
}
|
||||
|
||||
array_cap_slice :: proc(a: $A/Array($T)) -> []T {
|
||||
s := mem.Raw_Slice{a.data, a.cap}
|
||||
return transmute([]T)s
|
||||
}
|
||||
|
||||
array_get :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> T {
|
||||
runtime.bounds_check_error_loc(loc, index, array_len(a))
|
||||
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^
|
||||
}
|
||||
array_get_ptr :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, index, array_len(a))
|
||||
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))
|
||||
}
|
||||
|
||||
array_set :: proc(a: ^$A/Array($T), index: int, item: T, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, index, array_len(a^))
|
||||
(^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item
|
||||
}
|
||||
|
||||
|
||||
array_reserve :: proc(a: ^$A/Array, capacity: int) {
|
||||
if capacity > a.len {
|
||||
array_set_capacity(a, capacity)
|
||||
}
|
||||
}
|
||||
|
||||
array_resize :: proc(a: ^$A/Array, length: int) {
|
||||
if length > a.len {
|
||||
array_set_capacity(a, length)
|
||||
}
|
||||
a.len = length
|
||||
}
|
||||
|
||||
|
||||
|
||||
array_push_back :: proc(a: ^$A/Array($T), item: T) {
|
||||
if array_space(a^) == 0 {
|
||||
array_grow(a)
|
||||
}
|
||||
|
||||
a.len += 1
|
||||
array_set(a, a.len-1, item)
|
||||
}
|
||||
|
||||
array_push_front :: proc(a: ^$A/Array($T), item: T) {
|
||||
if array_space(a^) == 0 {
|
||||
array_grow(a)
|
||||
}
|
||||
|
||||
a.len += 1
|
||||
data := array_slice(a^)
|
||||
copy(data[1:], data[:])
|
||||
data[0] = item
|
||||
}
|
||||
|
||||
array_pop_back :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc)
|
||||
item := array_get(a^, a.len-1)
|
||||
a.len -= 1
|
||||
return item
|
||||
}
|
||||
|
||||
array_pop_front :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc)
|
||||
item := array_get(a^, 0)
|
||||
s := array_slice(a^)
|
||||
copy(s[:], s[1:])
|
||||
a.len -= 1
|
||||
return item
|
||||
}
|
||||
|
||||
|
||||
array_consume :: proc(a: ^$A/Array($T), count: int, loc := #caller_location) {
|
||||
assert(condition=a.len >= count, loc=loc)
|
||||
a.len -= count
|
||||
}
|
||||
|
||||
|
||||
array_trim :: proc(a: ^$A/Array($T)) {
|
||||
array_set_capacity(a, a.len)
|
||||
}
|
||||
|
||||
array_clear :: proc(a: ^$A/Array($T)) {
|
||||
array_resize(a, 0)
|
||||
}
|
||||
|
||||
array_clone :: proc(a: $A/Array($T), allocator := context.allocator) -> A {
|
||||
res: A
|
||||
array_init(&res, array_len(a), array_len(a), allocator)
|
||||
copy(array_slice(res), array_slice(a))
|
||||
return res
|
||||
}
|
||||
|
||||
array_push_back_elems :: proc(a: ^$A/Array($T), items: ..T) {
|
||||
if array_space(a^) < len(items) {
|
||||
array_grow(a, a.len + len(items))
|
||||
}
|
||||
offset := a.len
|
||||
data := array_cap_slice(a^)
|
||||
n := copy(data[a.len:], items)
|
||||
a.len += n
|
||||
}
|
||||
|
||||
array_push :: proc{array_push_back, array_push_back_elems}
|
||||
array_append :: proc{array_push_back, array_push_back_elems}
|
||||
|
||||
array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) {
|
||||
if new_capacity == a.cap {
|
||||
return
|
||||
}
|
||||
|
||||
if new_capacity < a.len {
|
||||
array_resize(a, new_capacity)
|
||||
}
|
||||
|
||||
new_data: ^T
|
||||
if new_capacity > 0 {
|
||||
if a.allocator.procedure == nil {
|
||||
a.allocator = context.allocator
|
||||
}
|
||||
new_data = (^T)(mem.alloc(size_of(T)*new_capacity, align_of(T), a.allocator))
|
||||
if new_data != nil {
|
||||
mem.copy(new_data, a.data, size_of(T)*a.len)
|
||||
}
|
||||
}
|
||||
mem.free(a.data, a.allocator)
|
||||
a.data = new_data
|
||||
a.cap = new_capacity
|
||||
}
|
||||
array_grow :: proc(a: ^$A/Array, min_capacity: int = 0) {
|
||||
new_capacity := max(array_len(a^)*2 + 8, min_capacity)
|
||||
array_set_capacity(a, new_capacity)
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
package dynamic_bit_array
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:mem"
|
||||
|
||||
/*
|
||||
Note that these constants are dependent on the backing being a u64.
|
||||
*/
|
||||
@(private="file")
|
||||
INDEX_SHIFT :: 6
|
||||
|
||||
@(private="file")
|
||||
INDEX_MASK :: 63
|
||||
|
||||
@(private="file")
|
||||
NUM_BITS :: 64
|
||||
|
||||
Bit_Array :: struct {
|
||||
bits: [dynamic]u64,
|
||||
bias: int,
|
||||
max_index: int,
|
||||
free_pointer: bool,
|
||||
}
|
||||
|
||||
Bit_Array_Iterator :: struct {
|
||||
array: ^Bit_Array,
|
||||
word_idx: int,
|
||||
bit_idx: uint,
|
||||
}
|
||||
|
||||
/*
|
||||
In:
|
||||
- ba: ^Bit_Array - the array to iterate over
|
||||
|
||||
Out:
|
||||
- it: ^Bit_Array_Iterator - the iterator that holds iteration state
|
||||
*/
|
||||
make_iterator :: proc (ba: ^Bit_Array) -> (it: Bit_Array_Iterator) {
|
||||
return Bit_Array_Iterator { array = ba }
|
||||
}
|
||||
|
||||
/*
|
||||
In:
|
||||
- it: ^Bit_Array_Iterator - the iterator struct that holds the state.
|
||||
|
||||
Out:
|
||||
- set: bool - the state of the bit at `index`
|
||||
- index: int - the next bit of the Bit_Array referenced by `it`.
|
||||
- ok: bool - `true` if the iterator returned a valid index,
|
||||
`false` if there were no more bits
|
||||
*/
|
||||
iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) {
|
||||
index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
|
||||
if index > it.array.max_index { return false, 0, false }
|
||||
|
||||
word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
|
||||
set = (word >> it.bit_idx & 1) == 1
|
||||
|
||||
it.bit_idx += 1
|
||||
if it.bit_idx >= NUM_BITS {
|
||||
it.bit_idx = 0
|
||||
it.word_idx += 1
|
||||
}
|
||||
|
||||
return set, index, true
|
||||
}
|
||||
|
||||
/*
|
||||
In:
|
||||
- it: ^Bit_Array_Iterator - the iterator struct that holds the state.
|
||||
|
||||
Out:
|
||||
- index: int - the next set bit of the Bit_Array referenced by `it`.
|
||||
- ok: bool - `true` if the iterator returned a valid index,
|
||||
`false` if there were no more bits set
|
||||
*/
|
||||
iterate_by_set :: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
|
||||
return iterate_internal_(it, true)
|
||||
}
|
||||
|
||||
/*
|
||||
In:
|
||||
- it: ^Bit_Array_Iterator - the iterator struct that holds the state.
|
||||
|
||||
Out:
|
||||
- index: int - the next unset bit of the Bit_Array referenced by `it`.
|
||||
- ok: bool - `true` if the iterator returned a valid index,
|
||||
`false` if there were no more unset bits
|
||||
*/
|
||||
iterate_by_unset:: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
|
||||
return iterate_internal_(it, false)
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) {
|
||||
word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
|
||||
when ! ITERATE_SET_BITS { word = ~word }
|
||||
|
||||
// if the word is empty or we have already gone over all the bits in it,
|
||||
// b.bit_idx is greater than the index of any set bit in the word,
|
||||
// meaning that word >> b.bit_idx == 0.
|
||||
for it.word_idx < len(it.array.bits) && word >> it.bit_idx == 0 {
|
||||
it.word_idx += 1
|
||||
it.bit_idx = 0
|
||||
word = it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
|
||||
when ! ITERATE_SET_BITS { word = ~word }
|
||||
}
|
||||
|
||||
// if we are iterating the set bits, reaching the end of the array means we have no more bits to check
|
||||
when ITERATE_SET_BITS {
|
||||
if it.word_idx >= len(it.array.bits) {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
// reaching here means that the word has some set bits
|
||||
it.bit_idx += uint(intrinsics.count_trailing_zeros(word >> it.bit_idx))
|
||||
index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
|
||||
|
||||
it.bit_idx += 1
|
||||
if it.bit_idx >= NUM_BITS {
|
||||
it.bit_idx = 0
|
||||
it.word_idx += 1
|
||||
}
|
||||
return index, index <= it.array.max_index
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
In:
|
||||
- ba: ^Bit_Array - a pointer to the Bit Array
|
||||
- index: The bit index. Can be an enum member.
|
||||
|
||||
Out:
|
||||
- res: The bit you're interested in.
|
||||
- ok: Whether the index was valid. Returns `false` if the index is smaller than the bias.
|
||||
|
||||
The `ok` return value may be ignored.
|
||||
*/
|
||||
get :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (res: bool, ok: bool) {
|
||||
idx := int(index) - ba.bias
|
||||
|
||||
if ba == nil || int(index) < ba.bias { return false, false }
|
||||
context.allocator = allocator
|
||||
|
||||
leg_index := idx >> INDEX_SHIFT
|
||||
bit_index := idx & INDEX_MASK
|
||||
|
||||
/*
|
||||
If we `get` a bit that doesn't fit in the Bit Array, it's naturally `false`.
|
||||
This early-out prevents unnecessary resizing.
|
||||
*/
|
||||
if leg_index + 1 > len(ba.bits) { return false, true }
|
||||
|
||||
val := u64(1 << uint(bit_index))
|
||||
res = ba.bits[leg_index] & val == val
|
||||
|
||||
return res, true
|
||||
}
|
||||
|
||||
/*
|
||||
In:
|
||||
- ba: ^Bit_Array - a pointer to the Bit Array
|
||||
- index: The bit index. Can be an enum member.
|
||||
|
||||
Out:
|
||||
- ok: Whether or not we managed to set requested bit.
|
||||
|
||||
`set` automatically resizes the Bit Array to accommodate the requested index if needed.
|
||||
*/
|
||||
set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
|
||||
|
||||
idx := int(index) - ba.bias
|
||||
|
||||
if ba == nil || int(index) < ba.bias { return false }
|
||||
context.allocator = allocator
|
||||
|
||||
leg_index := idx >> INDEX_SHIFT
|
||||
bit_index := idx & INDEX_MASK
|
||||
|
||||
resize_if_needed(ba, leg_index) or_return
|
||||
|
||||
ba.max_index = max(idx, ba.max_index)
|
||||
ba.bits[leg_index] |= 1 << uint(bit_index)
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
In:
|
||||
- ba: ^Bit_Array - a pointer to the Bit Array
|
||||
- index: The bit index. Can be an enum member.
|
||||
|
||||
Out:
|
||||
- ok: Whether or not we managed to unset requested bit.
|
||||
|
||||
`unset` automatically resizes the Bit Array to accommodate the requested index if needed.
|
||||
*/
|
||||
unset :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
|
||||
|
||||
idx := int(index) - ba.bias
|
||||
|
||||
if ba == nil || int(index) < ba.bias { return false }
|
||||
context.allocator = allocator
|
||||
|
||||
leg_index := idx >> INDEX_SHIFT
|
||||
bit_index := idx & INDEX_MASK
|
||||
|
||||
resize_if_needed(ba, leg_index) or_return
|
||||
|
||||
ba.max_index = max(idx, ba.max_index)
|
||||
ba.bits[leg_index] &= ~(1 << uint(bit_index))
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||
*/
|
||||
create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
|
||||
context.allocator = allocator
|
||||
size_in_bits := max_index - min_index
|
||||
|
||||
if size_in_bits < 1 { return {}, false }
|
||||
|
||||
legs := size_in_bits >> INDEX_SHIFT
|
||||
|
||||
res = new(Bit_Array)
|
||||
res.bias = min_index
|
||||
res.max_index = max_index
|
||||
res.free_pointer = true
|
||||
return res, resize_if_needed(res, legs)
|
||||
}
|
||||
|
||||
/*
|
||||
Sets all bits to `false`.
|
||||
*/
|
||||
clear :: proc(ba: ^Bit_Array) {
|
||||
if ba == nil { return }
|
||||
mem.zero_slice(ba.bits[:])
|
||||
}
|
||||
|
||||
/*
|
||||
Releases the memory used by the Bit Array.
|
||||
*/
|
||||
destroy :: proc(ba: ^Bit_Array) {
|
||||
if ba == nil { return }
|
||||
delete(ba.bits)
|
||||
if ba.free_pointer { // Only free if this Bit_Array was created using `create`, not when on the stack.
|
||||
free(ba)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Resizes the Bit Array. For internal use.
|
||||
If you want to reserve the memory for a given-sized Bit Array up front, you can use `create`.
|
||||
*/
|
||||
@(private="file")
|
||||
resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocator) -> (ok: bool) {
|
||||
if ba == nil { return false }
|
||||
|
||||
context.allocator = allocator
|
||||
|
||||
if legs + 1 > len(ba.bits) {
|
||||
resize(&ba.bits, legs + 1)
|
||||
}
|
||||
return len(ba.bits) > legs
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package dynamic_bit_array
|
||||
|
||||
/*
|
||||
The Bit Array can be used in several ways:
|
||||
|
||||
-- By default you don't need to instantiate a Bit Array:
|
||||
|
||||
package test
|
||||
|
||||
import "core:fmt"
|
||||
import "core:container/bit_array"
|
||||
|
||||
main :: proc() {
|
||||
using bit_array
|
||||
|
||||
bits: Bit_Array
|
||||
|
||||
// returns `true`
|
||||
fmt.println(set(&bits, 42))
|
||||
|
||||
// returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
|
||||
was_set, was_retrieved := get(&bits, -1)
|
||||
fmt.println(was_set, was_retrieved)
|
||||
destroy(&bits)
|
||||
}
|
||||
|
||||
-- A Bit Array can optionally allow for negative indices, if the mininum value was given during creation:
|
||||
|
||||
package test
|
||||
|
||||
import "core:fmt"
|
||||
import "core:container/bit_array"
|
||||
|
||||
main :: proc() {
|
||||
Foo :: enum int {
|
||||
Negative_Test = -42,
|
||||
Bar = 420,
|
||||
Leaves = 69105,
|
||||
}
|
||||
|
||||
using bit_array
|
||||
|
||||
bits := create(int(max(Foo)), int(min(Foo)))
|
||||
defer destroy(bits)
|
||||
|
||||
fmt.printf("Set(Bar): %v\n", set(bits, Foo.Bar))
|
||||
fmt.printf("Get(Bar): %v, %v\n", get(bits, Foo.Bar))
|
||||
fmt.printf("Set(Negative_Test): %v\n", set(bits, Foo.Negative_Test))
|
||||
fmt.printf("Get(Leaves): %v, %v\n", get(bits, Foo.Leaves))
|
||||
fmt.printf("Get(Negative_Test): %v, %v\n", get(bits, Foo.Negative_Test))
|
||||
fmt.printf("Freed.\n")
|
||||
}
|
||||
*/
|
||||
@@ -1,80 +0,0 @@
|
||||
package container
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Bloom_Hash_Proc :: #type proc(data: []byte) -> u32
|
||||
|
||||
Bloom_Hash :: struct {
|
||||
hash_proc: Bloom_Hash_Proc,
|
||||
next: ^Bloom_Hash,
|
||||
}
|
||||
|
||||
Bloom_Filter :: struct {
|
||||
allocator: mem.Allocator,
|
||||
hash: ^Bloom_Hash,
|
||||
bits: []byte,
|
||||
}
|
||||
|
||||
bloom_filter_init :: proc(b: ^Bloom_Filter, size: int, allocator := context.allocator) {
|
||||
b.allocator = allocator
|
||||
b.bits = make([]byte, size, allocator)
|
||||
}
|
||||
|
||||
bloom_filter_destroy :: proc(b: ^Bloom_Filter) {
|
||||
context.allocator = b.allocator
|
||||
delete(b.bits)
|
||||
for b.hash != nil {
|
||||
hash := b.hash
|
||||
b.hash = b.hash.next
|
||||
free(hash)
|
||||
}
|
||||
}
|
||||
|
||||
bloom_filter_add_hash_proc :: proc(b: ^Bloom_Filter, hash_proc: Bloom_Hash_Proc) {
|
||||
context.allocator = b.allocator
|
||||
h := new(Bloom_Hash)
|
||||
h.hash_proc = hash_proc
|
||||
|
||||
head := &b.hash
|
||||
for head^ != nil {
|
||||
head = &(head^.next)
|
||||
}
|
||||
head^ = h
|
||||
}
|
||||
|
||||
bloom_filter_add :: proc(b: ^Bloom_Filter, item: []byte) {
|
||||
#no_bounds_check for h := b.hash; h != nil; h = h.next {
|
||||
hash := h.hash_proc(item)
|
||||
hash %= u32(len(b.bits) * 8)
|
||||
b.bits[hash >> 3] |= 1 << (hash & 3)
|
||||
}
|
||||
}
|
||||
|
||||
bloom_filter_add_string :: proc(b: ^Bloom_Filter, item: string) {
|
||||
bloom_filter_add(b, transmute([]byte)item)
|
||||
}
|
||||
|
||||
bloom_filter_add_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) {
|
||||
item := mem.slice_ptr((^byte)(data), size)
|
||||
bloom_filter_add(b, item)
|
||||
}
|
||||
|
||||
bloom_filter_test :: proc(b: ^Bloom_Filter, item: []byte) -> bool {
|
||||
#no_bounds_check for h := b.hash; h != nil; h = h.next {
|
||||
hash := h.hash_proc(item)
|
||||
hash %= u32(len(b.bits) * 8)
|
||||
if (b.bits[hash >> 3] & (1 << (hash & 3)) == 0) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
bloom_filter_test_string :: proc(b: ^Bloom_Filter, item: string) -> bool {
|
||||
return bloom_filter_test(b, transmute([]byte)item)
|
||||
}
|
||||
|
||||
bloom_filter_test_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) -> bool {
|
||||
item := mem.slice_ptr((^byte)(data), size)
|
||||
return bloom_filter_test(b, item)
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package container_intrusive_list
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
// An intrusive doubly-linked list
|
||||
//
|
||||
// As this is an intrusive container, a `Node` must be embedded in your own
|
||||
// structure which is conventionally called a "link". The use of `push_front`
|
||||
// and `push_back` take the address of this node. Retrieving the data
|
||||
// associated with the node requires finding the relative offset of the node
|
||||
// of the parent structure. The parent type and field name are given to
|
||||
// `iterator_*` procedures, or to the built-in `container_of` procedure.
|
||||
//
|
||||
// This data structure is two-pointers in size:
|
||||
// 8 bytes on 32-bit platforms and 16 bytes on 64-bit platforms
|
||||
List :: struct {
|
||||
head: ^Node,
|
||||
tail: ^Node,
|
||||
}
|
||||
|
||||
|
||||
Node :: struct {
|
||||
next, prev: ^Node,
|
||||
}
|
||||
|
||||
push_front :: proc(list: ^List, node: ^Node) {
|
||||
if list.head != nil {
|
||||
list.head.prev = node
|
||||
node.prev, node.next = nil, list.head
|
||||
list.head = node
|
||||
} else {
|
||||
list.head, list.tail = node, node
|
||||
node.prev, node.next = nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
push_back :: proc(list: ^List, node: ^Node) {
|
||||
if list.tail != nil {
|
||||
list.tail.next = node
|
||||
node.prev, node.next = list.tail, nil
|
||||
list.tail = node
|
||||
} else {
|
||||
list.head, list.tail = node, node
|
||||
node.prev, node.next = nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
remove :: proc(list: ^List, node: ^Node) {
|
||||
if node != nil {
|
||||
if node.next != nil {
|
||||
node.next.prev = node.prev
|
||||
}
|
||||
if node.prev != nil {
|
||||
node.prev.next = node.next
|
||||
}
|
||||
if list.head == node {
|
||||
list.head = node.next
|
||||
}
|
||||
if list.tail == node {
|
||||
list.tail = node.prev
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove_by_proc :: proc(list: ^List, to_erase: proc(^Node) -> bool) {
|
||||
for node := list.head; node != nil; {
|
||||
next := node.next
|
||||
if to_erase(node) {
|
||||
if node.next != nil {
|
||||
node.next.prev = node.prev
|
||||
}
|
||||
if node.prev != nil {
|
||||
node.prev.next = node.next
|
||||
}
|
||||
if list.head == node {
|
||||
list.head = node.next
|
||||
}
|
||||
if list.tail == node {
|
||||
list.tail = node.prev
|
||||
}
|
||||
}
|
||||
node = next
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
is_empty :: proc(list: ^List) -> bool {
|
||||
return list.head == nil
|
||||
}
|
||||
|
||||
pop_front :: proc(list: ^List) -> ^Node {
|
||||
link := list.head
|
||||
if link == nil {
|
||||
return nil
|
||||
}
|
||||
if link.next != nil {
|
||||
link.next.prev = link.prev
|
||||
}
|
||||
if link.prev != nil {
|
||||
link.prev.next = link.next
|
||||
}
|
||||
if link == list.head {
|
||||
list.head = link.next
|
||||
}
|
||||
if link == list.tail {
|
||||
list.tail = link.prev
|
||||
}
|
||||
return link
|
||||
|
||||
}
|
||||
pop_back :: proc(list: ^List) -> ^Node {
|
||||
link := list.tail
|
||||
if link == nil {
|
||||
return nil
|
||||
}
|
||||
if link.next != nil {
|
||||
link.next.prev = link.prev
|
||||
}
|
||||
if link.prev != nil {
|
||||
link.prev.next = link.next
|
||||
}
|
||||
if link == list.head {
|
||||
list.head = link.next
|
||||
}
|
||||
if link == list.tail {
|
||||
list.tail = link.prev
|
||||
}
|
||||
return link
|
||||
}
|
||||
|
||||
|
||||
Iterator :: struct($T: typeid) {
|
||||
curr: ^Node,
|
||||
offset: uintptr,
|
||||
}
|
||||
|
||||
iterator_head :: proc(list: List, $T: typeid, $field_name: string) -> Iterator(T)
|
||||
where intrinsics.type_has_field(T, field_name),
|
||||
intrinsics.type_field_type(T, field_name) == Node {
|
||||
return {list.head, offset_of_by_string(T, field_name)}
|
||||
}
|
||||
|
||||
iterator_tail :: proc(list: List, $T: typeid, $field_name: string) -> Iterator(T)
|
||||
where intrinsics.type_has_field(T, field_name),
|
||||
intrinsics.type_field_type(T, field_name) == Node {
|
||||
return {list.tail, offset_of_by_string(T, field_name)}
|
||||
}
|
||||
|
||||
iterator_from_node :: proc(node: ^Node, $T: typeid, $field_name: string) -> Iterator(T)
|
||||
where intrinsics.type_has_field(T, field_name),
|
||||
intrinsics.type_field_type(T, field_name) == Node {
|
||||
return {node, offset_of_by_string(T, field_name)}
|
||||
}
|
||||
|
||||
iterate_next :: proc(it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
||||
node := it.curr
|
||||
if node == nil {
|
||||
return nil, false
|
||||
}
|
||||
it.curr = node.next
|
||||
|
||||
return (^T)(uintptr(node) - it.offset), true
|
||||
}
|
||||
|
||||
iterate_prev :: proc(it: ^Iterator($T)) -> (ptr: ^T, ok: bool) {
|
||||
node := it.curr
|
||||
if node == nil {
|
||||
return nil, false
|
||||
}
|
||||
it.curr = node.prev
|
||||
|
||||
return (^T)(uintptr(node) - it.offset), true
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package container_lru
|
||||
|
||||
import "core:runtime"
|
||||
import "core:intrinsics"
|
||||
_ :: runtime
|
||||
_ :: intrinsics
|
||||
|
||||
Node :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
prev, next: ^Node(Key, Value),
|
||||
key: Key,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
// Cache is an LRU cache. It automatically removes entries as new entries are
|
||||
// added if the capacity is reached. Entries are removed based on how recently
|
||||
// they were used where the oldest entries are removed first.
|
||||
Cache :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
head: ^Node(Key, Value),
|
||||
tail: ^Node(Key, Value),
|
||||
|
||||
entries: map[Key]^Node(Key, Value),
|
||||
|
||||
count: int,
|
||||
capacity: int,
|
||||
|
||||
node_allocator: runtime.Allocator,
|
||||
|
||||
on_remove: proc(key: Key, value: Value, user_data: rawptr),
|
||||
on_remove_user_data: rawptr,
|
||||
}
|
||||
|
||||
// init initializes a Cache
|
||||
init :: proc(c: ^$C/Cache($Key, $Value), capacity: int, entries_allocator := context.allocator, node_allocator := context.allocator) {
|
||||
c.entries.allocator = entries_allocator
|
||||
c.node_allocator = node_allocator
|
||||
c.capacity = capacity
|
||||
}
|
||||
|
||||
// destroy deinitializes a Cachem
|
||||
destroy :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
|
||||
clear(c, call_on_remove)
|
||||
delete(c.entries)
|
||||
}
|
||||
|
||||
// clear the contents of a Cache
|
||||
clear :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
|
||||
for _, node in c.entries {
|
||||
if call_on_remove {
|
||||
_call_on_remove(c, node)
|
||||
}
|
||||
free(node, c.node_allocator)
|
||||
}
|
||||
runtime.clear(&c.entries)
|
||||
c.head = nil
|
||||
c.tail = nil
|
||||
c.count = 0
|
||||
}
|
||||
|
||||
// set the given key value pair. This operation updates the recent usage of the item.
|
||||
set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Allocator_Error {
|
||||
if e, ok := c.entries[key]; ok {
|
||||
e.value = value
|
||||
_pop_node(c, e)
|
||||
_push_front_node(c, e)
|
||||
return nil
|
||||
}
|
||||
|
||||
e : ^Node(Key, Value) = nil
|
||||
assert(c.count <= c.capacity)
|
||||
if c.count == c.capacity {
|
||||
e = c.tail
|
||||
_remove_node(c, e)
|
||||
}
|
||||
else {
|
||||
c.count += 1
|
||||
e = new(Node(Key, Value), c.node_allocator) or_return
|
||||
}
|
||||
|
||||
e.key = key
|
||||
e.value = value
|
||||
_push_front_node(c, e)
|
||||
c.entries[key] = e
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// get a value from the cache from a given key. This operation updates the usage of the item.
|
||||
get :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
|
||||
e: ^Node(Key, Value)
|
||||
e, ok = c.entries[key]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
_pop_node(c, e)
|
||||
_push_front_node(c, e)
|
||||
return e.value, true
|
||||
}
|
||||
|
||||
// get_ptr gets the pointer to a value the cache from a given key. This operation updates the usage of the item.
|
||||
get_ptr :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: ^Value, ok: bool) #optional_ok {
|
||||
e: ^Node(Key, Value)
|
||||
e, ok = c.entries[key]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
_pop_node(c, e)
|
||||
_push_front_node(c, e)
|
||||
return &e.value, true
|
||||
}
|
||||
|
||||
// peek gets the value from the cache from a given key without updating the recent usage.
|
||||
peek :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
|
||||
e: ^Node(Key, Value)
|
||||
e, ok = c.entries[key]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
return e.value, true
|
||||
}
|
||||
|
||||
// exists checks for the existence of a value from a given key without updating the recent usage.
|
||||
exists :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
|
||||
return key in c.entries
|
||||
}
|
||||
|
||||
// remove removes an item from the cache.
|
||||
remove :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
|
||||
e, ok := c.entries[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
_remove_node(c, e)
|
||||
free(node, c.node_allocator)
|
||||
c.count -= 1
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_remove_node :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
|
||||
if c.head == node {
|
||||
c.head = node.next
|
||||
}
|
||||
if c.tail == node {
|
||||
c.tail = node.prev
|
||||
}
|
||||
if node.prev != nil {
|
||||
node.prev.next = node.next
|
||||
}
|
||||
if node.next != nil {
|
||||
node.next.prev = node.prev
|
||||
}
|
||||
node.prev = nil
|
||||
node.next = nil
|
||||
|
||||
delete_key(&c.entries, node.key)
|
||||
|
||||
_call_on_remove(c, node)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_call_on_remove :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
|
||||
if c.on_remove != nil {
|
||||
c.on_remove(node.key, node.value, c.on_remove_user_data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_push_front_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
|
||||
if c.head != nil {
|
||||
e.next = c.head
|
||||
e.next.prev = e
|
||||
}
|
||||
c.head = e
|
||||
if c.tail == nil {
|
||||
c.tail = e
|
||||
}
|
||||
e.prev = nil
|
||||
}
|
||||
|
||||
@(private)
|
||||
_pop_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
if c.head == e {
|
||||
c.head = e.next
|
||||
}
|
||||
if c.tail == e {
|
||||
c.tail = e.prev
|
||||
}
|
||||
if e.prev != nil {
|
||||
e.prev.next = e.next
|
||||
}
|
||||
|
||||
if e.next != nil {
|
||||
e.next.prev = e.prev
|
||||
}
|
||||
e.prev = nil
|
||||
e.next = nil
|
||||
}
|
||||
@@ -1,377 +0,0 @@
|
||||
package container
|
||||
|
||||
import "core:intrinsics"
|
||||
_ :: intrinsics
|
||||
|
||||
|
||||
Map :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
hash: Array(int),
|
||||
entries: Array(Map_Entry(Key, Value)),
|
||||
}
|
||||
|
||||
Map_Entry :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
hash: uintptr,
|
||||
next: int,
|
||||
key: Key,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
map_init :: proc{
|
||||
map_init_none,
|
||||
map_init_cap,
|
||||
}
|
||||
map_delete
|
||||
|
||||
map_has
|
||||
map_get
|
||||
map_get_default
|
||||
map_get_ptr
|
||||
map_set
|
||||
map_remove
|
||||
map_reserve
|
||||
map_clear
|
||||
|
||||
// Multi Map
|
||||
|
||||
multi_map_find_first
|
||||
multi_map_find_next
|
||||
multi_map_count
|
||||
multi_map_get :: proc{
|
||||
multi_map_get_array,
|
||||
multi_map_get_slice,
|
||||
};
|
||||
multi_map_get_as_slice
|
||||
multi_map_insert
|
||||
multi_map_remove
|
||||
multi_map_remove_all
|
||||
|
||||
*/
|
||||
|
||||
map_init :: proc{map_init_none, map_init_cap}
|
||||
|
||||
map_init_none :: proc(m: ^$M/Map($Key, $Value), allocator := context.allocator) {
|
||||
m.hash.allocator = allocator
|
||||
m.entries.allocator = allocator
|
||||
}
|
||||
|
||||
map_init_cap :: proc(m: ^$M/Map($Key, $Value), cap: int, allocator := context.allocator) {
|
||||
m.hash.allocator = allocator
|
||||
m.entries.allocator = allocator
|
||||
map_reserve(m, cap)
|
||||
}
|
||||
|
||||
map_delete :: proc(m: $M/Map($Key, $Value)) {
|
||||
array_delete(m.hash)
|
||||
array_delete(m.entries)
|
||||
}
|
||||
|
||||
|
||||
map_has :: proc(m: $M/Map($Key, $Value), key: Key) -> bool {
|
||||
return _map_find_or_fail(m, key) >= 0
|
||||
}
|
||||
|
||||
map_get :: proc(m: $M/Map($Key, $Value), key: Key) -> (res: Value, ok: bool) #optional_ok {
|
||||
i := _map_find_or_fail(m, key)
|
||||
if i < 0 {
|
||||
return {}, false
|
||||
}
|
||||
return array_get(m.entries, i).value, true
|
||||
}
|
||||
|
||||
map_get_default :: proc(m: $M/Map($Key, $Value), key: Key, default: Value) -> (res: Value, ok: bool) #optional_ok {
|
||||
i := _map_find_or_fail(m, key)
|
||||
if i < 0 {
|
||||
return default, false
|
||||
}
|
||||
return array_get(m.entries, i).value, true
|
||||
}
|
||||
|
||||
map_get_ptr :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Value {
|
||||
i := _map_find_or_fail(m, key)
|
||||
if i < 0 {
|
||||
return nil
|
||||
}
|
||||
return array_get_ptr(m.entries, i).value
|
||||
}
|
||||
|
||||
map_set :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
|
||||
if array_len(m.hash) == 0 {
|
||||
_map_grow(m)
|
||||
}
|
||||
|
||||
i := _map_find_or_make(m, key)
|
||||
array_get_ptr(m.entries, i).value = value
|
||||
if _map_full(m^) {
|
||||
_map_grow(m)
|
||||
}
|
||||
}
|
||||
|
||||
map_remove :: proc(m: ^$M/Map($Key, $Value), key: Key) {
|
||||
fr := _map_find_key(m^, key)
|
||||
if fr.entry_index >= 0 {
|
||||
_map_erase(m, fr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
map_reserve :: proc(m: ^$M/Map($Key, $Value), new_size: int) {
|
||||
nm: M
|
||||
map_init(&nm, m.hash.allocator)
|
||||
array_resize(&nm.hash, new_size)
|
||||
array_reserve(&nm.entries, array_len(m.entries))
|
||||
|
||||
for i in 0..<new_size {
|
||||
array_set(&nm.hash, i, -1)
|
||||
}
|
||||
for i in 0..<array_len(m.entries) {
|
||||
e := array_get(m.entries, i)
|
||||
multi_map_insert(&nm, e.key, e.value)
|
||||
}
|
||||
|
||||
map_delete(m^)
|
||||
m^ = nm
|
||||
}
|
||||
|
||||
map_clear :: proc(m: ^$M/Map($Key, $Value)) {
|
||||
array_clear(&m.hash)
|
||||
array_clear(&m.entries)
|
||||
}
|
||||
|
||||
|
||||
|
||||
multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Map_Entry(Key, Value) {
|
||||
i := _map_find_or_fail(m, key)
|
||||
if i < 0 {
|
||||
return nil
|
||||
}
|
||||
return array_get_ptr(m.entries, i)
|
||||
}
|
||||
|
||||
multi_map_find_next :: proc(m: $M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> ^Map_Entry(Key, Value) {
|
||||
i := e.next
|
||||
for i >= 0 {
|
||||
it := array_get_ptr(m.entries, i)
|
||||
if it.hash == e.hash && it.key == e.key {
|
||||
return it
|
||||
}
|
||||
i = it.next
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
multi_map_count :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
|
||||
n := 0
|
||||
e := multi_map_find_first(m, key)
|
||||
for e != nil {
|
||||
n += 1
|
||||
e = multi_map_find_next(m, e)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
multi_map_get :: proc{multi_map_get_array, multi_map_get_slice}
|
||||
|
||||
multi_map_get_array :: proc(m: $M/Map($Key, $Value), key: Key, items: ^Array(Value)) {
|
||||
if items == nil {
|
||||
return
|
||||
}
|
||||
e := multi_map_find_first(m, key)
|
||||
for e != nil {
|
||||
array_append(items, e.value)
|
||||
e = multi_map_find_next(m, e)
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_get_slice :: proc(m: $M/Map($Key, $Value), key: Key, items: []Value) {
|
||||
e := multi_map_find_first(m, key)
|
||||
i := 0
|
||||
for e != nil && i < len(items) {
|
||||
items[i] = e.value
|
||||
i += 1
|
||||
e = multi_map_find_next(m, e)
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_get_as_slice :: proc(m: $M/Map($Key, $Value), key: Key) -> []Value {
|
||||
items: Array(Value)
|
||||
array_init(&items, 0)
|
||||
|
||||
e := multi_map_find_first(m, key)
|
||||
for e != nil {
|
||||
array_append(&items, e.value)
|
||||
e = multi_map_find_next(m, e)
|
||||
}
|
||||
|
||||
return array_slice(items)
|
||||
}
|
||||
|
||||
|
||||
multi_map_insert :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
|
||||
if array_len(m.hash) == 0 {
|
||||
_map_grow(m)
|
||||
}
|
||||
|
||||
i := _map_make(m, key)
|
||||
array_get_ptr(m.entries, i).value = value
|
||||
if _map_full(m^) {
|
||||
_map_grow(m)
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_remove :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) {
|
||||
fr := _map_find_entry(m, e)
|
||||
if fr.entry_index >= 0 {
|
||||
_map_erase(m, fr)
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_remove_all :: proc(m: ^$M/Map($Key, $Value), key: Key) {
|
||||
for map_exist(m^, key) {
|
||||
map_remove(m, key)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Internal
|
||||
|
||||
|
||||
Map_Find_Result :: struct {
|
||||
hash_index: int,
|
||||
entry_prev: int,
|
||||
entry_index: int,
|
||||
}
|
||||
|
||||
_map_add_entry :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int where intrinsics.type_is_valid_map_key(Key) {
|
||||
hasher := intrinsics.type_hasher_proc(Key)
|
||||
|
||||
e: Map_Entry(Key, Value)
|
||||
e.key = key
|
||||
e.hash = hasher(&e.key, 0)
|
||||
e.next = -1
|
||||
idx := array_len(m.entries)
|
||||
array_push(&m.entries, e)
|
||||
return idx
|
||||
}
|
||||
|
||||
_map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) {
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next)
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next
|
||||
}
|
||||
|
||||
if fr.entry_index == array_len(m.entries)-1 {
|
||||
array_pop_back(&m.entries)
|
||||
return
|
||||
}
|
||||
|
||||
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1))
|
||||
last := _map_find_key(m^, array_get(m.entries, fr.entry_index).key)
|
||||
|
||||
if last.entry_prev < 0 {
|
||||
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index
|
||||
} else {
|
||||
array_set(&m.hash, last.hash_index, fr.entry_index)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_map_find_key :: proc(m: $M/Map($Key, $Value), key: Key) -> Map_Find_Result where intrinsics.type_is_valid_map_key(Key) {
|
||||
fr: Map_Find_Result
|
||||
fr.hash_index = -1
|
||||
fr.entry_prev = -1
|
||||
fr.entry_index = -1
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr
|
||||
}
|
||||
|
||||
hasher := intrinsics.type_hasher_proc(Key)
|
||||
|
||||
key := key
|
||||
hash := hasher(&key, 0)
|
||||
|
||||
fr.hash_index = int(hash % uintptr(array_len(m.hash)))
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index)
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index)
|
||||
if it.hash == hash && it.key == key {
|
||||
return fr
|
||||
}
|
||||
fr.entry_prev = fr.entry_index
|
||||
fr.entry_index = it.next
|
||||
}
|
||||
return fr
|
||||
}
|
||||
|
||||
_map_find_entry :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> Map_Find_Result {
|
||||
fr: Map_Find_Result
|
||||
fr.hash_index = -1
|
||||
fr.entry_prev = -1
|
||||
fr.entry_index = -1
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr
|
||||
}
|
||||
|
||||
fr.hash_index = int(e.hash % uintptr(array_len(m.hash)))
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index)
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index)
|
||||
if it == e {
|
||||
return fr
|
||||
}
|
||||
fr.entry_prev = fr.entry_index
|
||||
fr.entry_index = it.next
|
||||
}
|
||||
return fr
|
||||
}
|
||||
|
||||
_map_find_or_fail :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
|
||||
return _map_find_key(m, key).entry_index
|
||||
}
|
||||
_map_find_or_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
|
||||
fr := _map_find_key(m^, key)
|
||||
if fr.entry_index >= 0 {
|
||||
return fr.entry_index
|
||||
}
|
||||
|
||||
i := _map_add_entry(m, key)
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i)
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
|
||||
_map_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
|
||||
fr := _map_find_key(m^, key)
|
||||
i := _map_add_entry(m, key)
|
||||
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i)
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i
|
||||
}
|
||||
|
||||
array_get_ptr(m.entries, i).next = fr.entry_index
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
|
||||
_map_full :: proc(m: $M/Map($Key, $Value)) -> bool {
|
||||
// TODO(bill): Determine good max load factor
|
||||
return array_len(m.entries) >= (array_len(m.hash) / 4)*3
|
||||
}
|
||||
|
||||
_map_grow :: proc(m: ^$M/Map($Key, $Value)) {
|
||||
new_size := array_len(m.entries) * 4 + 7 // TODO(bill): Determine good grow rate
|
||||
map_reserve(m, new_size)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
package container
|
||||
|
||||
Priority_Queue :: struct($T: typeid) {
|
||||
data: Array(T),
|
||||
len: int,
|
||||
priority: proc(item: T) -> int,
|
||||
}
|
||||
|
||||
priority_queue_init_none :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, allocator := context.allocator) {
|
||||
queue_init_len(q, f, 0, allocator)
|
||||
}
|
||||
priority_queue_init_len :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, allocator := context.allocator) {
|
||||
queue_init_len_cap(q, f, 0, 16, allocator)
|
||||
}
|
||||
priority_queue_init_len_cap :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, cap: int, allocator := context.allocator) {
|
||||
array_init(&q.data, len, cap, allocator)
|
||||
q.len = len
|
||||
q.priority = f
|
||||
}
|
||||
|
||||
priority_queue_init :: proc{priority_queue_init_none, priority_queue_init_len, priority_queue_init_len_cap}
|
||||
|
||||
|
||||
priority_queue_delete :: proc(q: $Q/Priority_Queue($T)) {
|
||||
array_delete(q.data)
|
||||
}
|
||||
|
||||
priority_queue_clear :: proc(q: ^$Q/Priority_Queue($T)) {
|
||||
q.len = 0
|
||||
}
|
||||
|
||||
priority_queue_len :: proc(q: $Q/Priority_Queue($T)) -> int {
|
||||
return q.len
|
||||
}
|
||||
|
||||
priority_queue_cap :: proc(q: $Q/Priority_Queue($T)) -> int {
|
||||
return array_cap(q.data)
|
||||
}
|
||||
|
||||
priority_queue_space :: proc(q: $Q/Priority_Queue($T)) -> int {
|
||||
return array_len(q.data) - q.len
|
||||
}
|
||||
|
||||
priority_queue_reserve :: proc(q: ^$Q/Priority_Queue($T), capacity: int) {
|
||||
if capacity > q.len {
|
||||
array_resize(&q.data, new_capacity)
|
||||
}
|
||||
}
|
||||
|
||||
priority_queue_resize :: proc(q: ^$Q/Priority_Queue($T), length: int) {
|
||||
if length > q.len {
|
||||
array_resize(&q.data, new_capacity)
|
||||
}
|
||||
q.len = length
|
||||
}
|
||||
|
||||
_priority_queue_grow :: proc(q: ^$Q/Priority_Queue($T), min_capacity: int = 0) {
|
||||
new_capacity := max(array_len(q.data)*2 + 8, min_capacity)
|
||||
array_resize(&q.data, new_capacity)
|
||||
}
|
||||
|
||||
|
||||
priority_queue_push :: proc(q: ^$Q/Priority_Queue($T), item: T) {
|
||||
if array_len(q.data) - q.len == 0 {
|
||||
_priority_queue_grow(q)
|
||||
}
|
||||
|
||||
s := array_slice(q.data)
|
||||
s[q.len] = item
|
||||
|
||||
i := q.len
|
||||
for i > 0 {
|
||||
p := (i - 1) / 2
|
||||
if q.priority(s[p]) <= q.priority(item) {
|
||||
break
|
||||
}
|
||||
s[i] = s[p]
|
||||
i = p
|
||||
}
|
||||
|
||||
q.len += 1
|
||||
if q.len > 0 {
|
||||
s[i] = item
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
priority_queue_pop :: proc(q: ^$Q/Priority_Queue($T)) -> T {
|
||||
assert(q.len > 0)
|
||||
|
||||
s := array_slice(q.data)
|
||||
min := s[0]
|
||||
root := s[q.len-1]
|
||||
q.len -= 1
|
||||
|
||||
i := 0
|
||||
for i * 2 + 1 < q.len {
|
||||
a := i * 2 + 1
|
||||
b := i * 2 + 2
|
||||
c := b < q.len && q.priority(s[b]) < q.priority(s[a]) ? b : a
|
||||
|
||||
if q.priority(s[c]) >= q.priority(root) {
|
||||
break
|
||||
}
|
||||
s[i] = s[c]
|
||||
i = c
|
||||
}
|
||||
|
||||
if q.len > 0 {
|
||||
s[i] = root
|
||||
}
|
||||
return min
|
||||
}
|
||||
|
||||
priority_queue_peek :: proc(q: ^$Q/Priority_Queue($T)) -> T {
|
||||
assert(q.len > 0)
|
||||
|
||||
s := array_slice(q.data)
|
||||
return s[0]
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package container_priority_queue
|
||||
|
||||
import "core:builtin"
|
||||
|
||||
Priority_Queue :: struct($T: typeid) {
|
||||
queue: [dynamic]T,
|
||||
|
||||
less: proc(a, b: T) -> bool,
|
||||
swap: proc(q: []T, i, j: int),
|
||||
}
|
||||
|
||||
DEFAULT_CAPACITY :: 16
|
||||
|
||||
default_swap_proc :: proc($T: typeid) -> proc(q: []T, i, j: int) {
|
||||
return proc(q: []T, i, j: int) {
|
||||
q[i], q[j] = q[j], q[i]
|
||||
}
|
||||
}
|
||||
|
||||
init :: proc(pq: ^$Q/Priority_Queue($T), less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int), capacity := DEFAULT_CAPACITY, allocator := context.allocator) {
|
||||
if pq.queue.allocator.procedure == nil {
|
||||
pq.queue.allocator = allocator
|
||||
}
|
||||
reserve(pq, capacity)
|
||||
pq.less = less
|
||||
pq.swap = swap
|
||||
}
|
||||
|
||||
init_from_dynamic_array :: proc(pq: ^$Q/Priority_Queue($T), queue: [dynamic]T, less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int)) {
|
||||
pq.queue = queue
|
||||
pq.less = less
|
||||
pq.swap = swap
|
||||
n := builtin.len(pq.queue)
|
||||
for i := n/2 - 1; i >= 0; i -= 1 {
|
||||
_shift_down(pq, i, n)
|
||||
}
|
||||
}
|
||||
|
||||
destroy :: proc(pq: ^$Q/Priority_Queue($T)) {
|
||||
clear(pq)
|
||||
delete(pq.queue)
|
||||
}
|
||||
|
||||
reserve :: proc(pq: ^$Q/Priority_Queue($T), capacity: int) {
|
||||
builtin.reserve(&pq.queue, capacity)
|
||||
}
|
||||
clear :: proc(pq: ^$Q/Priority_Queue($T)) {
|
||||
builtin.clear(&pq.queue)
|
||||
}
|
||||
len :: proc(pq: $Q/Priority_Queue($T)) -> int {
|
||||
return builtin.len(pq.queue)
|
||||
}
|
||||
cap :: proc(pq: $Q/Priority_Queue($T)) -> int {
|
||||
return builtin.cap(pq.queue)
|
||||
}
|
||||
|
||||
_shift_down :: proc(pq: ^$Q/Priority_Queue($T), i0, n: int) -> bool {
|
||||
// O(n log n)
|
||||
if 0 > i0 || i0 > n {
|
||||
return false
|
||||
}
|
||||
|
||||
i := i0
|
||||
queue := pq.queue[:]
|
||||
|
||||
for {
|
||||
j1 := 2*i + 1
|
||||
if j1 < 0 || j1 >= n {
|
||||
break
|
||||
}
|
||||
j := j1
|
||||
if j2 := j1+1; j2 < n && pq.less(queue[j2], queue[j1]) {
|
||||
j = j2
|
||||
}
|
||||
if !pq.less(queue[j], queue[i]) {
|
||||
break
|
||||
}
|
||||
|
||||
pq.swap(queue, i, j)
|
||||
i = j
|
||||
}
|
||||
return i > i0
|
||||
}
|
||||
|
||||
_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
|
||||
j := j
|
||||
queue := pq.queue[:]
|
||||
n := builtin.len(queue)
|
||||
for 0 <= j {
|
||||
i := (j-1)/2
|
||||
if i == j || !pq.less(queue[j], queue[i]) {
|
||||
break
|
||||
}
|
||||
pq.swap(queue, i, j)
|
||||
j = i
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): When an element at index 'i' has changed its value, this will fix the
|
||||
// the heap ordering. This is using a basic "heapsort" with shift up and a shift down parts.
|
||||
fix :: proc(pq: ^$Q/Priority_Queue($T), i: int) {
|
||||
if !_shift_down(pq, i, builtin.len(pq.queue)) {
|
||||
_shift_up(pq, i)
|
||||
}
|
||||
}
|
||||
|
||||
push :: proc(pq: ^$Q/Priority_Queue($T), value: T) {
|
||||
append(&pq.queue, value)
|
||||
_shift_up(pq, builtin.len(pq.queue)-1)
|
||||
}
|
||||
|
||||
pop :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value: T) {
|
||||
assert(condition=builtin.len(pq.queue)>0, loc=loc)
|
||||
|
||||
n := builtin.len(pq.queue)-1
|
||||
pq.swap(pq.queue[:], 0, n)
|
||||
_shift_down(pq, 0, n)
|
||||
return builtin.pop(&pq.queue)
|
||||
}
|
||||
|
||||
pop_safe :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value: T, ok: bool) {
|
||||
if builtin.len(pq.queue) > 0 {
|
||||
n := builtin.len(pq.queue)-1
|
||||
pq.swap(pq.queue[:], 0, n)
|
||||
_shift_down(pq, 0, n)
|
||||
return builtin.pop_safe(&pq.queue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
remove :: proc(pq: ^$Q/Priority_Queue($T), i: int) -> (value: T, ok: bool) {
|
||||
n := builtin.len(pq.queue)
|
||||
if 0 <= i && i < n {
|
||||
if n != i {
|
||||
pq.swap(pq.queue[:], i, n)
|
||||
_shift_down(pq, i, n)
|
||||
_shift_up(pq, i)
|
||||
}
|
||||
value, ok = builtin.pop_safe(&pq.queue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
package container
|
||||
|
||||
Queue :: struct($T: typeid) {
|
||||
data: Array(T),
|
||||
len: int,
|
||||
offset: int,
|
||||
}
|
||||
|
||||
/*
|
||||
queue_init :: proc{
|
||||
queue_init_none,
|
||||
queue_init_len,
|
||||
queue_init_len_cap,
|
||||
}
|
||||
queue_delete
|
||||
queue_clear
|
||||
queue_len
|
||||
queue_cap
|
||||
queue_space
|
||||
queue_get
|
||||
queue_set
|
||||
queue_reserve
|
||||
queue_resize
|
||||
queue_push :: proc{
|
||||
queue_push_back,
|
||||
queue_push_elems,
|
||||
};
|
||||
queue_push_front
|
||||
queue_pop_front
|
||||
queue_pop_back
|
||||
queue_consume
|
||||
*/
|
||||
|
||||
queue_init_none :: proc(q: ^$Q/Queue($T), allocator := context.allocator) {
|
||||
queue_init_len(q, 0, allocator)
|
||||
}
|
||||
queue_init_len :: proc(q: ^$Q/Queue($T), len: int, allocator := context.allocator) {
|
||||
queue_init_len_cap(q, 0, 16, allocator)
|
||||
}
|
||||
queue_init_len_cap :: proc(q: ^$Q/Queue($T), len: int, cap: int, allocator := context.allocator) {
|
||||
array_init(&q.data, len, cap, allocator)
|
||||
q.len = len
|
||||
q.offset = 0
|
||||
}
|
||||
|
||||
queue_init :: proc{queue_init_none, queue_init_len, queue_init_len_cap}
|
||||
|
||||
queue_delete :: proc(q: $Q/Queue($T)) {
|
||||
array_delete(q.data)
|
||||
}
|
||||
|
||||
queue_clear :: proc(q: ^$Q/Queue($T)) {
|
||||
q.len = 0
|
||||
}
|
||||
|
||||
queue_len :: proc(q: $Q/Queue($T)) -> int {
|
||||
return q.len
|
||||
}
|
||||
|
||||
queue_cap :: proc(q: $Q/Queue($T)) -> int {
|
||||
return array_cap(q.data)
|
||||
}
|
||||
|
||||
queue_space :: proc(q: $Q/Queue($T)) -> int {
|
||||
return array_len(q.data) - q.len
|
||||
}
|
||||
|
||||
queue_get :: proc(q: $Q/Queue($T), index: int) -> T {
|
||||
i := (index + q.offset) % array_len(q.data)
|
||||
data := array_slice(q.data)
|
||||
return data[i]
|
||||
}
|
||||
|
||||
queue_set :: proc(q: ^$Q/Queue($T), index: int, item: T) {
|
||||
i := (index + q.offset) % array_len(q.data)
|
||||
data := array_slice(q.data)
|
||||
data[i] = item
|
||||
}
|
||||
|
||||
|
||||
queue_reserve :: proc(q: ^$Q/Queue($T), capacity: int) {
|
||||
if capacity > q.len {
|
||||
_queue_increase_capacity(q, capacity)
|
||||
}
|
||||
}
|
||||
|
||||
queue_resize :: proc(q: ^$Q/Queue($T), length: int) {
|
||||
if length > q.len {
|
||||
_queue_increase_capacity(q, length)
|
||||
}
|
||||
q.len = length
|
||||
}
|
||||
|
||||
queue_push_back :: proc(q: ^$Q/Queue($T), item: T) {
|
||||
if queue_space(q^) == 0 {
|
||||
_queue_grow(q)
|
||||
}
|
||||
|
||||
queue_set(q, q.len, item)
|
||||
q.len += 1
|
||||
}
|
||||
|
||||
queue_push_front :: proc(q: ^$Q/Queue($T), item: T) {
|
||||
if queue_space(q^) == 0 {
|
||||
_queue_grow(q)
|
||||
}
|
||||
|
||||
q.offset = (q.offset - 1 + array_len(q.data)) % array_len(q.data)
|
||||
q.len += 1
|
||||
queue_set(q, 0, item)
|
||||
}
|
||||
|
||||
queue_pop_front :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
assert(q.len > 0)
|
||||
item := queue_get(q^, 0)
|
||||
q.offset = (q.offset + 1) % array_len(q.data)
|
||||
q.len -= 1
|
||||
if q.len == 0 {
|
||||
q.offset = 0
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
queue_pop_back :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
assert(q.len > 0)
|
||||
item := queue_get(q^, q.len-1)
|
||||
q.len -= 1
|
||||
return item
|
||||
}
|
||||
|
||||
queue_consume :: proc(q: ^$Q/Queue($T), count: int) {
|
||||
q.offset = (q.offset + count) & array_len(q.data)
|
||||
q.len -= count
|
||||
}
|
||||
|
||||
|
||||
queue_push_elems :: proc(q: ^$Q/Queue($T), items: ..T) {
|
||||
if queue_space(q^) < len(items) {
|
||||
_queue_grow(q, q.len + len(items))
|
||||
}
|
||||
size := array_len(q.data)
|
||||
insert := (q.offset + q.len) % size
|
||||
|
||||
to_insert := len(items)
|
||||
if insert + to_insert > size {
|
||||
to_insert = size - insert
|
||||
}
|
||||
|
||||
the_items := items[:]
|
||||
|
||||
data := array_slice(q.data)
|
||||
|
||||
q.len += copy(data[insert:][:to_insert], the_items)
|
||||
the_items = the_items[to_insert:]
|
||||
q.len += copy(data[:], the_items)
|
||||
}
|
||||
|
||||
queue_push :: proc{queue_push_back, queue_push_elems}
|
||||
|
||||
|
||||
|
||||
_queue_increase_capacity :: proc(q: ^$Q/Queue($T), new_capacity: int) {
|
||||
end := array_len(q.data)
|
||||
array_resize(&q.data, new_capacity)
|
||||
if q.offset + q.len > end {
|
||||
end_items := q.len + end
|
||||
data := array_slice(q.data)
|
||||
copy(data[new_capacity-end_items:][:end_items], data[q.offset:][:end_items])
|
||||
q.offset += new_capacity - end
|
||||
}
|
||||
}
|
||||
_queue_grow :: proc(q: ^$Q/Queue($T), min_capacity: int = 0) {
|
||||
new_capacity := max(array_len(q.data)*2 + 8, min_capacity)
|
||||
_queue_increase_capacity(q, new_capacity)
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package container_queue
|
||||
|
||||
import "core:builtin"
|
||||
import "core:runtime"
|
||||
_ :: runtime
|
||||
|
||||
// Dynamically resizable double-ended queue/ring-buffer
|
||||
Queue :: struct($T: typeid) {
|
||||
data: [dynamic]T,
|
||||
len: uint,
|
||||
offset: uint,
|
||||
}
|
||||
|
||||
DEFAULT_CAPACITY :: 16
|
||||
|
||||
// Procedure to initialize a queue
|
||||
init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := context.allocator) -> bool {
|
||||
if q.data.allocator.procedure == nil {
|
||||
q.data.allocator = allocator
|
||||
}
|
||||
clear(q)
|
||||
return reserve(q, capacity)
|
||||
}
|
||||
|
||||
// Procedure to initialize a queue from a fixed backing slice
|
||||
init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
|
||||
clear(q)
|
||||
q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
|
||||
data = raw_data(backing),
|
||||
len = builtin.len(backing),
|
||||
cap = builtin.len(backing),
|
||||
allocator = {procedure=runtime.nil_allocator_proc, data=nil},
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Procedure to destroy a queue
|
||||
destroy :: proc(q: ^$Q/Queue($T)) {
|
||||
delete(q.data)
|
||||
}
|
||||
|
||||
// The length of the queue
|
||||
len :: proc(q: $Q/Queue($T)) -> int {
|
||||
return int(q.len)
|
||||
}
|
||||
|
||||
// The current capacity of the queue
|
||||
cap :: proc(q: $Q/Queue($T)) -> int {
|
||||
return builtin.len(q.data)
|
||||
}
|
||||
|
||||
// Remaining space in the queue (cap-len)
|
||||
space :: proc(q: $Q/Queue($T)) -> int {
|
||||
return builtin.len(q.data) - int(q.len)
|
||||
}
|
||||
|
||||
// Reserve enough space for at least the specified capacity
|
||||
reserve :: proc(q: ^$Q/Queue($T), capacity: int) -> bool {
|
||||
if uint(capacity) > q.len {
|
||||
return _grow(q, uint(capacity))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
|
||||
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||
|
||||
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
||||
return q.data[idx]
|
||||
}
|
||||
|
||||
front :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
return q.data[q.offset]
|
||||
}
|
||||
|
||||
back :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
return q.data[idx]
|
||||
}
|
||||
|
||||
set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||
|
||||
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
||||
q.data[idx] = val
|
||||
}
|
||||
get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||
|
||||
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
// Push an element to the back of the queue
|
||||
push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
|
||||
if space(q^) == 0 {
|
||||
_grow(q) or_return
|
||||
}
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
q.data[idx] = elem
|
||||
q.len += 1
|
||||
return true
|
||||
}
|
||||
|
||||
// Push an element to the front of the queue
|
||||
push_front :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
|
||||
if space(q^) == 0 {
|
||||
_grow(q) or_return
|
||||
}
|
||||
q.offset = uint(q.offset - 1 + builtin.len(q.data)) % builtin.len(q.data)
|
||||
q.len += 1
|
||||
q.data[q.offset] = elem
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
// Pop an element from the back of the queue
|
||||
pop_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
|
||||
assert(condition=q.len > 0, loc=loc)
|
||||
q.len -= 1
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
elem = q.data[idx]
|
||||
return
|
||||
}
|
||||
// Safely pop an element from the back of the queue
|
||||
pop_back_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
|
||||
if q.len > 0 {
|
||||
q.len -= 1
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
elem = q.data[idx]
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pop an element from the front of the queue
|
||||
pop_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
|
||||
assert(condition=q.len > 0, loc=loc)
|
||||
elem = q.data[q.offset]
|
||||
q.offset = (q.offset+1)%builtin.len(q.data)
|
||||
q.len -= 1
|
||||
return
|
||||
}
|
||||
// Safely pop an element from the front of the queue
|
||||
pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
|
||||
if q.len > 0 {
|
||||
elem = q.data[q.offset]
|
||||
q.offset = (q.offset+1)%builtin.len(q.data)
|
||||
q.len -= 1
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Push multiple elements to the front of the queue
|
||||
push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> bool {
|
||||
n := uint(builtin.len(elems))
|
||||
if space(q^) < int(n) {
|
||||
_grow(q, q.len + n) or_return
|
||||
}
|
||||
|
||||
sz := uint(builtin.len(q.data))
|
||||
insert_from := (q.offset + q.len) % sz
|
||||
insert_to := n
|
||||
if insert_from + insert_to > sz {
|
||||
insert_to = sz - insert_from
|
||||
}
|
||||
copy(q.data[insert_from:], elems[:insert_to])
|
||||
copy(q.data[:insert_from], elems[insert_to:])
|
||||
q.len += n
|
||||
return true
|
||||
}
|
||||
|
||||
// Consume `n` elements from the front of the queue
|
||||
consume_front :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
|
||||
assert(condition=int(q.len) >= n, loc=loc)
|
||||
if n > 0 {
|
||||
nu := uint(n)
|
||||
q.offset = (q.offset + nu) % builtin.len(q.data)
|
||||
q.len -= nu
|
||||
}
|
||||
}
|
||||
|
||||
// Consume `n` elements from the back of the queue
|
||||
consume_back :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
|
||||
assert(condition=int(q.len) >= n, loc=loc)
|
||||
if n > 0 {
|
||||
q.len -= uint(n)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
append_elem :: push_back
|
||||
append_elems :: push_back_elems
|
||||
push :: proc{push_back, push_back_elems}
|
||||
append :: proc{push_back, push_back_elems}
|
||||
|
||||
|
||||
// Clear the contents of the queue
|
||||
clear :: proc(q: ^$Q/Queue($T)) {
|
||||
q.len = 0
|
||||
q.offset = 0
|
||||
}
|
||||
|
||||
|
||||
// Internal growinh procedure
|
||||
_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> bool {
|
||||
new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
|
||||
n := uint(builtin.len(q.data))
|
||||
builtin.resize(&q.data, int(new_capacity)) or_return
|
||||
if q.offset + q.len > n {
|
||||
diff := n - q.offset
|
||||
copy(q.data[new_capacity-diff:], q.data[q.offset:][:diff])
|
||||
q.offset += new_capacity - n
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package container
|
||||
|
||||
|
||||
Ring :: struct($T: typeid) {
|
||||
next, prev: ^Ring(T),
|
||||
value: T,
|
||||
}
|
||||
|
||||
ring_init :: proc(r: ^$R/Ring) -> ^R {
|
||||
r.prev, r.next = r, r
|
||||
return r
|
||||
}
|
||||
|
||||
ring_next :: proc(r: ^$R/Ring) -> ^R {
|
||||
if r.next == nil {
|
||||
return ring_init(r)
|
||||
}
|
||||
return r.next
|
||||
}
|
||||
ring_prev :: proc(r: ^$R/Ring) -> ^R {
|
||||
if r.prev == nil {
|
||||
return ring_init(r)
|
||||
}
|
||||
return r.prev
|
||||
}
|
||||
|
||||
|
||||
ring_move :: proc(r: ^$R/Ring, n: int) -> ^R {
|
||||
r := r
|
||||
if r.next == nil {
|
||||
return ring_init(r)
|
||||
}
|
||||
|
||||
switch {
|
||||
case n < 0:
|
||||
for _ in n..<0 {
|
||||
r = r.prev
|
||||
}
|
||||
case n > 0:
|
||||
for _ in 0..<n {
|
||||
r = r.next
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
ring_link :: proc(r, s: ^$R/Ring) -> ^R {
|
||||
n := ring_next(r)
|
||||
if s != nil {
|
||||
p := ring_prev(s)
|
||||
r.next = s
|
||||
s.prev = r
|
||||
n.prev = p
|
||||
p.next = n
|
||||
}
|
||||
return n
|
||||
}
|
||||
ring_unlink :: proc(r: ^$R/Ring, n: int) -> ^R {
|
||||
if n <= 0 {
|
||||
return nil
|
||||
}
|
||||
return ring_link(r, ring_move(r, n+1))
|
||||
}
|
||||
ring_len :: proc(r: ^$R/Ring) -> int {
|
||||
n := 0
|
||||
if r != nil {
|
||||
n = 1
|
||||
for p := ring_next(r); p != r; p = p.next {
|
||||
n += 1
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -1,240 +0,0 @@
|
||||
package container
|
||||
|
||||
Set :: struct {
|
||||
hash: Array(int),
|
||||
entries: Array(Set_Entry),
|
||||
}
|
||||
|
||||
Set_Entry :: struct {
|
||||
key: u64,
|
||||
next: int,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
set_init :: proc{
|
||||
set_init_none,
|
||||
set_init_cap,
|
||||
}
|
||||
set_delete
|
||||
|
||||
set_in
|
||||
set_not_in
|
||||
set_add
|
||||
set_remove
|
||||
set_reserve
|
||||
set_clear
|
||||
*/
|
||||
|
||||
set_init :: proc{set_init_none, set_init_cap}
|
||||
|
||||
set_init_none :: proc(m: ^Set, allocator := context.allocator) {
|
||||
m.hash.allocator = allocator
|
||||
m.entries.allocator = allocator
|
||||
}
|
||||
|
||||
set_init_cap :: proc(m: ^Set, cap: int, allocator := context.allocator) {
|
||||
m.hash.allocator = allocator
|
||||
m.entries.allocator = allocator
|
||||
set_reserve(m, cap)
|
||||
}
|
||||
|
||||
set_delete :: proc(m: Set) {
|
||||
array_delete(m.hash)
|
||||
array_delete(m.entries)
|
||||
}
|
||||
|
||||
|
||||
set_in :: proc(m: Set, key: u64) -> bool {
|
||||
return _set_find_or_fail(m, key) >= 0
|
||||
}
|
||||
set_not_in :: proc(m: Set, key: u64) -> bool {
|
||||
return _set_find_or_fail(m, key) < 0
|
||||
}
|
||||
|
||||
set_add :: proc(m: ^Set, key: u64) {
|
||||
if array_len(m.hash) == 0 {
|
||||
_set_grow(m)
|
||||
}
|
||||
|
||||
_ = _set_find_or_make(m, key)
|
||||
if _set_full(m^) {
|
||||
_set_grow(m)
|
||||
}
|
||||
}
|
||||
|
||||
set_remove :: proc(m: ^Set, key: u64) {
|
||||
fr := _set_find_key(m^, key)
|
||||
if fr.entry_index >= 0 {
|
||||
_set_erase(m, fr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set_reserve :: proc(m: ^Set, new_size: int) {
|
||||
nm: Set
|
||||
set_init(&nm, m.hash.allocator)
|
||||
array_resize(&nm.hash, new_size)
|
||||
array_reserve(&nm.entries, array_len(m.entries))
|
||||
|
||||
for i in 0..<new_size {
|
||||
array_set(&nm.hash, i, -1)
|
||||
}
|
||||
for i in 0..<array_len(m.entries) {
|
||||
e := array_get(m.entries, i)
|
||||
set_add(&nm, e.key)
|
||||
}
|
||||
|
||||
set_delete(m^)
|
||||
m^ = nm
|
||||
}
|
||||
|
||||
set_clear :: proc(m: ^Set) {
|
||||
array_clear(&m.hash)
|
||||
array_clear(&m.entries)
|
||||
}
|
||||
|
||||
|
||||
set_equal :: proc(a, b: Set) -> bool {
|
||||
a_entries := array_slice(a.entries)
|
||||
b_entries := array_slice(b.entries)
|
||||
if len(a_entries) != len(b_entries) {
|
||||
return false
|
||||
}
|
||||
for e in a_entries {
|
||||
if set_not_in(b, e.key) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Internal
|
||||
|
||||
_set_add_entry :: proc(m: ^Set, key: u64) -> int {
|
||||
e: Set_Entry
|
||||
e.key = key
|
||||
e.next = -1
|
||||
idx := array_len(m.entries)
|
||||
array_push(&m.entries, e)
|
||||
return idx
|
||||
}
|
||||
|
||||
_set_erase :: proc(m: ^Set, fr: Map_Find_Result) {
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next)
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next
|
||||
}
|
||||
|
||||
if fr.entry_index == array_len(m.entries)-1 {
|
||||
array_pop_back(&m.entries)
|
||||
return
|
||||
}
|
||||
|
||||
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1))
|
||||
last := _set_find_key(m^, array_get(m.entries, fr.entry_index).key)
|
||||
|
||||
if last.entry_prev < 0 {
|
||||
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index
|
||||
} else {
|
||||
array_set(&m.hash, last.hash_index, fr.entry_index)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_set_find_key :: proc(m: Set, key: u64) -> Map_Find_Result {
|
||||
fr: Map_Find_Result
|
||||
fr.hash_index = -1
|
||||
fr.entry_prev = -1
|
||||
fr.entry_index = -1
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr
|
||||
}
|
||||
|
||||
fr.hash_index = int(key % u64(array_len(m.hash)))
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index)
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index)
|
||||
if it.key == key {
|
||||
return fr
|
||||
}
|
||||
fr.entry_prev = fr.entry_index
|
||||
fr.entry_index = it.next
|
||||
}
|
||||
return fr
|
||||
}
|
||||
|
||||
_set_find_entry :: proc(m: ^Set, e: ^Set_Entry) -> Map_Find_Result {
|
||||
fr: Map_Find_Result
|
||||
fr.hash_index = -1
|
||||
fr.entry_prev = -1
|
||||
fr.entry_index = -1
|
||||
|
||||
if array_len(m.hash) == 0 {
|
||||
return fr
|
||||
}
|
||||
|
||||
fr.hash_index = int(e.key % u64(array_len(m.hash)))
|
||||
fr.entry_index = array_get(m.hash, fr.hash_index)
|
||||
for fr.entry_index >= 0 {
|
||||
it := array_get_ptr(m.entries, fr.entry_index)
|
||||
if it == e {
|
||||
return fr
|
||||
}
|
||||
fr.entry_prev = fr.entry_index
|
||||
fr.entry_index = it.next
|
||||
}
|
||||
return fr
|
||||
}
|
||||
|
||||
_set_find_or_fail :: proc(m: Set, key: u64) -> int {
|
||||
return _set_find_key(m, key).entry_index
|
||||
}
|
||||
_set_find_or_make :: proc(m: ^Set, key: u64) -> int {
|
||||
fr := _set_find_key(m^, key)
|
||||
if fr.entry_index >= 0 {
|
||||
return fr.entry_index
|
||||
}
|
||||
|
||||
i := _set_add_entry(m, key)
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i)
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
|
||||
_set_make :: proc(m: ^Set, key: u64) -> int {
|
||||
fr := _set_find_key(m^, key)
|
||||
i := _set_add_entry(m, key)
|
||||
|
||||
if fr.entry_prev < 0 {
|
||||
array_set(&m.hash, fr.hash_index, i)
|
||||
} else {
|
||||
array_get_ptr(m.entries, fr.entry_prev).next = i
|
||||
}
|
||||
|
||||
array_get_ptr(m.entries, i).next = fr.entry_index
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
|
||||
_set_full :: proc(m: Set) -> bool {
|
||||
// TODO(bill): Determine good max load factor
|
||||
return array_len(m.entries) >= (array_len(m.hash) / 4)*3
|
||||
}
|
||||
|
||||
_set_grow :: proc(m: ^Set) {
|
||||
new_size := array_len(m.entries) * 4 + 7 // TODO(bill): Determine good grow rate
|
||||
set_reserve(m, new_size)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
package container
|
||||
|
||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||
data: [N]T,
|
||||
len: int,
|
||||
}
|
||||
|
||||
|
||||
small_array_len :: proc(a: $A/Small_Array) -> int {
|
||||
return a.len
|
||||
}
|
||||
|
||||
small_array_cap :: proc(a: $A/Small_Array) -> int {
|
||||
return len(a.data)
|
||||
}
|
||||
|
||||
small_array_space :: proc(a: $A/Small_Array) -> int {
|
||||
return len(a.data) - a.len
|
||||
}
|
||||
|
||||
small_array_slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
return a.data[:a.len]
|
||||
}
|
||||
|
||||
|
||||
small_array_get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T {
|
||||
return a.data[index]
|
||||
}
|
||||
small_array_get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
|
||||
return &a.data[index]
|
||||
}
|
||||
|
||||
small_array_set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
|
||||
a.data[index] = item
|
||||
}
|
||||
|
||||
small_array_resize :: proc(a: ^$A/Small_Array, length: int) {
|
||||
a.len = min(length, len(a.data))
|
||||
}
|
||||
|
||||
|
||||
small_array_push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < len(a.data) {
|
||||
a.len += 1
|
||||
a.data[a.len-1] = item
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
small_array_push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < len(a.data) {
|
||||
a.len += 1
|
||||
data := small_array_slice(a)
|
||||
copy(data[1:], data[:])
|
||||
data[0] = item
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
small_array_pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc)
|
||||
item := a.data[a.len-1]
|
||||
a.len -= 1
|
||||
return item
|
||||
}
|
||||
|
||||
small_array_pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=a.len > 0, loc=loc)
|
||||
item := a.data[0]
|
||||
s := small_array_slice(a)
|
||||
copy(s[:], s[1:])
|
||||
a.len -= 1
|
||||
return item
|
||||
}
|
||||
|
||||
|
||||
small_array_consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
assert(condition=a.len >= count, loc=loc)
|
||||
a.len -= count
|
||||
}
|
||||
|
||||
small_array_clear :: proc(a: ^$A/Small_Array($N, $T)) {
|
||||
small_array_resize(a, 0)
|
||||
}
|
||||
|
||||
small_array_push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
n := copy(a.data[a.len:], items[:])
|
||||
a.len += n
|
||||
}
|
||||
|
||||
small_array_push :: proc{small_array_push_back, small_array_push_back_elems}
|
||||
small_array_append :: proc{small_array_push_back, small_array_push_back_elems}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package container_small_array
|
||||
|
||||
import "core:builtin"
|
||||
|
||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||
data: [N]T,
|
||||
len: int,
|
||||
}
|
||||
|
||||
|
||||
len :: proc(a: $A/Small_Array) -> int {
|
||||
return a.len
|
||||
}
|
||||
|
||||
cap :: proc(a: $A/Small_Array) -> int {
|
||||
return builtin.len(a.data)
|
||||
}
|
||||
|
||||
space :: proc(a: $A/Small_Array) -> int {
|
||||
return builtin.len(a.data) - a.len
|
||||
}
|
||||
|
||||
slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
return a.data[:a.len]
|
||||
}
|
||||
|
||||
|
||||
get :: proc(a: $A/Small_Array($N, $T), index: int) -> T {
|
||||
return a.data[index]
|
||||
}
|
||||
get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
||||
return &a.data[index]
|
||||
}
|
||||
|
||||
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
||||
a.data[index] = item
|
||||
}
|
||||
|
||||
resize :: proc(a: ^$A/Small_Array, length: int) {
|
||||
a.len = min(length, builtin.len(a.data))
|
||||
}
|
||||
|
||||
|
||||
push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < cap(a^) {
|
||||
a.data[a.len] = item
|
||||
a.len += 1
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < cap(a^) {
|
||||
a.len += 1
|
||||
data := slice(a)
|
||||
copy(data[1:], data[:])
|
||||
data[0] = item
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||
item := a.data[a.len-1]
|
||||
a.len -= 1
|
||||
return item
|
||||
}
|
||||
|
||||
pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||
item := a.data[0]
|
||||
s := slice(a)
|
||||
copy(s[:], s[1:])
|
||||
a.len -= 1
|
||||
return item
|
||||
}
|
||||
|
||||
pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
if N > 0 && a.len > 0 {
|
||||
item = a.data[a.len-1]
|
||||
a.len -= 1
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
if N > 0 && a.len > 0 {
|
||||
item = a.data[0]
|
||||
s := slice(a)
|
||||
copy(s[:], s[1:])
|
||||
a.len -= 1
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
assert(condition=a.len >= count, loc=loc)
|
||||
a.len -= count
|
||||
}
|
||||
|
||||
clear :: proc(a: ^$A/Small_Array($N, $T)) {
|
||||
resize(a, 0)
|
||||
}
|
||||
|
||||
push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
n := copy(a.data[a.len:], items[:])
|
||||
a.len += n
|
||||
}
|
||||
|
||||
append_elem :: push_back
|
||||
append_elems :: push_back_elems
|
||||
push :: proc{push_back, push_back_elems}
|
||||
append :: proc{push_back, push_back_elems}
|
||||
@@ -0,0 +1,98 @@
|
||||
// The following is a generic O(V+E) topological sorter implementation.
|
||||
// This is the fastest known method for topological sorting and Odin's
|
||||
// map type is being used to accelerate lookups.
|
||||
package container_topological_sort
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:runtime"
|
||||
_ :: intrinsics
|
||||
_ :: runtime
|
||||
|
||||
|
||||
Relations :: struct($K: typeid) where intrinsics.type_is_valid_map_key(K) {
|
||||
dependents: map[K]bool,
|
||||
dependencies: int,
|
||||
}
|
||||
|
||||
Sorter :: struct(K: typeid) where intrinsics.type_is_valid_map_key(K) {
|
||||
relations: map[K]Relations(K),
|
||||
dependents_allocator: runtime.Allocator,
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
make_relations :: proc(sorter: ^$S/Sorter($K)) -> (r: Relations(K)) {
|
||||
r.dependents.allocator = sorter.dependents_allocator
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
init :: proc(sorter: ^$S/Sorter($K)) {
|
||||
sorter.relations = make(map[K]Relations(K))
|
||||
sorter.dependents_allocator = context.allocator
|
||||
}
|
||||
|
||||
destroy :: proc(sorter: ^$S/Sorter($K)) {
|
||||
for _, v in &sorter.relations {
|
||||
delete(v.dependents)
|
||||
}
|
||||
delete(sorter.relations)
|
||||
}
|
||||
|
||||
add_key :: proc(sorter: ^$S/Sorter($K), key: K) -> bool {
|
||||
if key in sorter.relations {
|
||||
return false
|
||||
}
|
||||
sorter.relations[key] = make_relations(sorter)
|
||||
return true
|
||||
}
|
||||
|
||||
add_dependency :: proc(sorter: ^$S/Sorter($K), key, dependency: K) -> bool {
|
||||
if key == dependency {
|
||||
return false
|
||||
}
|
||||
|
||||
find := &sorter.relations[dependency]
|
||||
if find == nil {
|
||||
find = map_insert(&sorter.relations, dependency, make_relations(sorter))
|
||||
}
|
||||
|
||||
if find.dependents[key] {
|
||||
return true
|
||||
}
|
||||
find.dependents[key] = true
|
||||
|
||||
find = &sorter.relations[key]
|
||||
if find == nil {
|
||||
find = map_insert(&sorter.relations, key, make_relations(sorter))
|
||||
}
|
||||
|
||||
find.dependencies += 1
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
sort :: proc(sorter: ^$S/Sorter($K)) -> (sorted, cycled: [dynamic]K) {
|
||||
relations := &sorter.relations
|
||||
|
||||
for k, v in relations {
|
||||
if v.dependencies == 0 {
|
||||
append(&sorted, k)
|
||||
}
|
||||
}
|
||||
|
||||
for root in &sorted do for k, _ in relations[root].dependents {
|
||||
relation := &relations[k]
|
||||
relation.dependencies -= 1
|
||||
if relation.dependencies == 0 {
|
||||
append(&sorted, k)
|
||||
}
|
||||
}
|
||||
|
||||
for k, v in relations {
|
||||
if v.dependencies != 0 {
|
||||
append(&cycled, k)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
+42
-44
@@ -2,48 +2,45 @@
|
||||
A crypto library for the Odin language
|
||||
|
||||
## Supported
|
||||
This library offers various algorithms available in either native Odin or via bindings to the [Botan](https://botan.randombit.net/) crypto library.
|
||||
This library offers various algorithms implemented in Odin.
|
||||
Please see the chart below for the options.
|
||||
**Note:** All crypto hash algorithms, offered by [Botan\'s FFI](https://botan.randombit.net/handbook/api_ref/hash.html), have been added.
|
||||
|
||||
## Hashing algorithms
|
||||
| Algorithm | Odin | Botan |
|
||||
|:-------------------------------------------------------------------------------------------------------------|:-----------------|:---------------------|
|
||||
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | ✔️ | |
|
||||
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ | ✔️ |
|
||||
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ | |
|
||||
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | ✔️ | ✔️ |
|
||||
| [Grøstl](http://www.groestl.info/Groestl.zip) | ✔️ | |
|
||||
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | ✔️ | |
|
||||
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | ✔️ | |
|
||||
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ |
|
||||
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | ✔️ | |
|
||||
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | ✔️ | ✔️ |
|
||||
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ | ✔️ |
|
||||
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | ✔️ | ✔️\* |
|
||||
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | ✔️ | ✔️ |
|
||||
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | ✔️ | ✔️ |
|
||||
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ |
|
||||
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ | ✔️ |
|
||||
| [Skein](https://www.schneier.com/academic/skein/) | | ✔️\*\* |
|
||||
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | ✔️ | ✔️ |
|
||||
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | ✔️ | ✔️ |
|
||||
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ | ✔️ |
|
||||
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ | |
|
||||
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | ✔️ | ✔️ |
|
||||
|
||||
\* Only `RIPEMD-160`
|
||||
\*\* Only `SKEIN-512`
|
||||
| Algorithm | |
|
||||
|:-------------------------------------------------------------------------------------------------------------|:-----------------|
|
||||
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | ✔️ |
|
||||
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
||||
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
||||
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | ✔️ |
|
||||
| [Grøstl](http://www.groestl.info/Groestl.zip) | ✔️ |
|
||||
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | ✔️ |
|
||||
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | ✔️ |
|
||||
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | ✔️ |
|
||||
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | ✔️ |
|
||||
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ |
|
||||
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | ✔️ |
|
||||
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | ✔️ |
|
||||
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | ✔️ |
|
||||
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | ✔️ |
|
||||
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | ✔️ |
|
||||
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ |
|
||||
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ |
|
||||
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | ✔️ |
|
||||
|
||||
#### 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 four procedures.
|
||||
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.
|
||||
\* On some algorithms there is another part to the name, since they might offer control about additional parameters.
|
||||
For instance, `HAVAL` offers different sizes as well as three different round amounts.
|
||||
Computing a 256-bit hash with 3 rounds is therefore achieved by calling `haval.hash_256_3(...)`.
|
||||
|
||||
@@ -51,13 +48,6 @@ Computing a 256-bit hash with 3 rounds is therefore achieved by calling `haval.h
|
||||
The above mentioned procedures internally call three procedures: `init`, `update` and `final`.
|
||||
You may also directly call them, if you wish.
|
||||
|
||||
#### Context system
|
||||
The library uses a context system internally to be able to switch between Odin / Botan implementations freely.
|
||||
When an Odin implementation is available, it is the default.
|
||||
You may change what is used during runtime by calling `foo.use_botan()` or `foo.use_odin()`.
|
||||
It is also possible to set this during compile time via `USE_BOTAN_LIB=true`.
|
||||
Internally a vtable is used to set the appropriate procedures when switching. This works for all the procedures mentioned in the APIs above.
|
||||
|
||||
#### Example
|
||||
```odin
|
||||
package crypto_example
|
||||
@@ -67,12 +57,20 @@ import "core:crypto/md4"
|
||||
|
||||
main :: proc() {
|
||||
input := "foo"
|
||||
// Compute the hash via Odin implementation
|
||||
|
||||
// Compute the hash, using the high level API
|
||||
computed_hash := md4.hash(input)
|
||||
// Switch to Botan
|
||||
md4.use_botan()
|
||||
// Compute the hash via Botan bindings
|
||||
computed_hash_botan := md4.hash(input)
|
||||
|
||||
// Variant that takes a destination buffer, instead of returning the computed hash
|
||||
hash := make([]byte, md4.DIGEST_SIZE) // @note: Destination buffer has to be at least as big as the digest size of the hash
|
||||
md4.hash(input, hash[:])
|
||||
|
||||
// Compute the hash, using the low level API
|
||||
ctx: md4.Md4_Context
|
||||
computed_hash_low: [16]byte
|
||||
md4.init(&ctx)
|
||||
md4.update(&ctx, transmute([]byte)input)
|
||||
md4.final(&ctx, computed_hash_low[:])
|
||||
}
|
||||
```
|
||||
For example uses of all available algorithms, please see the tests within `tests/core/crypto`.
|
||||
|
||||
@@ -6,7 +6,6 @@ package _blake2
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the BLAKE2 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/rfc7693> and <https://www.blake2.net/>
|
||||
*/
|
||||
@@ -76,7 +75,7 @@ BLAKE2B_IV := [8]u64 {
|
||||
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
init :: proc(ctx: ^$T) {
|
||||
when T == Blake2s_Context {
|
||||
block_size :: BLAKE2S_BLOCK_SIZE
|
||||
} else when T == Blake2b_Context {
|
||||
@@ -139,17 +138,17 @@ init_odin :: proc(ctx: ^$T) {
|
||||
}
|
||||
if len(ctx.cfg.key) > 0 {
|
||||
copy(ctx.padded_key[:], ctx.cfg.key)
|
||||
update_odin(ctx, ctx.padded_key[:])
|
||||
update(ctx, ctx.padded_key[:])
|
||||
ctx.is_keyed = true
|
||||
}
|
||||
copy(ctx.ih[:], ctx.h[:])
|
||||
copy(ctx.h[:], ctx.ih[:])
|
||||
if ctx.is_keyed {
|
||||
update_odin(ctx, ctx.padded_key[:])
|
||||
update(ctx, ctx.padded_key[:])
|
||||
}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^$T, p: []byte) {
|
||||
update :: proc "contextless" (ctx: ^$T, p: []byte) {
|
||||
p := p
|
||||
when T == Blake2s_Context {
|
||||
block_size :: BLAKE2S_BLOCK_SIZE
|
||||
@@ -161,7 +160,7 @@ update_odin :: proc(ctx: ^$T, p: []byte) {
|
||||
if len(p) > left {
|
||||
copy(ctx.x[ctx.nx:], p[:left])
|
||||
p = p[left:]
|
||||
blake2_blocks(ctx, ctx.x[:])
|
||||
blocks(ctx, ctx.x[:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
if len(p) > block_size {
|
||||
@@ -169,13 +168,22 @@ update_odin :: proc(ctx: ^$T, p: []byte) {
|
||||
if n == len(p) {
|
||||
n -= block_size
|
||||
}
|
||||
blake2_blocks(ctx, p[:n])
|
||||
blocks(ctx, p[:n])
|
||||
p = p[n:]
|
||||
}
|
||||
ctx.nx += copy(ctx.x[ctx.nx:], p)
|
||||
}
|
||||
|
||||
blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
|
||||
final :: proc "contextless" (ctx: ^$T, hash: []byte) {
|
||||
when T == Blake2s_Context {
|
||||
blake2s_final(ctx, hash)
|
||||
}
|
||||
when T == Blake2b_Context {
|
||||
blake2b_final(ctx, hash)
|
||||
}
|
||||
}
|
||||
|
||||
blake2s_final :: proc "contextless" (ctx: ^Blake2s_Context, hash: []byte) {
|
||||
if ctx.is_keyed {
|
||||
for i := 0; i < len(ctx.padded_key); i += 1 {
|
||||
ctx.padded_key[i] = 0
|
||||
@@ -193,7 +201,7 @@ blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
|
||||
ctx.f[1] = 0xffffffff
|
||||
}
|
||||
|
||||
blake2_blocks(ctx, ctx.x[:])
|
||||
blocks(ctx, ctx.x[:])
|
||||
|
||||
j := 0
|
||||
for s, _ in ctx.h[:(ctx.size - 1) / 4 + 1] {
|
||||
@@ -205,7 +213,7 @@ blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
|
||||
blake2b_final :: proc "contextless" (ctx: ^Blake2b_Context, hash: []byte) {
|
||||
if ctx.is_keyed {
|
||||
for i := 0; i < len(ctx.padded_key); i += 1 {
|
||||
ctx.padded_key[i] = 0
|
||||
@@ -223,7 +231,7 @@ blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
|
||||
ctx.f[1] = 0xffffffffffffffff
|
||||
}
|
||||
|
||||
blake2_blocks(ctx, ctx.x[:])
|
||||
blocks(ctx, ctx.x[:])
|
||||
|
||||
j := 0
|
||||
for s, _ in ctx.h[:(ctx.size - 1) / 8 + 1] {
|
||||
@@ -239,7 +247,7 @@ blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
blake2_blocks :: proc(ctx: ^$T, p: []byte) {
|
||||
blocks :: proc "contextless" (ctx: ^$T, p: []byte) {
|
||||
when T == Blake2s_Context {
|
||||
blake2s_blocks(ctx, p)
|
||||
}
|
||||
@@ -248,7 +256,7 @@ blake2_blocks :: proc(ctx: ^$T, p: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
blake2s_blocks :: #force_inline proc "contextless"(ctx: ^Blake2s_Context, p: []byte) {
|
||||
blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []byte) {
|
||||
h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
|
||||
p := p
|
||||
for len(p) >= BLAKE2S_BLOCK_SIZE {
|
||||
@@ -1404,7 +1412,7 @@ blake2s_blocks :: #force_inline proc "contextless"(ctx: ^Blake2s_Context, p: []b
|
||||
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
|
||||
}
|
||||
|
||||
blake2b_blocks :: #force_inline proc "contextless"(ctx: ^Blake2b_Context, p: []byte) {
|
||||
blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []byte) {
|
||||
h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
|
||||
p := p
|
||||
for len(p) >= BLAKE2B_BLOCK_SIZE {
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package _ctx
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog: Initial creation and testing of the bindings.
|
||||
|
||||
Implementation of the context, used internally by the crypto library.
|
||||
*/
|
||||
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
|
||||
Hash_Size :: enum {
|
||||
_16,
|
||||
_20,
|
||||
_24,
|
||||
_28,
|
||||
_32,
|
||||
_40,
|
||||
_48,
|
||||
_64,
|
||||
_128,
|
||||
}
|
||||
|
||||
Hash_Context :: struct {
|
||||
botan_hash_algo: cstring,
|
||||
external_ctx: any,
|
||||
internal_ctx: any,
|
||||
hash_size: Hash_Size,
|
||||
hash_size_val: int,
|
||||
is_using_odin: bool,
|
||||
using vtbl: ^Hash_Context_Vtable,
|
||||
}
|
||||
|
||||
Hash_Context_Vtable :: struct {
|
||||
hash_bytes_16 : proc (ctx: ^Hash_Context, input: []byte) -> [16]byte,
|
||||
hash_bytes_20 : proc (ctx: ^Hash_Context, input: []byte) -> [20]byte,
|
||||
hash_bytes_24 : proc (ctx: ^Hash_Context, input: []byte) -> [24]byte,
|
||||
hash_bytes_28 : proc (ctx: ^Hash_Context, input: []byte) -> [28]byte,
|
||||
hash_bytes_32 : proc (ctx: ^Hash_Context, input: []byte) -> [32]byte,
|
||||
hash_bytes_40 : proc (ctx: ^Hash_Context, input: []byte) -> [40]byte,
|
||||
hash_bytes_48 : proc (ctx: ^Hash_Context, input: []byte) -> [48]byte,
|
||||
hash_bytes_64 : proc (ctx: ^Hash_Context, input: []byte) -> [64]byte,
|
||||
hash_bytes_128 : proc (ctx: ^Hash_Context, input: []byte) -> [128]byte,
|
||||
hash_file_16 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool),
|
||||
hash_file_20 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool),
|
||||
hash_file_24 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool),
|
||||
hash_file_28 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool),
|
||||
hash_file_32 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool),
|
||||
hash_file_40 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([40]byte, bool),
|
||||
hash_file_48 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool),
|
||||
hash_file_64 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool),
|
||||
hash_file_128 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([128]byte, bool),
|
||||
hash_stream_16 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([16]byte, bool),
|
||||
hash_stream_20 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([20]byte, bool),
|
||||
hash_stream_24 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([24]byte, bool),
|
||||
hash_stream_28 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([28]byte, bool),
|
||||
hash_stream_32 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([32]byte, bool),
|
||||
hash_stream_40 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([40]byte, bool),
|
||||
hash_stream_48 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([48]byte, bool),
|
||||
hash_stream_64 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([64]byte, bool),
|
||||
hash_stream_128 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([128]byte, bool),
|
||||
hash_bytes_slice : proc (ctx: ^Hash_Context, input: []byte, out_size: int, allocator := context.allocator) -> []byte,
|
||||
hash_file_slice : proc (ctx: ^Hash_Context, hd: os.Handle, out_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool),
|
||||
hash_stream_slice : proc (ctx: ^Hash_Context, s: io.Stream, out_size: int, allocator := context.allocator) -> ([]byte, bool),
|
||||
init : proc (ctx: ^Hash_Context),
|
||||
update : proc (ctx: ^Hash_Context, data: []byte),
|
||||
final : proc (ctx: ^Hash_Context, hash: []byte),
|
||||
}
|
||||
|
||||
_init_vtable :: #force_inline proc() -> ^Hash_Context {
|
||||
ctx := new(Hash_Context)
|
||||
vtbl := new(Hash_Context_Vtable)
|
||||
ctx.vtbl = vtbl
|
||||
return ctx
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# fiat
|
||||
|
||||
This package contains low level arithmetic required to implement certain
|
||||
cryptographic primitives, ported from the [fiat-crypto project][1]
|
||||
along with some higher-level helpers.
|
||||
|
||||
## Notes
|
||||
|
||||
fiat-crypto gives the choice of 3 licenses for derived works. The 1-Clause
|
||||
BSD license is chosen as it is compatible with Odin's existing licensing.
|
||||
|
||||
The routines are intended to be timing-safe, as long as the underlying
|
||||
integer arithmetic is constant time. This is true on most systems commonly
|
||||
used today, with the notable exception of WASM.
|
||||
|
||||
While fiat-crypto provides both output targeting both 32-bit and 64-bit
|
||||
architectures, only the 64-bit versions were used, as 32-bit architectures
|
||||
are becoming increasingly uncommon and irrelevant.
|
||||
|
||||
With the current Odin syntax, the Go output is trivially ported in most
|
||||
cases and was used as the basis of the port.
|
||||
|
||||
In the future, it would be better to auto-generate Odin either directly
|
||||
by adding an appropriate code-gen backend written in Coq, or perhaps by
|
||||
parsing the JSON output.
|
||||
|
||||
As this is a port rather than autogenerated output, none of fiat-crypto's
|
||||
formal verification guarantees apply, unless it is possible to prove binary
|
||||
equivalence.
|
||||
|
||||
For the most part, alterations to the base fiat-crypto generated code was
|
||||
kept to a minimum, to aid auditability. This results in a somewhat
|
||||
ideosyncratic style, and in some cases minor performance penalties.
|
||||
|
||||
[1]: https://github.com/mit-plv/fiat-crypto
|
||||
@@ -0,0 +1,24 @@
|
||||
package fiat
|
||||
|
||||
// This package provides various helpers and types common to all of the
|
||||
// fiat-crypto derived backends.
|
||||
|
||||
// This code only works on a two's complement system.
|
||||
#assert((-1 & 3) == 3)
|
||||
|
||||
u1 :: distinct u8
|
||||
i1 :: distinct i8
|
||||
|
||||
cmovznz_u64 :: #force_inline proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) {
|
||||
x1 := (u64(arg1) * 0xffffffffffffffff)
|
||||
x2 := ((x1 & arg3) | ((~x1) & arg2))
|
||||
out1 = x2
|
||||
return
|
||||
}
|
||||
|
||||
cmovznz_u32 :: #force_inline proc "contextless" (arg1: u1, arg2, arg3: u32) -> (out1: u32) {
|
||||
x1 := (u32(arg1) * 0xffffffff)
|
||||
x2 := ((x1 & arg3) | ((~x1) & arg2))
|
||||
out1 = x2
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package field_curve25519
|
||||
|
||||
import "core:crypto"
|
||||
import "core:mem"
|
||||
|
||||
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 {
|
||||
return transmute(^Tight_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
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.
|
||||
tmp1: [32]byte = ---
|
||||
copy_slice(tmp1[:], arg1[:])
|
||||
tmp1[31] &= 127
|
||||
|
||||
_fe_from_bytes(out1, &tmp1)
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
}
|
||||
|
||||
fe_equal :: proc "contextless" (arg1, arg2: ^Tight_Field_Element) -> int {
|
||||
tmp2: [32]byte = ---
|
||||
|
||||
fe_to_bytes(&tmp2, arg2)
|
||||
ret := fe_equal_bytes(arg1, &tmp2)
|
||||
|
||||
mem.zero_explicit(&tmp2, size_of(tmp2))
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
fe_equal_bytes :: proc "contextless" (arg1: ^Tight_Field_Element, arg2: ^[32]byte) -> int {
|
||||
tmp1: [32]byte = ---
|
||||
|
||||
fe_to_bytes(&tmp1, arg1)
|
||||
|
||||
ret := crypto.compare_constant_time(tmp1[:], arg2[:])
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
fe_carry_pow2k :: proc (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)
|
||||
return
|
||||
}
|
||||
|
||||
fe_carry_square(out1, arg1)
|
||||
for _ in 1..<arg2 {
|
||||
fe_carry_square(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.
|
||||
|
||||
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(&tmp2, fe_relax_cast(&tmp1), 2)
|
||||
fe_carry_mul(&tmp2, arg1, 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))
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 5)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 10)
|
||||
fe_carry_mul(&tmp2, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
fe_carry_pow2k(&tmp3, fe_relax_cast(&tmp2), 20)
|
||||
fe_carry_mul(&tmp2, fe_relax_cast(&tmp3), fe_relax_cast(&tmp2))
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp2), 10)
|
||||
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 50)
|
||||
fe_carry_mul(&tmp2, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
|
||||
fe_carry_pow2k(&tmp3, fe_relax_cast(&tmp2), 100)
|
||||
fe_carry_mul(&tmp2, fe_relax_cast(&tmp3), fe_relax_cast(&tmp2))
|
||||
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)
|
||||
|
||||
// quartic = x^((p-1)/4)
|
||||
quartic := &tmp2
|
||||
fe_carry_square(quartic, fe_relax_cast(&tmp1))
|
||||
fe_carry_mul(quartic, fe_relax_cast(quartic), arg1)
|
||||
|
||||
// Serialize quartic once to save on repeated serialization/sanitization.
|
||||
quartic_buf: [32]byte = ---
|
||||
fe_to_bytes(&quartic_buf, quartic)
|
||||
check := &tmp3
|
||||
|
||||
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)
|
||||
|
||||
// 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)
|
||||
|
||||
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))
|
||||
|
||||
return p1 | m1
|
||||
}
|
||||
|
||||
fe_carry_inv :: proc (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_square(&tmp1, fe_relax_cast(&tmp1))
|
||||
fe_carry_mul(out1, fe_relax_cast(&tmp1), arg1)
|
||||
|
||||
mem.zero_explicit(&tmp1, size_of(tmp1))
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
// 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_curve25519
|
||||
|
||||
// The file provides arithmetic on the field Z/(2^255-19) using
|
||||
// unsaturated 64-bit integer arithmetic. 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.
|
||||
// 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
|
||||
// instead of 51-bit limbs will be faster, though the gains are
|
||||
// minimal unless adcx/adox/mulx are used.
|
||||
|
||||
import fiat "core:crypto/_fiat"
|
||||
import "core:math/bits"
|
||||
|
||||
Loose_Field_Element :: distinct [5]u64
|
||||
Tight_Field_Element :: distinct [5]u64
|
||||
|
||||
SQRT_M1 := Tight_Field_Element{
|
||||
1718705420411056,
|
||||
234908883556509,
|
||||
2233514472574048,
|
||||
2117202627021982,
|
||||
765476049583133,
|
||||
}
|
||||
|
||||
_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))
|
||||
out1 = x2
|
||||
out2 = x3
|
||||
return
|
||||
}
|
||||
|
||||
_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)
|
||||
out1 = x3
|
||||
out2 = (0x0 - fiat.u1(x2))
|
||||
return
|
||||
}
|
||||
|
||||
fe_carry_mul :: proc (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))
|
||||
x8, x7 := bits.mul_u64(arg1[4], (arg2[1] * 0x13))
|
||||
x10, x9 := bits.mul_u64(arg1[3], (arg2[4] * 0x13))
|
||||
x12, x11 := bits.mul_u64(arg1[3], (arg2[3] * 0x13))
|
||||
x14, x13 := bits.mul_u64(arg1[3], (arg2[2] * 0x13))
|
||||
x16, x15 := bits.mul_u64(arg1[2], (arg2[4] * 0x13))
|
||||
x18, x17 := bits.mul_u64(arg1[2], (arg2[3] * 0x13))
|
||||
x20, x19 := bits.mul_u64(arg1[1], (arg2[4] * 0x13))
|
||||
x22, x21 := bits.mul_u64(arg1[4], arg2[0])
|
||||
x24, x23 := bits.mul_u64(arg1[3], arg2[1])
|
||||
x26, x25 := bits.mul_u64(arg1[3], arg2[0])
|
||||
x28, x27 := bits.mul_u64(arg1[2], arg2[2])
|
||||
x30, x29 := bits.mul_u64(arg1[2], arg2[1])
|
||||
x32, x31 := bits.mul_u64(arg1[2], arg2[0])
|
||||
x34, x33 := bits.mul_u64(arg1[1], arg2[3])
|
||||
x36, x35 := bits.mul_u64(arg1[1], arg2[2])
|
||||
x38, x37 := bits.mul_u64(arg1[1], arg2[1])
|
||||
x40, x39 := bits.mul_u64(arg1[1], arg2[0])
|
||||
x42, x41 := bits.mul_u64(arg1[0], arg2[4])
|
||||
x44, x43 := bits.mul_u64(arg1[0], arg2[3])
|
||||
x46, x45 := bits.mul_u64(arg1[0], arg2[2])
|
||||
x48, x47 := bits.mul_u64(arg1[0], arg2[1])
|
||||
x50, x49 := bits.mul_u64(arg1[0], arg2[0])
|
||||
x51, x52 := bits.add_u64(x13, x7, u64(0x0))
|
||||
x53, _ := bits.add_u64(x14, x8, u64(fiat.u1(x52)))
|
||||
x55, x56 := bits.add_u64(x17, x51, u64(0x0))
|
||||
x57, _ := bits.add_u64(x18, x53, u64(fiat.u1(x56)))
|
||||
x59, x60 := bits.add_u64(x19, x55, u64(0x0))
|
||||
x61, _ := bits.add_u64(x20, x57, u64(fiat.u1(x60)))
|
||||
x63, x64 := bits.add_u64(x49, x59, u64(0x0))
|
||||
x65, _ := bits.add_u64(x50, x61, u64(fiat.u1(x64)))
|
||||
x67 := ((x63 >> 51) | ((x65 << 13) & 0xffffffffffffffff))
|
||||
x68 := (x63 & 0x7ffffffffffff)
|
||||
x69, x70 := bits.add_u64(x23, x21, u64(0x0))
|
||||
x71, _ := bits.add_u64(x24, x22, u64(fiat.u1(x70)))
|
||||
x73, x74 := bits.add_u64(x27, x69, u64(0x0))
|
||||
x75, _ := bits.add_u64(x28, x71, u64(fiat.u1(x74)))
|
||||
x77, x78 := bits.add_u64(x33, x73, u64(0x0))
|
||||
x79, _ := bits.add_u64(x34, x75, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x41, x77, u64(0x0))
|
||||
x83, _ := bits.add_u64(x42, x79, u64(fiat.u1(x82)))
|
||||
x85, x86 := bits.add_u64(x25, x1, u64(0x0))
|
||||
x87, _ := bits.add_u64(x26, x2, u64(fiat.u1(x86)))
|
||||
x89, x90 := bits.add_u64(x29, x85, u64(0x0))
|
||||
x91, _ := bits.add_u64(x30, x87, u64(fiat.u1(x90)))
|
||||
x93, x94 := bits.add_u64(x35, x89, u64(0x0))
|
||||
x95, _ := bits.add_u64(x36, x91, u64(fiat.u1(x94)))
|
||||
x97, x98 := bits.add_u64(x43, x93, u64(0x0))
|
||||
x99, _ := bits.add_u64(x44, x95, u64(fiat.u1(x98)))
|
||||
x101, x102 := bits.add_u64(x9, x3, u64(0x0))
|
||||
x103, _ := bits.add_u64(x10, x4, u64(fiat.u1(x102)))
|
||||
x105, x106 := bits.add_u64(x31, x101, u64(0x0))
|
||||
x107, _ := bits.add_u64(x32, x103, u64(fiat.u1(x106)))
|
||||
x109, x110 := bits.add_u64(x37, x105, u64(0x0))
|
||||
x111, _ := bits.add_u64(x38, x107, u64(fiat.u1(x110)))
|
||||
x113, x114 := bits.add_u64(x45, x109, u64(0x0))
|
||||
x115, _ := bits.add_u64(x46, x111, u64(fiat.u1(x114)))
|
||||
x117, x118 := bits.add_u64(x11, x5, u64(0x0))
|
||||
x119, _ := bits.add_u64(x12, x6, u64(fiat.u1(x118)))
|
||||
x121, x122 := bits.add_u64(x15, x117, u64(0x0))
|
||||
x123, _ := bits.add_u64(x16, x119, u64(fiat.u1(x122)))
|
||||
x125, x126 := bits.add_u64(x39, x121, u64(0x0))
|
||||
x127, _ := bits.add_u64(x40, x123, u64(fiat.u1(x126)))
|
||||
x129, x130 := bits.add_u64(x47, x125, u64(0x0))
|
||||
x131, _ := bits.add_u64(x48, x127, u64(fiat.u1(x130)))
|
||||
x133, x134 := bits.add_u64(x67, x129, u64(0x0))
|
||||
x135 := (u64(fiat.u1(x134)) + x131)
|
||||
x136 := ((x133 >> 51) | ((x135 << 13) & 0xffffffffffffffff))
|
||||
x137 := (x133 & 0x7ffffffffffff)
|
||||
x138, x139 := bits.add_u64(x136, x113, u64(0x0))
|
||||
x140 := (u64(fiat.u1(x139)) + x115)
|
||||
x141 := ((x138 >> 51) | ((x140 << 13) & 0xffffffffffffffff))
|
||||
x142 := (x138 & 0x7ffffffffffff)
|
||||
x143, x144 := bits.add_u64(x141, x97, u64(0x0))
|
||||
x145 := (u64(fiat.u1(x144)) + x99)
|
||||
x146 := ((x143 >> 51) | ((x145 << 13) & 0xffffffffffffffff))
|
||||
x147 := (x143 & 0x7ffffffffffff)
|
||||
x148, x149 := bits.add_u64(x146, x81, u64(0x0))
|
||||
x150 := (u64(fiat.u1(x149)) + x83)
|
||||
x151 := ((x148 >> 51) | ((x150 << 13) & 0xffffffffffffffff))
|
||||
x152 := (x148 & 0x7ffffffffffff)
|
||||
x153 := (x151 * 0x13)
|
||||
x154 := (x68 + x153)
|
||||
x155 := (x154 >> 51)
|
||||
x156 := (x154 & 0x7ffffffffffff)
|
||||
x157 := (x155 + x137)
|
||||
x158 := fiat.u1((x157 >> 51))
|
||||
x159 := (x157 & 0x7ffffffffffff)
|
||||
x160 := (u64(x158) + x142)
|
||||
out1[0] = x156
|
||||
out1[1] = x159
|
||||
out1[2] = x160
|
||||
out1[3] = x147
|
||||
out1[4] = x152
|
||||
}
|
||||
|
||||
fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
x1 := (arg1[4] * 0x13)
|
||||
x2 := (x1 * 0x2)
|
||||
x3 := (arg1[4] * 0x2)
|
||||
x4 := (arg1[3] * 0x13)
|
||||
x5 := (x4 * 0x2)
|
||||
x6 := (arg1[3] * 0x2)
|
||||
x7 := (arg1[2] * 0x2)
|
||||
x8 := (arg1[1] * 0x2)
|
||||
x10, x9 := bits.mul_u64(arg1[4], x1)
|
||||
x12, x11 := bits.mul_u64(arg1[3], x2)
|
||||
x14, x13 := bits.mul_u64(arg1[3], x4)
|
||||
x16, x15 := bits.mul_u64(arg1[2], x2)
|
||||
x18, x17 := bits.mul_u64(arg1[2], x5)
|
||||
x20, x19 := bits.mul_u64(arg1[2], arg1[2])
|
||||
x22, x21 := bits.mul_u64(arg1[1], x2)
|
||||
x24, x23 := bits.mul_u64(arg1[1], x6)
|
||||
x26, x25 := bits.mul_u64(arg1[1], x7)
|
||||
x28, x27 := bits.mul_u64(arg1[1], arg1[1])
|
||||
x30, x29 := bits.mul_u64(arg1[0], x3)
|
||||
x32, x31 := bits.mul_u64(arg1[0], x6)
|
||||
x34, x33 := bits.mul_u64(arg1[0], x7)
|
||||
x36, x35 := bits.mul_u64(arg1[0], x8)
|
||||
x38, x37 := bits.mul_u64(arg1[0], arg1[0])
|
||||
x39, x40 := bits.add_u64(x21, x17, u64(0x0))
|
||||
x41, _ := bits.add_u64(x22, x18, u64(fiat.u1(x40)))
|
||||
x43, x44 := bits.add_u64(x37, x39, u64(0x0))
|
||||
x45, _ := bits.add_u64(x38, x41, u64(fiat.u1(x44)))
|
||||
x47 := ((x43 >> 51) | ((x45 << 13) & 0xffffffffffffffff))
|
||||
x48 := (x43 & 0x7ffffffffffff)
|
||||
x49, x50 := bits.add_u64(x23, x19, u64(0x0))
|
||||
x51, _ := bits.add_u64(x24, x20, u64(fiat.u1(x50)))
|
||||
x53, x54 := bits.add_u64(x29, x49, u64(0x0))
|
||||
x55, _ := bits.add_u64(x30, x51, u64(fiat.u1(x54)))
|
||||
x57, x58 := bits.add_u64(x25, x9, u64(0x0))
|
||||
x59, _ := bits.add_u64(x26, x10, u64(fiat.u1(x58)))
|
||||
x61, x62 := bits.add_u64(x31, x57, u64(0x0))
|
||||
x63, _ := bits.add_u64(x32, x59, u64(fiat.u1(x62)))
|
||||
x65, x66 := bits.add_u64(x27, x11, u64(0x0))
|
||||
x67, _ := bits.add_u64(x28, x12, u64(fiat.u1(x66)))
|
||||
x69, x70 := bits.add_u64(x33, x65, u64(0x0))
|
||||
x71, _ := bits.add_u64(x34, x67, u64(fiat.u1(x70)))
|
||||
x73, x74 := bits.add_u64(x15, x13, u64(0x0))
|
||||
x75, _ := bits.add_u64(x16, x14, u64(fiat.u1(x74)))
|
||||
x77, x78 := bits.add_u64(x35, x73, u64(0x0))
|
||||
x79, _ := bits.add_u64(x36, x75, u64(fiat.u1(x78)))
|
||||
x81, x82 := bits.add_u64(x47, x77, u64(0x0))
|
||||
x83 := (u64(fiat.u1(x82)) + x79)
|
||||
x84 := ((x81 >> 51) | ((x83 << 13) & 0xffffffffffffffff))
|
||||
x85 := (x81 & 0x7ffffffffffff)
|
||||
x86, x87 := bits.add_u64(x84, x69, u64(0x0))
|
||||
x88 := (u64(fiat.u1(x87)) + x71)
|
||||
x89 := ((x86 >> 51) | ((x88 << 13) & 0xffffffffffffffff))
|
||||
x90 := (x86 & 0x7ffffffffffff)
|
||||
x91, x92 := bits.add_u64(x89, x61, u64(0x0))
|
||||
x93 := (u64(fiat.u1(x92)) + x63)
|
||||
x94 := ((x91 >> 51) | ((x93 << 13) & 0xffffffffffffffff))
|
||||
x95 := (x91 & 0x7ffffffffffff)
|
||||
x96, x97 := bits.add_u64(x94, x53, u64(0x0))
|
||||
x98 := (u64(fiat.u1(x97)) + x55)
|
||||
x99 := ((x96 >> 51) | ((x98 << 13) & 0xffffffffffffffff))
|
||||
x100 := (x96 & 0x7ffffffffffff)
|
||||
x101 := (x99 * 0x13)
|
||||
x102 := (x48 + x101)
|
||||
x103 := (x102 >> 51)
|
||||
x104 := (x102 & 0x7ffffffffffff)
|
||||
x105 := (x103 + x85)
|
||||
x106 := fiat.u1((x105 >> 51))
|
||||
x107 := (x105 & 0x7ffffffffffff)
|
||||
x108 := (u64(x106) + x90)
|
||||
out1[0] = x104
|
||||
out1[1] = x107
|
||||
out1[2] = x108
|
||||
out1[3] = x95
|
||||
out1[4] = x100
|
||||
}
|
||||
|
||||
fe_carry :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := ((x1 >> 51) + arg1[1])
|
||||
x3 := ((x2 >> 51) + arg1[2])
|
||||
x4 := ((x3 >> 51) + arg1[3])
|
||||
x5 := ((x4 >> 51) + arg1[4])
|
||||
x6 := ((x1 & 0x7ffffffffffff) + ((x5 >> 51) * 0x13))
|
||||
x7 := (u64(fiat.u1((x6 >> 51))) + (x2 & 0x7ffffffffffff))
|
||||
x8 := (x6 & 0x7ffffffffffff)
|
||||
x9 := (x7 & 0x7ffffffffffff)
|
||||
x10 := (u64(fiat.u1((x7 >> 51))) + (x3 & 0x7ffffffffffff))
|
||||
x11 := (x4 & 0x7ffffffffffff)
|
||||
x12 := (x5 & 0x7ffffffffffff)
|
||||
out1[0] = x8
|
||||
out1[1] = x9
|
||||
out1[2] = x10
|
||||
out1[3] = x11
|
||||
out1[4] = x12
|
||||
}
|
||||
|
||||
fe_add :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
|
||||
x1 := (arg1[0] + arg2[0])
|
||||
x2 := (arg1[1] + arg2[1])
|
||||
x3 := (arg1[2] + arg2[2])
|
||||
x4 := (arg1[3] + arg2[3])
|
||||
x5 := (arg1[4] + arg2[4])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_sub :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
|
||||
x1 := ((0xfffffffffffda + arg1[0]) - arg2[0])
|
||||
x2 := ((0xffffffffffffe + arg1[1]) - arg2[1])
|
||||
x3 := ((0xffffffffffffe + arg1[2]) - arg2[2])
|
||||
x4 := ((0xffffffffffffe + arg1[3]) - arg2[3])
|
||||
x5 := ((0xffffffffffffe + arg1[4]) - arg2[4])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
|
||||
x1 := (0xfffffffffffda - arg1[0])
|
||||
x2 := (0xffffffffffffe - arg1[1])
|
||||
x3 := (0xffffffffffffe - arg1[2])
|
||||
x4 := (0xffffffffffffe - arg1[3])
|
||||
x5 := (0xffffffffffffe - arg1[4])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_cond_assign :: 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])
|
||||
x4 := fiat.cmovznz_u64(fiat.u1(arg2), out1[3], arg1[3])
|
||||
x5 := fiat.cmovznz_u64(fiat.u1(arg2), out1[4], arg1[4])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
out1[3] = x4
|
||||
out1[4] = x5
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: ^[32]byte, arg1: ^Tight_Field_Element) {
|
||||
x1, x2 := _subborrowx_u51(0x0, arg1[0], 0x7ffffffffffed)
|
||||
x3, x4 := _subborrowx_u51(x2, arg1[1], 0x7ffffffffffff)
|
||||
x5, x6 := _subborrowx_u51(x4, arg1[2], 0x7ffffffffffff)
|
||||
x7, x8 := _subborrowx_u51(x6, arg1[3], 0x7ffffffffffff)
|
||||
x9, x10 := _subborrowx_u51(x8, arg1[4], 0x7ffffffffffff)
|
||||
x11 := fiat.cmovznz_u64(x10, u64(0x0), 0xffffffffffffffff)
|
||||
x12, x13 := _addcarryx_u51(0x0, x1, (x11 & 0x7ffffffffffed))
|
||||
x14, x15 := _addcarryx_u51(x13, x3, (x11 & 0x7ffffffffffff))
|
||||
x16, x17 := _addcarryx_u51(x15, x5, (x11 & 0x7ffffffffffff))
|
||||
x18, x19 := _addcarryx_u51(x17, x7, (x11 & 0x7ffffffffffff))
|
||||
x20, _ := _addcarryx_u51(x19, x9, (x11 & 0x7ffffffffffff))
|
||||
x22 := (x20 << 4)
|
||||
x23 := (x18 * u64(0x2))
|
||||
x24 := (x16 << 6)
|
||||
x25 := (x14 << 3)
|
||||
x26 := (u8(x12) & 0xff)
|
||||
x27 := (x12 >> 8)
|
||||
x28 := (u8(x27) & 0xff)
|
||||
x29 := (x27 >> 8)
|
||||
x30 := (u8(x29) & 0xff)
|
||||
x31 := (x29 >> 8)
|
||||
x32 := (u8(x31) & 0xff)
|
||||
x33 := (x31 >> 8)
|
||||
x34 := (u8(x33) & 0xff)
|
||||
x35 := (x33 >> 8)
|
||||
x36 := (u8(x35) & 0xff)
|
||||
x37 := u8((x35 >> 8))
|
||||
x38 := (x25 + u64(x37))
|
||||
x39 := (u8(x38) & 0xff)
|
||||
x40 := (x38 >> 8)
|
||||
x41 := (u8(x40) & 0xff)
|
||||
x42 := (x40 >> 8)
|
||||
x43 := (u8(x42) & 0xff)
|
||||
x44 := (x42 >> 8)
|
||||
x45 := (u8(x44) & 0xff)
|
||||
x46 := (x44 >> 8)
|
||||
x47 := (u8(x46) & 0xff)
|
||||
x48 := (x46 >> 8)
|
||||
x49 := (u8(x48) & 0xff)
|
||||
x50 := u8((x48 >> 8))
|
||||
x51 := (x24 + u64(x50))
|
||||
x52 := (u8(x51) & 0xff)
|
||||
x53 := (x51 >> 8)
|
||||
x54 := (u8(x53) & 0xff)
|
||||
x55 := (x53 >> 8)
|
||||
x56 := (u8(x55) & 0xff)
|
||||
x57 := (x55 >> 8)
|
||||
x58 := (u8(x57) & 0xff)
|
||||
x59 := (x57 >> 8)
|
||||
x60 := (u8(x59) & 0xff)
|
||||
x61 := (x59 >> 8)
|
||||
x62 := (u8(x61) & 0xff)
|
||||
x63 := (x61 >> 8)
|
||||
x64 := (u8(x63) & 0xff)
|
||||
x65 := fiat.u1((x63 >> 8))
|
||||
x66 := (x23 + u64(x65))
|
||||
x67 := (u8(x66) & 0xff)
|
||||
x68 := (x66 >> 8)
|
||||
x69 := (u8(x68) & 0xff)
|
||||
x70 := (x68 >> 8)
|
||||
x71 := (u8(x70) & 0xff)
|
||||
x72 := (x70 >> 8)
|
||||
x73 := (u8(x72) & 0xff)
|
||||
x74 := (x72 >> 8)
|
||||
x75 := (u8(x74) & 0xff)
|
||||
x76 := (x74 >> 8)
|
||||
x77 := (u8(x76) & 0xff)
|
||||
x78 := u8((x76 >> 8))
|
||||
x79 := (x22 + u64(x78))
|
||||
x80 := (u8(x79) & 0xff)
|
||||
x81 := (x79 >> 8)
|
||||
x82 := (u8(x81) & 0xff)
|
||||
x83 := (x81 >> 8)
|
||||
x84 := (u8(x83) & 0xff)
|
||||
x85 := (x83 >> 8)
|
||||
x86 := (u8(x85) & 0xff)
|
||||
x87 := (x85 >> 8)
|
||||
x88 := (u8(x87) & 0xff)
|
||||
x89 := (x87 >> 8)
|
||||
x90 := (u8(x89) & 0xff)
|
||||
x91 := u8((x89 >> 8))
|
||||
out1[0] = x26
|
||||
out1[1] = x28
|
||||
out1[2] = x30
|
||||
out1[3] = x32
|
||||
out1[4] = x34
|
||||
out1[5] = x36
|
||||
out1[6] = x39
|
||||
out1[7] = x41
|
||||
out1[8] = x43
|
||||
out1[9] = x45
|
||||
out1[10] = x47
|
||||
out1[11] = x49
|
||||
out1[12] = x52
|
||||
out1[13] = x54
|
||||
out1[14] = x56
|
||||
out1[15] = x58
|
||||
out1[16] = x60
|
||||
out1[17] = x62
|
||||
out1[18] = x64
|
||||
out1[19] = x67
|
||||
out1[20] = x69
|
||||
out1[21] = x71
|
||||
out1[22] = x73
|
||||
out1[23] = x75
|
||||
out1[24] = x77
|
||||
out1[25] = x80
|
||||
out1[26] = x82
|
||||
out1[27] = x84
|
||||
out1[28] = x86
|
||||
out1[29] = x88
|
||||
out1[30] = x90
|
||||
out1[31] = x91
|
||||
}
|
||||
|
||||
_fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) {
|
||||
x1 := (u64(arg1[31]) << 44)
|
||||
x2 := (u64(arg1[30]) << 36)
|
||||
x3 := (u64(arg1[29]) << 28)
|
||||
x4 := (u64(arg1[28]) << 20)
|
||||
x5 := (u64(arg1[27]) << 12)
|
||||
x6 := (u64(arg1[26]) << 4)
|
||||
x7 := (u64(arg1[25]) << 47)
|
||||
x8 := (u64(arg1[24]) << 39)
|
||||
x9 := (u64(arg1[23]) << 31)
|
||||
x10 := (u64(arg1[22]) << 23)
|
||||
x11 := (u64(arg1[21]) << 15)
|
||||
x12 := (u64(arg1[20]) << 7)
|
||||
x13 := (u64(arg1[19]) << 50)
|
||||
x14 := (u64(arg1[18]) << 42)
|
||||
x15 := (u64(arg1[17]) << 34)
|
||||
x16 := (u64(arg1[16]) << 26)
|
||||
x17 := (u64(arg1[15]) << 18)
|
||||
x18 := (u64(arg1[14]) << 10)
|
||||
x19 := (u64(arg1[13]) << 2)
|
||||
x20 := (u64(arg1[12]) << 45)
|
||||
x21 := (u64(arg1[11]) << 37)
|
||||
x22 := (u64(arg1[10]) << 29)
|
||||
x23 := (u64(arg1[9]) << 21)
|
||||
x24 := (u64(arg1[8]) << 13)
|
||||
x25 := (u64(arg1[7]) << 5)
|
||||
x26 := (u64(arg1[6]) << 48)
|
||||
x27 := (u64(arg1[5]) << 40)
|
||||
x28 := (u64(arg1[4]) << 32)
|
||||
x29 := (u64(arg1[3]) << 24)
|
||||
x30 := (u64(arg1[2]) << 16)
|
||||
x31 := (u64(arg1[1]) << 8)
|
||||
x32 := arg1[0]
|
||||
x33 := (x31 + u64(x32))
|
||||
x34 := (x30 + x33)
|
||||
x35 := (x29 + x34)
|
||||
x36 := (x28 + x35)
|
||||
x37 := (x27 + x36)
|
||||
x38 := (x26 + x37)
|
||||
x39 := (x38 & 0x7ffffffffffff)
|
||||
x40 := u8((x38 >> 51))
|
||||
x41 := (x25 + u64(x40))
|
||||
x42 := (x24 + x41)
|
||||
x43 := (x23 + x42)
|
||||
x44 := (x22 + x43)
|
||||
x45 := (x21 + x44)
|
||||
x46 := (x20 + x45)
|
||||
x47 := (x46 & 0x7ffffffffffff)
|
||||
x48 := u8((x46 >> 51))
|
||||
x49 := (x19 + u64(x48))
|
||||
x50 := (x18 + x49)
|
||||
x51 := (x17 + x50)
|
||||
x52 := (x16 + x51)
|
||||
x53 := (x15 + x52)
|
||||
x54 := (x14 + x53)
|
||||
x55 := (x13 + x54)
|
||||
x56 := (x55 & 0x7ffffffffffff)
|
||||
x57 := u8((x55 >> 51))
|
||||
x58 := (x12 + u64(x57))
|
||||
x59 := (x11 + x58)
|
||||
x60 := (x10 + x59)
|
||||
x61 := (x9 + x60)
|
||||
x62 := (x8 + x61)
|
||||
x63 := (x7 + x62)
|
||||
x64 := (x63 & 0x7ffffffffffff)
|
||||
x65 := u8((x63 >> 51))
|
||||
x66 := (x6 + u64(x65))
|
||||
x67 := (x5 + x66)
|
||||
x68 := (x4 + x67)
|
||||
x69 := (x3 + x68)
|
||||
x70 := (x2 + x69)
|
||||
x71 := (x1 + x70)
|
||||
out1[0] = x39
|
||||
out1[1] = x47
|
||||
out1[2] = x56
|
||||
out1[3] = x64
|
||||
out1[4] = x71
|
||||
}
|
||||
|
||||
fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, 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
|
||||
}
|
||||
|
||||
fe_carry_scmul_121666 :: proc (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])
|
||||
x8, x7 := bits.mul_u64(0x1db42, arg1[1])
|
||||
x10, x9 := bits.mul_u64(0x1db42, arg1[0])
|
||||
x11 := ((x9 >> 51) | ((x10 << 13) & 0xffffffffffffffff))
|
||||
x12 := (x9 & 0x7ffffffffffff)
|
||||
x13, x14 := bits.add_u64(x11, x7, u64(0x0))
|
||||
x15 := (u64(fiat.u1(x14)) + x8)
|
||||
x16 := ((x13 >> 51) | ((x15 << 13) & 0xffffffffffffffff))
|
||||
x17 := (x13 & 0x7ffffffffffff)
|
||||
x18, x19 := bits.add_u64(x16, x5, u64(0x0))
|
||||
x20 := (u64(fiat.u1(x19)) + x6)
|
||||
x21 := ((x18 >> 51) | ((x20 << 13) & 0xffffffffffffffff))
|
||||
x22 := (x18 & 0x7ffffffffffff)
|
||||
x23, x24 := bits.add_u64(x21, x3, u64(0x0))
|
||||
x25 := (u64(fiat.u1(x24)) + x4)
|
||||
x26 := ((x23 >> 51) | ((x25 << 13) & 0xffffffffffffffff))
|
||||
x27 := (x23 & 0x7ffffffffffff)
|
||||
x28, x29 := bits.add_u64(x26, x1, u64(0x0))
|
||||
x30 := (u64(fiat.u1(x29)) + x2)
|
||||
x31 := ((x28 >> 51) | ((x30 << 13) & 0xffffffffffffffff))
|
||||
x32 := (x28 & 0x7ffffffffffff)
|
||||
x33 := (x31 * 0x13)
|
||||
x34 := (x12 + x33)
|
||||
x35 := fiat.u1((x34 >> 51))
|
||||
x36 := (x34 & 0x7ffffffffffff)
|
||||
x37 := (u64(x35) + x17)
|
||||
x38 := fiat.u1((x37 >> 51))
|
||||
x39 := (x37 & 0x7ffffffffffff)
|
||||
x40 := (u64(x38) + x22)
|
||||
out1[0] = x36
|
||||
out1[1] = x39
|
||||
out1[2] = x40
|
||||
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
|
||||
}
|
||||
|
||||
fe_cond_swap :: 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
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package field_poly1305
|
||||
|
||||
import "core:crypto/util"
|
||||
import "core:mem"
|
||||
|
||||
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 {
|
||||
return transmute(^Tight_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, arg2: byte, sanitize: bool = true) {
|
||||
// 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.
|
||||
//
|
||||
// This is somewhat cumbersome to use, so at a minimum a wrapper
|
||||
// makes implementing the actual MAC block processing considerably
|
||||
// neater.
|
||||
|
||||
assert(len(arg1) == 16)
|
||||
|
||||
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||
// While it may be unwise to do deserialization here on our
|
||||
// own when fiat-crypto provides equivalent functionality,
|
||||
// doing it this way provides a little under 3x performance
|
||||
// improvement when optimization is enabled.
|
||||
src_p := transmute(^[2]u64)(&arg1[0])
|
||||
lo := src_p[0]
|
||||
hi := src_p[1]
|
||||
|
||||
// This is inspired by poly1305-donna, though adjustments were
|
||||
// made since a Tight_Field_Element's limbs are 44-bits, 43-bits,
|
||||
// and 43-bits wide.
|
||||
//
|
||||
// Note: This could be transplated into fe_from_u64s, but that
|
||||
// code is called once per MAC, and is non-criticial path.
|
||||
hibit := u64(arg2) << 41 // arg2 << 128
|
||||
out1[0] = lo & 0xfffffffffff
|
||||
out1[1] = ((lo >> 44) | (hi << 20)) & 0x7ffffffffff
|
||||
out1[2] = ((hi >> 23) & 0x7ffffffffff) | hibit
|
||||
} else {
|
||||
tmp: [32]byte
|
||||
copy_slice(tmp[0:16], arg1[:])
|
||||
tmp[16] = arg2
|
||||
|
||||
_fe_from_bytes(out1, &tmp)
|
||||
if sanitize {
|
||||
// This is used to deserialize `s` which is confidential.
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fe_from_u64s :: proc "contextless" (out1: ^Tight_Field_Element, lo, hi: u64) {
|
||||
tmp: [32]byte
|
||||
util.PUT_U64_LE(tmp[0:8], lo)
|
||||
util.PUT_U64_LE(tmp[8:16], hi)
|
||||
|
||||
_fe_from_bytes(out1, &tmp)
|
||||
|
||||
// This routine is only used to deserialize `r` which is confidential.
|
||||
mem.zero_explicit(&tmp, size_of(tmp))
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
// 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_poly1305
|
||||
|
||||
// This file provides arithmetic on the field Z/(2^130 - 5) using
|
||||
// unsaturated 64-bit integer arithmetic. It is derived primarily
|
||||
// from the machine generate 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.
|
||||
// At some point, it may be worth adding support to fiat-crypto for
|
||||
// generating Odin output.
|
||||
|
||||
import fiat "core:crypto/_fiat"
|
||||
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) {
|
||||
x1 := ((u64(arg1) + arg2) + arg3)
|
||||
x2 := (x1 & 0xfffffffffff)
|
||||
x3 := fiat.u1((x1 >> 44))
|
||||
out1 = x2
|
||||
out2 = x3
|
||||
return
|
||||
}
|
||||
|
||||
_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)
|
||||
out1 = x3
|
||||
out2 = (0x0 - fiat.u1(x2))
|
||||
return
|
||||
}
|
||||
|
||||
_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))
|
||||
out1 = x2
|
||||
out2 = x3
|
||||
return
|
||||
}
|
||||
|
||||
_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)
|
||||
out1 = x3
|
||||
out2 = (0x0 - fiat.u1(x2))
|
||||
return
|
||||
}
|
||||
|
||||
fe_carry_mul :: proc (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))
|
||||
x8, x7 := bits.mul_u64(arg1[2], arg2[0])
|
||||
x10, x9 := bits.mul_u64(arg1[1], (arg2[1] * 0x2))
|
||||
x12, x11 := bits.mul_u64(arg1[1], arg2[0])
|
||||
x14, x13 := bits.mul_u64(arg1[0], arg2[2])
|
||||
x16, x15 := bits.mul_u64(arg1[0], arg2[1])
|
||||
x18, x17 := bits.mul_u64(arg1[0], arg2[0])
|
||||
x19, x20 := bits.add_u64(x5, x3, u64(0x0))
|
||||
x21, _ := bits.add_u64(x6, x4, u64(fiat.u1(x20)))
|
||||
x23, x24 := bits.add_u64(x17, x19, u64(0x0))
|
||||
x25, _ := bits.add_u64(x18, x21, u64(fiat.u1(x24)))
|
||||
x27 := ((x23 >> 44) | ((x25 << 20) & 0xffffffffffffffff))
|
||||
x28 := (x23 & 0xfffffffffff)
|
||||
x29, x30 := bits.add_u64(x9, x7, u64(0x0))
|
||||
x31, _ := bits.add_u64(x10, x8, u64(fiat.u1(x30)))
|
||||
x33, x34 := bits.add_u64(x13, x29, u64(0x0))
|
||||
x35, _ := bits.add_u64(x14, x31, u64(fiat.u1(x34)))
|
||||
x37, x38 := bits.add_u64(x11, x1, u64(0x0))
|
||||
x39, _ := bits.add_u64(x12, x2, u64(fiat.u1(x38)))
|
||||
x41, x42 := bits.add_u64(x15, x37, u64(0x0))
|
||||
x43, _ := bits.add_u64(x16, x39, u64(fiat.u1(x42)))
|
||||
x45, x46 := bits.add_u64(x27, x41, u64(0x0))
|
||||
x47 := (u64(fiat.u1(x46)) + x43)
|
||||
x48 := ((x45 >> 43) | ((x47 << 21) & 0xffffffffffffffff))
|
||||
x49 := (x45 & 0x7ffffffffff)
|
||||
x50, x51 := bits.add_u64(x48, x33, u64(0x0))
|
||||
x52 := (u64(fiat.u1(x51)) + x35)
|
||||
x53 := ((x50 >> 43) | ((x52 << 21) & 0xffffffffffffffff))
|
||||
x54 := (x50 & 0x7ffffffffff)
|
||||
x55 := (x53 * 0x5)
|
||||
x56 := (x28 + x55)
|
||||
x57 := (x56 >> 44)
|
||||
x58 := (x56 & 0xfffffffffff)
|
||||
x59 := (x57 + x49)
|
||||
x60 := fiat.u1((x59 >> 43))
|
||||
x61 := (x59 & 0x7ffffffffff)
|
||||
x62 := (u64(x60) + x54)
|
||||
out1[0] = x58
|
||||
out1[1] = x61
|
||||
out1[2] = x62
|
||||
}
|
||||
|
||||
fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
x1 := (arg1[2] * 0x5)
|
||||
x2 := (x1 * 0x2)
|
||||
x3 := (arg1[2] * 0x2)
|
||||
x4 := (arg1[1] * 0x2)
|
||||
x6, x5 := bits.mul_u64(arg1[2], x1)
|
||||
x8, x7 := bits.mul_u64(arg1[1], (x2 * 0x2))
|
||||
x10, x9 := bits.mul_u64(arg1[1], (arg1[1] * 0x2))
|
||||
x12, x11 := bits.mul_u64(arg1[0], x3)
|
||||
x14, x13 := bits.mul_u64(arg1[0], x4)
|
||||
x16, x15 := bits.mul_u64(arg1[0], arg1[0])
|
||||
x17, x18 := bits.add_u64(x15, x7, u64(0x0))
|
||||
x19, _ := bits.add_u64(x16, x8, u64(fiat.u1(x18)))
|
||||
x21 := ((x17 >> 44) | ((x19 << 20) & 0xffffffffffffffff))
|
||||
x22 := (x17 & 0xfffffffffff)
|
||||
x23, x24 := bits.add_u64(x11, x9, u64(0x0))
|
||||
x25, _ := bits.add_u64(x12, x10, u64(fiat.u1(x24)))
|
||||
x27, x28 := bits.add_u64(x13, x5, u64(0x0))
|
||||
x29, _ := bits.add_u64(x14, x6, u64(fiat.u1(x28)))
|
||||
x31, x32 := bits.add_u64(x21, x27, u64(0x0))
|
||||
x33 := (u64(fiat.u1(x32)) + x29)
|
||||
x34 := ((x31 >> 43) | ((x33 << 21) & 0xffffffffffffffff))
|
||||
x35 := (x31 & 0x7ffffffffff)
|
||||
x36, x37 := bits.add_u64(x34, x23, u64(0x0))
|
||||
x38 := (u64(fiat.u1(x37)) + x25)
|
||||
x39 := ((x36 >> 43) | ((x38 << 21) & 0xffffffffffffffff))
|
||||
x40 := (x36 & 0x7ffffffffff)
|
||||
x41 := (x39 * 0x5)
|
||||
x42 := (x22 + x41)
|
||||
x43 := (x42 >> 44)
|
||||
x44 := (x42 & 0xfffffffffff)
|
||||
x45 := (x43 + x35)
|
||||
x46 := fiat.u1((x45 >> 43))
|
||||
x47 := (x45 & 0x7ffffffffff)
|
||||
x48 := (u64(x46) + x40)
|
||||
out1[0] = x44
|
||||
out1[1] = x47
|
||||
out1[2] = x48
|
||||
}
|
||||
|
||||
fe_carry :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := ((x1 >> 44) + arg1[1])
|
||||
x3 := ((x2 >> 43) + arg1[2])
|
||||
x4 := ((x1 & 0xfffffffffff) + ((x3 >> 43) * 0x5))
|
||||
x5 := (u64(fiat.u1((x4 >> 44))) + (x2 & 0x7ffffffffff))
|
||||
x6 := (x4 & 0xfffffffffff)
|
||||
x7 := (x5 & 0x7ffffffffff)
|
||||
x8 := (u64(fiat.u1((x5 >> 43))) + (x3 & 0x7ffffffffff))
|
||||
out1[0] = x6
|
||||
out1[1] = x7
|
||||
out1[2] = x8
|
||||
}
|
||||
|
||||
fe_add :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
|
||||
x1 := (arg1[0] + arg2[0])
|
||||
x2 := (arg1[1] + arg2[1])
|
||||
x3 := (arg1[2] + arg2[2])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
fe_sub :: proc "contextless" (out1: ^Loose_Field_Element, arg1, arg2: ^Tight_Field_Element) {
|
||||
x1 := ((0x1ffffffffff6 + arg1[0]) - arg2[0])
|
||||
x2 := ((0xffffffffffe + arg1[1]) - arg2[1])
|
||||
x3 := ((0xffffffffffe + arg1[2]) - arg2[2])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
|
||||
x1 := (0x1ffffffffff6 - arg1[0])
|
||||
x2 := (0xffffffffffe - arg1[1])
|
||||
x3 := (0xffffffffffe - arg1[2])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
fe_cond_assign :: 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])
|
||||
out1[0] = x1
|
||||
out1[1] = x2
|
||||
out1[2] = x3
|
||||
}
|
||||
|
||||
fe_to_bytes :: proc "contextless" (out1: ^[32]byte, arg1: ^Tight_Field_Element) {
|
||||
x1, x2 := _subborrowx_u44(0x0, arg1[0], 0xffffffffffb)
|
||||
x3, x4 := _subborrowx_u43(x2, arg1[1], 0x7ffffffffff)
|
||||
x5, x6 := _subborrowx_u43(x4, arg1[2], 0x7ffffffffff)
|
||||
x7 := fiat.cmovznz_u64(x6, u64(0x0), 0xffffffffffffffff)
|
||||
x8, x9 := _addcarryx_u44(0x0, x1, (x7 & 0xffffffffffb))
|
||||
x10, x11 := _addcarryx_u43(x9, x3, (x7 & 0x7ffffffffff))
|
||||
x12, _ := _addcarryx_u43(x11, x5, (x7 & 0x7ffffffffff))
|
||||
x14 := (x12 << 7)
|
||||
x15 := (x10 << 4)
|
||||
x16 := (u8(x8) & 0xff)
|
||||
x17 := (x8 >> 8)
|
||||
x18 := (u8(x17) & 0xff)
|
||||
x19 := (x17 >> 8)
|
||||
x20 := (u8(x19) & 0xff)
|
||||
x21 := (x19 >> 8)
|
||||
x22 := (u8(x21) & 0xff)
|
||||
x23 := (x21 >> 8)
|
||||
x24 := (u8(x23) & 0xff)
|
||||
x25 := u8((x23 >> 8))
|
||||
x26 := (x15 + u64(x25))
|
||||
x27 := (u8(x26) & 0xff)
|
||||
x28 := (x26 >> 8)
|
||||
x29 := (u8(x28) & 0xff)
|
||||
x30 := (x28 >> 8)
|
||||
x31 := (u8(x30) & 0xff)
|
||||
x32 := (x30 >> 8)
|
||||
x33 := (u8(x32) & 0xff)
|
||||
x34 := (x32 >> 8)
|
||||
x35 := (u8(x34) & 0xff)
|
||||
x36 := u8((x34 >> 8))
|
||||
x37 := (x14 + u64(x36))
|
||||
x38 := (u8(x37) & 0xff)
|
||||
x39 := (x37 >> 8)
|
||||
x40 := (u8(x39) & 0xff)
|
||||
x41 := (x39 >> 8)
|
||||
x42 := (u8(x41) & 0xff)
|
||||
x43 := (x41 >> 8)
|
||||
x44 := (u8(x43) & 0xff)
|
||||
x45 := (x43 >> 8)
|
||||
x46 := (u8(x45) & 0xff)
|
||||
x47 := (x45 >> 8)
|
||||
x48 := (u8(x47) & 0xff)
|
||||
x49 := u8((x47 >> 8))
|
||||
out1[0] = x16
|
||||
out1[1] = x18
|
||||
out1[2] = x20
|
||||
out1[3] = x22
|
||||
out1[4] = x24
|
||||
out1[5] = x27
|
||||
out1[6] = x29
|
||||
out1[7] = x31
|
||||
out1[8] = x33
|
||||
out1[9] = x35
|
||||
out1[10] = x38
|
||||
out1[11] = x40
|
||||
out1[12] = x42
|
||||
out1[13] = x44
|
||||
out1[14] = x46
|
||||
out1[15] = x48
|
||||
out1[16] = x49
|
||||
}
|
||||
|
||||
_fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) {
|
||||
x1 := (u64(arg1[16]) << 41)
|
||||
x2 := (u64(arg1[15]) << 33)
|
||||
x3 := (u64(arg1[14]) << 25)
|
||||
x4 := (u64(arg1[13]) << 17)
|
||||
x5 := (u64(arg1[12]) << 9)
|
||||
x6 := (u64(arg1[11]) * u64(0x2))
|
||||
x7 := (u64(arg1[10]) << 36)
|
||||
x8 := (u64(arg1[9]) << 28)
|
||||
x9 := (u64(arg1[8]) << 20)
|
||||
x10 := (u64(arg1[7]) << 12)
|
||||
x11 := (u64(arg1[6]) << 4)
|
||||
x12 := (u64(arg1[5]) << 40)
|
||||
x13 := (u64(arg1[4]) << 32)
|
||||
x14 := (u64(arg1[3]) << 24)
|
||||
x15 := (u64(arg1[2]) << 16)
|
||||
x16 := (u64(arg1[1]) << 8)
|
||||
x17 := arg1[0]
|
||||
x18 := (x16 + u64(x17))
|
||||
x19 := (x15 + x18)
|
||||
x20 := (x14 + x19)
|
||||
x21 := (x13 + x20)
|
||||
x22 := (x12 + x21)
|
||||
x23 := (x22 & 0xfffffffffff)
|
||||
x24 := u8((x22 >> 44))
|
||||
x25 := (x11 + u64(x24))
|
||||
x26 := (x10 + x25)
|
||||
x27 := (x9 + x26)
|
||||
x28 := (x8 + x27)
|
||||
x29 := (x7 + x28)
|
||||
x30 := (x29 & 0x7ffffffffff)
|
||||
x31 := fiat.u1((x29 >> 43))
|
||||
x32 := (x6 + u64(x31))
|
||||
x33 := (x5 + x32)
|
||||
x34 := (x4 + x33)
|
||||
x35 := (x3 + x34)
|
||||
x36 := (x2 + x35)
|
||||
x37 := (x1 + x36)
|
||||
out1[0] = x23
|
||||
out1[1] = x30
|
||||
out1[2] = x37
|
||||
}
|
||||
|
||||
fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Element) {
|
||||
x1 := arg1[0]
|
||||
x2 := arg1[1]
|
||||
x3 := arg1[2]
|
||||
out1[0] = x1
|
||||
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
|
||||
}
|
||||
|
||||
fe_cond_swap :: 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
|
||||
}
|
||||
@@ -6,7 +6,6 @@ package _sha3
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
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.
|
||||
@@ -53,7 +52,7 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
|
||||
t: u64 = ---
|
||||
bc: [5]u64 = ---
|
||||
|
||||
when ODIN_ENDIAN != "little" {
|
||||
when ODIN_ENDIAN != .Little {
|
||||
v: uintptr = ---
|
||||
for i = 0; i < 25; i += 1 {
|
||||
v := uintptr(&st[i])
|
||||
@@ -99,7 +98,7 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
|
||||
st[0] ~= keccakf_rndc[r]
|
||||
}
|
||||
|
||||
when ODIN_ENDIAN != "little" {
|
||||
when ODIN_ENDIAN != .Little {
|
||||
for i = 0; i < 25; i += 1 {
|
||||
v = uintptr(&st[i])
|
||||
t = st[i]
|
||||
@@ -115,14 +114,14 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc "contextless" (c: ^Sha3_Context) {
|
||||
init :: proc "contextless" (c: ^Sha3_Context) {
|
||||
for i := 0; i < 25; i += 1 {
|
||||
c.st.q[i] = 0
|
||||
}
|
||||
c.rsiz = 200 - 2 * c.mdlen
|
||||
}
|
||||
|
||||
update_odin :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
|
||||
update :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
|
||||
j := c.pt
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
c.st.b[j] ~= data[i]
|
||||
@@ -135,7 +134,7 @@ update_odin :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
|
||||
c.pt = j
|
||||
}
|
||||
|
||||
final_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
final :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
if c.is_keccak {
|
||||
c.st.b[c.pt] ~= 0x01
|
||||
} else {
|
||||
@@ -149,14 +148,14 @@ final_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
shake_xof_odin :: proc "contextless" (c: ^Sha3_Context) {
|
||||
shake_xof :: proc "contextless" (c: ^Sha3_Context) {
|
||||
c.st.b[c.pt] ~= 0x1F
|
||||
c.st.b[c.rsiz - 1] ~= 0x80
|
||||
keccakf(&c.st.q)
|
||||
c.pt = 0
|
||||
}
|
||||
|
||||
shake_out_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
shake_out :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
j := c.pt
|
||||
for i := 0; i < len(hash); i += 1 {
|
||||
if j >= c.rsiz {
|
||||
|
||||
@@ -6,7 +6,6 @@ package _tiger
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the Tiger hashing algorithm, as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
||||
*/
|
||||
@@ -291,7 +290,7 @@ Tiger_Context :: struct {
|
||||
ver: int,
|
||||
}
|
||||
|
||||
round :: #force_inline proc "contextless"(a, b, c, x, mul: u64) -> (u64, u64, u64) {
|
||||
round :: #force_inline proc "contextless" (a, b, c, x, mul: u64) -> (u64, u64, u64) {
|
||||
a, b, c := a, b, c
|
||||
c ~= x
|
||||
a -= T1[c & 0xff] ~ T2[(c >> 16) & 0xff] ~ T3[(c >> 32) & 0xff] ~ T4[(c >> 48) & 0xff]
|
||||
@@ -300,7 +299,7 @@ round :: #force_inline proc "contextless"(a, b, c, x, mul: u64) -> (u64, u64, u6
|
||||
return a, b, c
|
||||
}
|
||||
|
||||
pass :: #force_inline proc "contextless"(a, b, c: u64, d: []u64, mul: u64) -> (x, y, z: u64) {
|
||||
pass :: #force_inline proc "contextless" (a, b, c: u64, d: []u64, mul: u64) -> (x, y, z: u64) {
|
||||
x, y, z = round(a, b, c, d[0], mul)
|
||||
y, z, x = round(y, z, x, d[1], mul)
|
||||
z, x, y = round(z, x, y, d[2], mul)
|
||||
@@ -312,7 +311,7 @@ pass :: #force_inline proc "contextless"(a, b, c: u64, d: []u64, mul: u64) -> (x
|
||||
return
|
||||
}
|
||||
|
||||
key_schedule :: #force_inline proc "contextless"(x: []u64) {
|
||||
key_schedule :: #force_inline proc "contextless" (x: []u64) {
|
||||
x[0] -= x[7] ~ 0xa5a5a5a5a5a5a5a5
|
||||
x[1] ~= x[0]
|
||||
x[2] += x[1]
|
||||
@@ -331,7 +330,7 @@ key_schedule :: #force_inline proc "contextless"(x: []u64) {
|
||||
x[7] -= x[6] ~ 0x0123456789abcdef
|
||||
}
|
||||
|
||||
compress :: #force_inline proc "contextless"(ctx: ^Tiger_Context, data: []byte) {
|
||||
compress :: #force_inline proc "contextless" (ctx: ^Tiger_Context, data: []byte) {
|
||||
a := ctx.a
|
||||
b := ctx.b
|
||||
c := ctx.c
|
||||
@@ -346,13 +345,13 @@ compress :: #force_inline proc "contextless"(ctx: ^Tiger_Context, data: []byte)
|
||||
ctx.c += c
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Tiger_Context) {
|
||||
init :: proc "contextless" (ctx: ^Tiger_Context) {
|
||||
ctx.a = 0x0123456789abcdef
|
||||
ctx.b = 0xfedcba9876543210
|
||||
ctx.c = 0xf096a5b4c3b2e187
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Tiger_Context, input: []byte) {
|
||||
update :: proc(ctx: ^Tiger_Context, input: []byte) {
|
||||
p := make([]byte, len(input))
|
||||
copy(p, input)
|
||||
|
||||
@@ -380,7 +379,7 @@ update_odin :: proc(ctx: ^Tiger_Context, input: []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Tiger_Context, hash: []byte) {
|
||||
final :: proc(ctx: ^Tiger_Context, hash: []byte) {
|
||||
length := ctx.length
|
||||
tmp: [64]byte
|
||||
if ctx.ver == 1 {
|
||||
@@ -391,16 +390,16 @@ final_odin :: proc(ctx: ^Tiger_Context, hash: []byte) {
|
||||
|
||||
size := length & 0x3f
|
||||
if size < 56 {
|
||||
update_odin(ctx, tmp[:56 - size])
|
||||
update(ctx, tmp[:56 - size])
|
||||
} else {
|
||||
update_odin(ctx, tmp[:64 + 56 - size])
|
||||
update(ctx, tmp[:64 + 56 - size])
|
||||
}
|
||||
|
||||
length <<= 3
|
||||
for i := uint(0); i < 8; i += 1 {
|
||||
tmp[i] = byte(length >> (8 * i))
|
||||
}
|
||||
update_odin(ctx, tmp[:8])
|
||||
update(ctx, tmp[:8])
|
||||
|
||||
for i := uint(0); i < 8; i += 1 {
|
||||
tmp[i] = byte(ctx.a >> (8 * i))
|
||||
|
||||
+397
-505
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,6 @@ package blake2b
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the BLAKE2B hashing algorithm.
|
||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
||||
@@ -15,78 +14,89 @@ package blake2b
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_blake2"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_64 = hash_bytes_odin
|
||||
ctx.hash_file_64 = hash_file_odin
|
||||
ctx.hash_stream_64 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_BLAKE2B)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 64
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [64]byte {
|
||||
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) -> [64]byte {
|
||||
_create_blake2b_ctx()
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: _blake2.Blake2b_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
_blake2.update(&ctx, data)
|
||||
_blake2.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _blake2.Blake2b_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
_blake2.update(&ctx, data)
|
||||
_blake2.final(&ctx, hash)
|
||||
}
|
||||
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_blake2b_ctx()
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: _blake2.Blake2b_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_blake2.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_blake2.final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
_create_blake2b_ctx()
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
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 {
|
||||
@@ -94,93 +104,24 @@ hash :: proc {
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
Blake2b_Context :: _blake2.Blake2b_Context
|
||||
|
||||
init :: proc(ctx: ^_blake2.Blake2b_Context) {
|
||||
_blake2.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
_blake2.update_odin(&c, data)
|
||||
_blake2.blake2b_final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_blake2.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_blake2.blake2b_final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_blake2b_ctx :: #force_inline proc() {
|
||||
ctx: _blake2.Blake2b_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2B_SIZE
|
||||
ctx.cfg = cfg
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._64
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_blake2b_ctx()
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
|
||||
_blake2.blake2b_final_odin(&c, hash)
|
||||
}
|
||||
final :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, hash: []byte) {
|
||||
_blake2.final(ctx, hash)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package blake2s
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the BLAKE2S hashing algorithm.
|
||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
||||
@@ -15,78 +14,89 @@ package blake2s
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
import "../_blake2"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin
|
||||
ctx.hash_file_32 = hash_file_odin
|
||||
ctx.hash_stream_32 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since Blake2s is not available in Botan
|
||||
@(warning="Blake2s is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 32
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [32]byte {
|
||||
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) -> [32]byte {
|
||||
_create_blake2s_ctx()
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: _blake2.Blake2s_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
_blake2.update(&ctx, data)
|
||||
_blake2.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _blake2.Blake2s_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
_blake2.update(&ctx, data)
|
||||
_blake2.final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_blake2s_ctx()
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: _blake2.Blake2s_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
ctx.cfg = cfg
|
||||
_blake2.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_blake2.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_blake2.final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
_create_blake2s_ctx()
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
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 {
|
||||
@@ -94,93 +104,24 @@ hash :: proc {
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
Blake2s_Context :: _blake2.Blake2b_Context
|
||||
|
||||
init :: proc(ctx: ^_blake2.Blake2s_Context) {
|
||||
_blake2.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
_blake2.update_odin(&c, data)
|
||||
_blake2.blake2s_final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_blake2.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_blake2.blake2s_final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_blake2s_ctx :: #force_inline proc() {
|
||||
ctx: _blake2.Blake2s_Context
|
||||
cfg: _blake2.Blake2_Config
|
||||
cfg.size = _blake2.BLAKE2S_SIZE
|
||||
ctx.cfg = cfg
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._32
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_blake2s_ctx()
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
|
||||
_blake2.blake2s_final_odin(&c, hash)
|
||||
}
|
||||
final :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, hash: []byte) {
|
||||
_blake2.final(ctx, hash)
|
||||
}
|
||||
|
||||
@@ -1,498 +0,0 @@
|
||||
package botan
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog: Initial creation and testing of the bindings.
|
||||
|
||||
Implementation of the context for the Botan side.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
hash_bytes_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._16, 16), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
|
||||
hash: [20]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._20, 20), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte {
|
||||
hash: [24]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._24, 24), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._28, 28), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._32, 32), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._48, 48), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._64, 64), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [128]byte {
|
||||
hash: [128]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._128, 128), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_bytes_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, nil, bit_size), 0)
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash[:]
|
||||
}
|
||||
|
||||
hash_file_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_16(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_16(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_20(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_20(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [20]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_24(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_24(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [24]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_28(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([128]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_128(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_128(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [128]byte{}, false
|
||||
}
|
||||
|
||||
hash_file_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_slice(ctx, os.stream_from_handle(hd), bit_size, allocator)
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_slice(ctx, buf[:], bit_size, allocator), ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
hash_stream_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._16, 16), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([20]byte, bool) {
|
||||
hash: [20]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._20, 20), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([24]byte, bool) {
|
||||
hash: [24]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._24, 24), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._28, 28), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._32, 32), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._48, 48), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._64, 64), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([128]byte, bool) {
|
||||
hash: [128]byte
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._128, 128), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash, true
|
||||
}
|
||||
|
||||
hash_stream_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
c: hash_t
|
||||
hash_init(&c, _check_ctx(ctx, nil, bit_size), 0)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
i := 1
|
||||
for i > 0 {
|
||||
i, _ = s->impl_read(buf)
|
||||
if i > 0 {
|
||||
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
|
||||
}
|
||||
}
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
return hash[:], true
|
||||
}
|
||||
|
||||
init :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
c: hash_t
|
||||
hash_init(&c, ctx.botan_hash_algo, 0)
|
||||
ctx.external_ctx = c
|
||||
}
|
||||
|
||||
update :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.external_ctx.(hash_t); ok {
|
||||
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
|
||||
}
|
||||
}
|
||||
|
||||
final :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.external_ctx.(hash_t); ok {
|
||||
hash_final(c, &hash[0])
|
||||
hash_destroy(c)
|
||||
}
|
||||
}
|
||||
|
||||
assign_hash_vtable :: proc(ctx: ^_ctx.Hash_Context, hash_algo: cstring) {
|
||||
ctx.init = init
|
||||
ctx.update = update
|
||||
ctx.final = final
|
||||
ctx.botan_hash_algo = hash_algo
|
||||
|
||||
switch hash_algo {
|
||||
case HASH_MD4, HASH_MD5:
|
||||
ctx.hash_bytes_16 = hash_bytes_16
|
||||
ctx.hash_file_16 = hash_file_16
|
||||
ctx.hash_stream_16 = hash_stream_16
|
||||
|
||||
case HASH_SHA1, HASH_RIPEMD_160:
|
||||
ctx.hash_bytes_20 = hash_bytes_20
|
||||
ctx.hash_file_20 = hash_file_20
|
||||
ctx.hash_stream_20 = hash_stream_20
|
||||
|
||||
case HASH_SHA2, HASH_SHA3:
|
||||
ctx.hash_bytes_28 = hash_bytes_28
|
||||
ctx.hash_file_28 = hash_file_28
|
||||
ctx.hash_stream_28 = hash_stream_28
|
||||
ctx.hash_bytes_32 = hash_bytes_32
|
||||
ctx.hash_file_32 = hash_file_32
|
||||
ctx.hash_stream_32 = hash_stream_32
|
||||
ctx.hash_bytes_48 = hash_bytes_48
|
||||
ctx.hash_file_48 = hash_file_48
|
||||
ctx.hash_stream_48 = hash_stream_48
|
||||
ctx.hash_bytes_64 = hash_bytes_64
|
||||
ctx.hash_file_64 = hash_file_64
|
||||
ctx.hash_stream_64 = hash_stream_64
|
||||
|
||||
case HASH_GOST, HASH_WHIRLPOOL, HASH_SM3:
|
||||
ctx.hash_bytes_32 = hash_bytes_32
|
||||
ctx.hash_file_32 = hash_file_32
|
||||
ctx.hash_stream_32 = hash_stream_32
|
||||
|
||||
case HASH_STREEBOG:
|
||||
ctx.hash_bytes_32 = hash_bytes_32
|
||||
ctx.hash_file_32 = hash_file_32
|
||||
ctx.hash_stream_32 = hash_stream_32
|
||||
ctx.hash_bytes_64 = hash_bytes_64
|
||||
ctx.hash_file_64 = hash_file_64
|
||||
ctx.hash_stream_64 = hash_stream_64
|
||||
|
||||
case HASH_BLAKE2B:
|
||||
ctx.hash_bytes_64 = hash_bytes_64
|
||||
ctx.hash_file_64 = hash_file_64
|
||||
ctx.hash_stream_64 = hash_stream_64
|
||||
|
||||
case HASH_TIGER:
|
||||
ctx.hash_bytes_16 = hash_bytes_16
|
||||
ctx.hash_file_16 = hash_file_16
|
||||
ctx.hash_stream_16 = hash_stream_16
|
||||
ctx.hash_bytes_20 = hash_bytes_20
|
||||
ctx.hash_file_20 = hash_file_20
|
||||
ctx.hash_stream_20 = hash_stream_20
|
||||
ctx.hash_bytes_24 = hash_bytes_24
|
||||
ctx.hash_file_24 = hash_file_24
|
||||
ctx.hash_stream_24 = hash_stream_24
|
||||
|
||||
case HASH_SKEIN_512:
|
||||
ctx.hash_bytes_slice = hash_bytes_slice
|
||||
ctx.hash_file_slice = hash_file_slice
|
||||
ctx.hash_stream_slice = hash_stream_slice
|
||||
}
|
||||
}
|
||||
|
||||
_check_ctx :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash_size: _ctx.Hash_Size, hash_size_val: int) -> cstring {
|
||||
ctx.hash_size = hash_size
|
||||
ctx.hash_size_val = hash_size_val
|
||||
switch ctx.botan_hash_algo {
|
||||
case HASH_SHA2:
|
||||
#partial switch hash_size {
|
||||
case ._28: return HASH_SHA_224
|
||||
case ._32: return HASH_SHA_256
|
||||
case ._48: return HASH_SHA_384
|
||||
case ._64: return HASH_SHA_512
|
||||
}
|
||||
case HASH_SHA3:
|
||||
#partial switch hash_size {
|
||||
case ._28: return HASH_SHA3_224
|
||||
case ._32: return HASH_SHA3_256
|
||||
case ._48: return HASH_SHA3_384
|
||||
case ._64: return HASH_SHA3_512
|
||||
}
|
||||
case HASH_KECCAK:
|
||||
#partial switch hash_size {
|
||||
case ._28: return HASH_KECCAK_224
|
||||
case ._32: return HASH_KECCAK_256
|
||||
case ._48: return HASH_KECCAK_384
|
||||
case ._64: return HASH_KECCAK_512
|
||||
}
|
||||
case HASH_STREEBOG:
|
||||
#partial switch hash_size {
|
||||
case ._32: return HASH_STREEBOG_256
|
||||
case ._64: return HASH_STREEBOG_512
|
||||
}
|
||||
case HASH_TIGER:
|
||||
#partial switch hash_size {
|
||||
case ._16: return HASH_TIGER_128
|
||||
case ._20: return HASH_TIGER_160
|
||||
case ._24: return HASH_TIGER_192
|
||||
}
|
||||
case HASH_SKEIN_512:
|
||||
return strings.unsafe_string_to_cstring(fmt.tprintf("Skein-512(%d)", hash_size_val * 8))
|
||||
case: return ctx.botan_hash_algo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,581 @@
|
||||
package chacha20
|
||||
|
||||
import "core:crypto/util"
|
||||
import "core:math/bits"
|
||||
import "core:mem"
|
||||
|
||||
KEY_SIZE :: 32
|
||||
NONCE_SIZE :: 12
|
||||
XNONCE_SIZE :: 24
|
||||
|
||||
_MAX_CTR_IETF :: 0xffffffff
|
||||
|
||||
_BLOCK_SIZE :: 64
|
||||
_STATE_SIZE_U32 :: 16
|
||||
_ROUNDS :: 20
|
||||
|
||||
_SIGMA_0 : u32 : 0x61707865
|
||||
_SIGMA_1 : u32 : 0x3320646e
|
||||
_SIGMA_2 : u32 : 0x79622d32
|
||||
_SIGMA_3 : u32 : 0x6b206574
|
||||
|
||||
Context :: struct {
|
||||
_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) {
|
||||
if len(key) != KEY_SIZE {
|
||||
panic("crypto/chacha20: invalid ChaCha20 key size")
|
||||
}
|
||||
if n_len := len(nonce); n_len != NONCE_SIZE && n_len != XNONCE_SIZE {
|
||||
panic("crypto/chacha20: invalid (X)ChaCha20 nonce size")
|
||||
}
|
||||
|
||||
k, n := key, nonce
|
||||
|
||||
// Derive the XChaCha20 subkey and sub-nonce via HChaCha20.
|
||||
is_xchacha := len(nonce) == XNONCE_SIZE
|
||||
if is_xchacha {
|
||||
sub_key := ctx._buffer[:KEY_SIZE]
|
||||
_hchacha20(sub_key, k, n)
|
||||
k = sub_key
|
||||
n = n[16:24]
|
||||
}
|
||||
|
||||
ctx._s[0] = _SIGMA_0
|
||||
ctx._s[1] = _SIGMA_1
|
||||
ctx._s[2] = _SIGMA_2
|
||||
ctx._s[3] = _SIGMA_3
|
||||
ctx._s[4] = util.U32_LE(k[0:4])
|
||||
ctx._s[5] = util.U32_LE(k[4:8])
|
||||
ctx._s[6] = util.U32_LE(k[8:12])
|
||||
ctx._s[7] = util.U32_LE(k[12:16])
|
||||
ctx._s[8] = util.U32_LE(k[16:20])
|
||||
ctx._s[9] = util.U32_LE(k[20:24])
|
||||
ctx._s[10] = util.U32_LE(k[24:28])
|
||||
ctx._s[11] = util.U32_LE(k[28:32])
|
||||
ctx._s[12] = 0
|
||||
if !is_xchacha {
|
||||
ctx._s[13] = util.U32_LE(n[0:4])
|
||||
ctx._s[14] = util.U32_LE(n[4:8])
|
||||
ctx._s[15] = util.U32_LE(n[8:12])
|
||||
} else {
|
||||
ctx._s[13] = 0
|
||||
ctx._s[14] = util.U32_LE(n[0:4])
|
||||
ctx._s[15] = util.U32_LE(n[4:8])
|
||||
|
||||
// The sub-key is stored in the keystream buffer. While
|
||||
// this will be overwritten in most circumstances, explicitly
|
||||
// clear it out early.
|
||||
mem.zero_explicit(&ctx._buffer, KEY_SIZE)
|
||||
}
|
||||
|
||||
ctx._off = _BLOCK_SIZE
|
||||
ctx._is_ietf_flavor = !is_xchacha
|
||||
ctx._is_initialized = true
|
||||
}
|
||||
|
||||
seek :: proc (ctx: ^Context, block_nr: u64) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
if ctx._is_ietf_flavor {
|
||||
if block_nr > _MAX_CTR_IETF {
|
||||
panic("crypto/chacha20: attempted to seek past maximum counter")
|
||||
}
|
||||
} else {
|
||||
ctx._s[13] = u32(block_nr >> 32)
|
||||
}
|
||||
ctx._s[12] = u32(block_nr)
|
||||
ctx._off = _BLOCK_SIZE
|
||||
}
|
||||
|
||||
xor_bytes :: proc (ctx: ^Context, dst, src: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
// TODO: Enforcing that dst and src alias exactly or not at all
|
||||
// is a good idea, though odd aliasing should be extremely uncommon.
|
||||
|
||||
src, dst := src, dst
|
||||
if dst_len := len(dst); dst_len < len(src) {
|
||||
src = src[:dst_len]
|
||||
}
|
||||
|
||||
for remaining := len(src); remaining > 0; {
|
||||
// Process multiple blocks at once
|
||||
if ctx._off == _BLOCK_SIZE {
|
||||
if nr_blocks := remaining / _BLOCK_SIZE; nr_blocks > 0 {
|
||||
direct_bytes := nr_blocks * _BLOCK_SIZE
|
||||
_do_blocks(ctx, dst, src, nr_blocks)
|
||||
remaining -= direct_bytes
|
||||
if remaining == 0 {
|
||||
return
|
||||
}
|
||||
dst = dst[direct_bytes:]
|
||||
src = src[direct_bytes:]
|
||||
}
|
||||
|
||||
// If there is a partial block, generate and buffer 1 block
|
||||
// worth of keystream.
|
||||
_do_blocks(ctx, ctx._buffer[:], nil, 1)
|
||||
ctx._off = 0
|
||||
}
|
||||
|
||||
// Process partial blocks from the buffered keystream.
|
||||
to_xor := min(_BLOCK_SIZE - ctx._off, remaining)
|
||||
buffered_keystream := ctx._buffer[ctx._off:]
|
||||
for i := 0; i < to_xor; i = i + 1 {
|
||||
dst[i] = buffered_keystream[i] ~ src[i]
|
||||
}
|
||||
ctx._off += to_xor
|
||||
dst = dst[to_xor:]
|
||||
src = src[to_xor:]
|
||||
remaining -= to_xor
|
||||
}
|
||||
}
|
||||
|
||||
keystream_bytes :: proc (ctx: ^Context, dst: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
dst := dst
|
||||
for remaining := len(dst); remaining > 0; {
|
||||
// Process multiple blocks at once
|
||||
if ctx._off == _BLOCK_SIZE {
|
||||
if nr_blocks := remaining / _BLOCK_SIZE; nr_blocks > 0 {
|
||||
direct_bytes := nr_blocks * _BLOCK_SIZE
|
||||
_do_blocks(ctx, dst, nil, nr_blocks)
|
||||
remaining -= direct_bytes
|
||||
if remaining == 0 {
|
||||
return
|
||||
}
|
||||
dst = dst[direct_bytes:]
|
||||
}
|
||||
|
||||
// If there is a partial block, generate and buffer 1 block
|
||||
// worth of keystream.
|
||||
_do_blocks(ctx, ctx._buffer[:], nil, 1)
|
||||
ctx._off = 0
|
||||
}
|
||||
|
||||
// Process partial blocks from the buffered keystream.
|
||||
to_copy := min(_BLOCK_SIZE - ctx._off, remaining)
|
||||
buffered_keystream := ctx._buffer[ctx._off:]
|
||||
copy(dst[:to_copy], buffered_keystream[:to_copy])
|
||||
ctx._off += to_copy
|
||||
dst = dst[to_copy:]
|
||||
remaining -= to_copy
|
||||
}
|
||||
}
|
||||
|
||||
reset :: proc (ctx: ^Context) {
|
||||
mem.zero_explicit(&ctx._s, size_of(ctx._s))
|
||||
mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
||||
|
||||
ctx._is_initialized = false
|
||||
}
|
||||
|
||||
_do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
|
||||
// Enforce the maximum consumed keystream per nonce.
|
||||
//
|
||||
// While all modern "standard" definitions of ChaCha20 use
|
||||
// the IETF 32-bit counter, for XChaCha20 most common
|
||||
// implementations allow for a 64-bit counter.
|
||||
//
|
||||
// Honestly, the answer here is "use a MRAE primitive", but
|
||||
// go with common practice in the case of XChaCha20.
|
||||
if ctx._is_ietf_flavor {
|
||||
if u64(ctx._s[12]) + u64(nr_blocks) > 0xffffffff {
|
||||
panic("crypto/chacha20: maximum ChaCha20 keystream per nonce reached")
|
||||
}
|
||||
} else {
|
||||
ctr := (u64(ctx._s[13]) << 32) | u64(ctx._s[12])
|
||||
if _, carry := bits.add_u64(ctr, u64(nr_blocks), 0); carry != 0 {
|
||||
panic("crypto/chacha20: maximum XChaCha20 keystream per nonce reached")
|
||||
}
|
||||
}
|
||||
|
||||
dst, src := dst, src
|
||||
x := &ctx._s
|
||||
for n := 0; n < nr_blocks; n = n + 1 {
|
||||
x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
|
||||
x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 := x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]
|
||||
|
||||
for i := _ROUNDS; i > 0; i = i - 2 {
|
||||
// Even when forcing inlining manually inlining all of
|
||||
// these is decently faster.
|
||||
|
||||
// quarterround(x, 0, 4, 8, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = util.ROTL32(x12, 16)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
|
||||
// quarterround(x, 1, 5, 9, 13)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
|
||||
// quarterround(x, 2, 6, 10, 14)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
|
||||
// quarterround(x, 3, 7, 11, 15)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
|
||||
// quarterround(x, 0, 5, 10, 15)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
|
||||
// quarterround(x, 1, 6, 11, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 16)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
|
||||
// quarterround(x, 2, 7, 8, 13)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
|
||||
// quarterround(x, 3, 4, 9, 14)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
}
|
||||
|
||||
x0 += _SIGMA_0
|
||||
x1 += _SIGMA_1
|
||||
x2 += _SIGMA_2
|
||||
x3 += _SIGMA_3
|
||||
x4 += x[4]
|
||||
x5 += x[5]
|
||||
x6 += x[6]
|
||||
x7 += x[7]
|
||||
x8 += x[8]
|
||||
x9 += x[9]
|
||||
x10 += x[10]
|
||||
x11 += x[11]
|
||||
x12 += x[12]
|
||||
x13 += x[13]
|
||||
x14 += x[14]
|
||||
x15 += x[15]
|
||||
|
||||
// While the "correct" answer to getting more performance out of
|
||||
// this is "use vector operations", support for that is currently
|
||||
// a work in progress/to be designed.
|
||||
//
|
||||
// Until dedicated assembly can be written leverage the fact that
|
||||
// the callers of this routine ensure that src/dst are valid.
|
||||
|
||||
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||
// util.PUT_U32_LE/util.U32_LE are not required on little-endian
|
||||
// systems that also happen to not be strict about aligned
|
||||
// memory access.
|
||||
|
||||
dst_p := transmute(^[16]u32)(&dst[0])
|
||||
if src != nil {
|
||||
src_p := transmute(^[16]u32)(&src[0])
|
||||
dst_p[0] = src_p[0] ~ x0
|
||||
dst_p[1] = src_p[1] ~ x1
|
||||
dst_p[2] = src_p[2] ~ x2
|
||||
dst_p[3] = src_p[3] ~ x3
|
||||
dst_p[4] = src_p[4] ~ x4
|
||||
dst_p[5] = src_p[5] ~ x5
|
||||
dst_p[6] = src_p[6] ~ x6
|
||||
dst_p[7] = src_p[7] ~ x7
|
||||
dst_p[8] = src_p[8] ~ x8
|
||||
dst_p[9] = src_p[9] ~ x9
|
||||
dst_p[10] = src_p[10] ~ x10
|
||||
dst_p[11] = src_p[11] ~ x11
|
||||
dst_p[12] = src_p[12] ~ x12
|
||||
dst_p[13] = src_p[13] ~ x13
|
||||
dst_p[14] = src_p[14] ~ x14
|
||||
dst_p[15] = src_p[15] ~ x15
|
||||
src = src[_BLOCK_SIZE:]
|
||||
} else {
|
||||
dst_p[0] = x0
|
||||
dst_p[1] = x1
|
||||
dst_p[2] = x2
|
||||
dst_p[3] = x3
|
||||
dst_p[4] = x4
|
||||
dst_p[5] = x5
|
||||
dst_p[6] = x6
|
||||
dst_p[7] = x7
|
||||
dst_p[8] = x8
|
||||
dst_p[9] = x9
|
||||
dst_p[10] = x10
|
||||
dst_p[11] = x11
|
||||
dst_p[12] = x12
|
||||
dst_p[13] = x13
|
||||
dst_p[14] = x14
|
||||
dst_p[15] = x15
|
||||
}
|
||||
dst = dst[_BLOCK_SIZE:]
|
||||
} else {
|
||||
#no_bounds_check {
|
||||
if src != nil {
|
||||
util.PUT_U32_LE(dst[0:4], util.U32_LE(src[0:4]) ~ x0)
|
||||
util.PUT_U32_LE(dst[4:8], util.U32_LE(src[4:8]) ~ x1)
|
||||
util.PUT_U32_LE(dst[8:12], util.U32_LE(src[8:12]) ~ x2)
|
||||
util.PUT_U32_LE(dst[12:16], util.U32_LE(src[12:16]) ~ x3)
|
||||
util.PUT_U32_LE(dst[16:20], util.U32_LE(src[16:20]) ~ x4)
|
||||
util.PUT_U32_LE(dst[20:24], util.U32_LE(src[20:24]) ~ x5)
|
||||
util.PUT_U32_LE(dst[24:28], util.U32_LE(src[24:28]) ~ x6)
|
||||
util.PUT_U32_LE(dst[28:32], util.U32_LE(src[28:32]) ~ x7)
|
||||
util.PUT_U32_LE(dst[32:36], util.U32_LE(src[32:36]) ~ x8)
|
||||
util.PUT_U32_LE(dst[36:40], util.U32_LE(src[36:40]) ~ x9)
|
||||
util.PUT_U32_LE(dst[40:44], util.U32_LE(src[40:44]) ~ x10)
|
||||
util.PUT_U32_LE(dst[44:48], util.U32_LE(src[44:48]) ~ x11)
|
||||
util.PUT_U32_LE(dst[48:52], util.U32_LE(src[48:52]) ~ x12)
|
||||
util.PUT_U32_LE(dst[52:56], util.U32_LE(src[52:56]) ~ x13)
|
||||
util.PUT_U32_LE(dst[56:60], util.U32_LE(src[56:60]) ~ x14)
|
||||
util.PUT_U32_LE(dst[60:64], util.U32_LE(src[60:64]) ~ x15)
|
||||
src = src[_BLOCK_SIZE:]
|
||||
} else {
|
||||
util.PUT_U32_LE(dst[0:4], x0)
|
||||
util.PUT_U32_LE(dst[4:8], x1)
|
||||
util.PUT_U32_LE(dst[8:12], x2)
|
||||
util.PUT_U32_LE(dst[12:16], x3)
|
||||
util.PUT_U32_LE(dst[16:20], x4)
|
||||
util.PUT_U32_LE(dst[20:24], x5)
|
||||
util.PUT_U32_LE(dst[24:28], x6)
|
||||
util.PUT_U32_LE(dst[28:32], x7)
|
||||
util.PUT_U32_LE(dst[32:36], x8)
|
||||
util.PUT_U32_LE(dst[36:40], x9)
|
||||
util.PUT_U32_LE(dst[40:44], x10)
|
||||
util.PUT_U32_LE(dst[44:48], x11)
|
||||
util.PUT_U32_LE(dst[48:52], x12)
|
||||
util.PUT_U32_LE(dst[52:56], x13)
|
||||
util.PUT_U32_LE(dst[56:60], x14)
|
||||
util.PUT_U32_LE(dst[60:64], x15)
|
||||
}
|
||||
dst = dst[_BLOCK_SIZE:]
|
||||
}
|
||||
}
|
||||
|
||||
// Increment the counter. Overflow checking is done upon
|
||||
// entry into the routine, so a 64-bit increment safely
|
||||
// covers both cases.
|
||||
new_ctr := ((u64(ctx._s[13]) << 32) | u64(ctx._s[12])) + 1
|
||||
x[12] = u32(new_ctr)
|
||||
x[13] = u32(new_ctr >> 32)
|
||||
}
|
||||
}
|
||||
|
||||
_hchacha20 :: proc (dst, key, nonce: []byte) {
|
||||
x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
|
||||
x4 := util.U32_LE(key[0:4])
|
||||
x5 := util.U32_LE(key[4:8])
|
||||
x6 := util.U32_LE(key[8:12])
|
||||
x7 := util.U32_LE(key[12:16])
|
||||
x8 := util.U32_LE(key[16:20])
|
||||
x9 := util.U32_LE(key[20:24])
|
||||
x10 := util.U32_LE(key[24:28])
|
||||
x11 := util.U32_LE(key[28:32])
|
||||
x12 := util.U32_LE(nonce[0:4])
|
||||
x13 := util.U32_LE(nonce[4:8])
|
||||
x14 := util.U32_LE(nonce[8:12])
|
||||
x15 := util.U32_LE(nonce[12:16])
|
||||
|
||||
for i := _ROUNDS; i > 0; i = i - 2 {
|
||||
// quarterround(x, 0, 4, 8, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = util.ROTL32(x12, 16)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x0 += x4
|
||||
x12 ~= x0
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x8 += x12
|
||||
x4 ~= x8
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
|
||||
// quarterround(x, 1, 5, 9, 13)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x1 += x5
|
||||
x13 ~= x1
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x9 += x13
|
||||
x5 ~= x9
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
|
||||
// quarterround(x, 2, 6, 10, 14)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x2 += x6
|
||||
x14 ~= x2
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x10 += x14
|
||||
x6 ~= x10
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
|
||||
// quarterround(x, 3, 7, 11, 15)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x3 += x7
|
||||
x15 ~= x3
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x11 += x15
|
||||
x7 ~= x11
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
|
||||
// quarterround(x, 0, 5, 10, 15)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 16)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 12)
|
||||
x0 += x5
|
||||
x15 ~= x0
|
||||
x15 = util.ROTL32(x15, 8)
|
||||
x10 += x15
|
||||
x5 ~= x10
|
||||
x5 = util.ROTL32(x5, 7)
|
||||
|
||||
// quarterround(x, 1, 6, 11, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 16)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 12)
|
||||
x1 += x6
|
||||
x12 ~= x1
|
||||
x12 = util.ROTL32(x12, 8)
|
||||
x11 += x12
|
||||
x6 ~= x11
|
||||
x6 = util.ROTL32(x6, 7)
|
||||
|
||||
// quarterround(x, 2, 7, 8, 13)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 16)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 12)
|
||||
x2 += x7
|
||||
x13 ~= x2
|
||||
x13 = util.ROTL32(x13, 8)
|
||||
x8 += x13
|
||||
x7 ~= x8
|
||||
x7 = util.ROTL32(x7, 7)
|
||||
|
||||
// quarterround(x, 3, 4, 9, 14)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 16)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 12)
|
||||
x3 += x4
|
||||
x14 ~= x3
|
||||
x14 = util.ROTL32(x14, 8)
|
||||
x9 += x14
|
||||
x4 ~= x9
|
||||
x4 = util.ROTL32(x4, 7)
|
||||
}
|
||||
|
||||
util.PUT_U32_LE(dst[0:4], x0)
|
||||
util.PUT_U32_LE(dst[4:8], x1)
|
||||
util.PUT_U32_LE(dst[8:12], x2)
|
||||
util.PUT_U32_LE(dst[12:16], x3)
|
||||
util.PUT_U32_LE(dst[16:20], x12)
|
||||
util.PUT_U32_LE(dst[20:24], x13)
|
||||
util.PUT_U32_LE(dst[24:28], x14)
|
||||
util.PUT_U32_LE(dst[28:32], x15)
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package chacha20poly1305
|
||||
|
||||
import "core:crypto"
|
||||
import "core:crypto/chacha20"
|
||||
import "core:crypto/poly1305"
|
||||
import "core:crypto/util"
|
||||
import "core:mem"
|
||||
|
||||
KEY_SIZE :: chacha20.KEY_SIZE
|
||||
NONCE_SIZE :: chacha20.NONCE_SIZE
|
||||
TAG_SIZE :: poly1305.TAG_SIZE
|
||||
|
||||
_P_MAX :: 64 * 0xffffffff // 64 * (2^32-1)
|
||||
|
||||
_validate_common_slice_sizes :: proc (tag, key, nonce, aad, text: []byte) {
|
||||
if len(tag) != TAG_SIZE {
|
||||
panic("crypto/chacha20poly1305: invalid destination tag size")
|
||||
}
|
||||
if len(key) != KEY_SIZE {
|
||||
panic("crypto/chacha20poly1305: invalid key size")
|
||||
}
|
||||
if len(nonce) != NONCE_SIZE {
|
||||
panic("crypto/chacha20poly1305: invalid nonce size")
|
||||
}
|
||||
|
||||
#assert(size_of(int) == 8 || size_of(int) <= 4)
|
||||
when size_of(int) == 8 {
|
||||
// A_MAX = 2^64 - 1 due to the length field limit.
|
||||
// P_MAX = 64 * (2^32 - 1) due to the IETF ChaCha20 counter limit.
|
||||
//
|
||||
// A_MAX is limited by size_of(int), so there is no need to
|
||||
// enforce it. P_MAX only needs to be checked on 64-bit targets,
|
||||
// for reasons that should be obvious.
|
||||
if text_len := len(text); text_len > _P_MAX {
|
||||
panic("crypto/chacha20poly1305: oversized src data")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_PAD: [16]byte
|
||||
_update_mac_pad16 :: #force_inline proc (ctx: ^poly1305.Context, x_len: int) {
|
||||
if pad_len := 16 - (x_len & (16-1)); pad_len != 16 {
|
||||
poly1305.update(ctx, _PAD[:pad_len])
|
||||
}
|
||||
}
|
||||
|
||||
encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
|
||||
_validate_common_slice_sizes(tag, key, nonce, aad, plaintext)
|
||||
if len(ciphertext) != len(plaintext) {
|
||||
panic("crypto/chacha20poly1305: invalid destination ciphertext size")
|
||||
}
|
||||
|
||||
stream_ctx: chacha20.Context = ---
|
||||
chacha20.init(&stream_ctx, key, nonce)
|
||||
|
||||
// otk = poly1305_key_gen(key, nonce)
|
||||
otk: [poly1305.KEY_SIZE]byte = ---
|
||||
chacha20.keystream_bytes(&stream_ctx, otk[:])
|
||||
mac_ctx: poly1305.Context = ---
|
||||
poly1305.init(&mac_ctx, otk[:])
|
||||
mem.zero_explicit(&otk, size_of(otk))
|
||||
|
||||
aad_len, ciphertext_len := len(aad), len(ciphertext)
|
||||
|
||||
// There is nothing preventing aad and ciphertext from overlapping
|
||||
// so auth the AAD before encrypting (slightly different from the
|
||||
// RFC, since the RFC encrypts into a new buffer).
|
||||
//
|
||||
// mac_data = aad | pad16(aad)
|
||||
poly1305.update(&mac_ctx, aad)
|
||||
_update_mac_pad16(&mac_ctx, aad_len)
|
||||
|
||||
// ciphertext = chacha20_encrypt(key, 1, nonce, plaintext)
|
||||
chacha20.seek(&stream_ctx, 1)
|
||||
chacha20.xor_bytes(&stream_ctx, ciphertext, plaintext)
|
||||
chacha20.reset(&stream_ctx) // Don't need the stream context anymore.
|
||||
|
||||
// mac_data |= ciphertext | pad16(ciphertext)
|
||||
poly1305.update(&mac_ctx, ciphertext)
|
||||
_update_mac_pad16(&mac_ctx, ciphertext_len)
|
||||
|
||||
// mac_data |= num_to_8_le_bytes(aad.length)
|
||||
// mac_data |= num_to_8_le_bytes(ciphertext.length)
|
||||
l_buf := otk[0:16] // Reuse the scratch buffer.
|
||||
util.PUT_U64_LE(l_buf[0:8], u64(aad_len))
|
||||
util.PUT_U64_LE(l_buf[8:16], u64(ciphertext_len))
|
||||
poly1305.update(&mac_ctx, l_buf)
|
||||
|
||||
// tag = poly1305_mac(mac_data, otk)
|
||||
poly1305.final(&mac_ctx, tag) // Implicitly sanitizes context.
|
||||
}
|
||||
|
||||
decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool {
|
||||
_validate_common_slice_sizes(tag, key, nonce, aad, ciphertext)
|
||||
if len(ciphertext) != len(plaintext) {
|
||||
panic("crypto/chacha20poly1305: invalid destination plaintext size")
|
||||
}
|
||||
|
||||
// Note: Unlike encrypt, this can fail early, so use defer for
|
||||
// sanitization rather than assuming control flow reaches certain
|
||||
// points where needed.
|
||||
|
||||
stream_ctx: chacha20.Context = ---
|
||||
chacha20.init(&stream_ctx, key, nonce)
|
||||
|
||||
// otk = poly1305_key_gen(key, nonce)
|
||||
otk: [poly1305.KEY_SIZE]byte = ---
|
||||
chacha20.keystream_bytes(&stream_ctx, otk[:])
|
||||
defer chacha20.reset(&stream_ctx)
|
||||
|
||||
mac_ctx: poly1305.Context = ---
|
||||
poly1305.init(&mac_ctx, otk[:])
|
||||
defer mem.zero_explicit(&otk, size_of(otk))
|
||||
|
||||
aad_len, ciphertext_len := len(aad), len(ciphertext)
|
||||
|
||||
// mac_data = aad | pad16(aad)
|
||||
// mac_data |= ciphertext | pad16(ciphertext)
|
||||
// mac_data |= num_to_8_le_bytes(aad.length)
|
||||
// mac_data |= num_to_8_le_bytes(ciphertext.length)
|
||||
poly1305.update(&mac_ctx, aad)
|
||||
_update_mac_pad16(&mac_ctx, aad_len)
|
||||
poly1305.update(&mac_ctx, ciphertext)
|
||||
_update_mac_pad16(&mac_ctx, ciphertext_len)
|
||||
l_buf := otk[0:16] // Reuse the scratch buffer.
|
||||
util.PUT_U64_LE(l_buf[0:8], u64(aad_len))
|
||||
util.PUT_U64_LE(l_buf[8:16], u64(ciphertext_len))
|
||||
poly1305.update(&mac_ctx, l_buf)
|
||||
|
||||
// tag = poly1305_mac(mac_data, otk)
|
||||
derived_tag := otk[0:poly1305.TAG_SIZE] // Reuse the scratch buffer again.
|
||||
poly1305.final(&mac_ctx, derived_tag) // Implicitly sanitizes context.
|
||||
|
||||
// Validate the tag in constant time.
|
||||
if crypto.compare_constant_time(tag, derived_tag) != 1 {
|
||||
// Zero out the plaintext, as a defense in depth measure.
|
||||
mem.zero_explicit(raw_data(plaintext), ciphertext_len)
|
||||
return false
|
||||
}
|
||||
|
||||
// plaintext = chacha20_decrypt(key, 1, nonce, ciphertext)
|
||||
chacha20.seek(&stream_ctx, 1)
|
||||
chacha20.xor_bytes(&stream_ctx, plaintext, ciphertext)
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package crypto
|
||||
|
||||
import "core:mem"
|
||||
|
||||
// compare_constant_time returns 1 iff a and b are equal, 0 otherwise.
|
||||
//
|
||||
// The execution time of this routine is constant regardless of the contents
|
||||
// of the slices being compared, as long as the length of the slices is equal.
|
||||
// If the length of the two slices is different, it will early-return 0.
|
||||
compare_constant_time :: proc "contextless" (a, b: []byte) -> int {
|
||||
// If the length of the slices is different, early return.
|
||||
//
|
||||
// This leaks the fact that the slices have a different length,
|
||||
// but the routine is primarily intended for comparing things
|
||||
// like MACS and password digests.
|
||||
n := len(a)
|
||||
if n != len(b) {
|
||||
return 0
|
||||
}
|
||||
|
||||
return compare_byte_ptrs_constant_time(raw_data(a), raw_data(b), n)
|
||||
}
|
||||
|
||||
// compare_byte_ptrs_constant_time returns 1 iff the bytes pointed to by
|
||||
// a and b are equal, 0 otherwise.
|
||||
//
|
||||
// The execution time of this routine is constant regardless of the
|
||||
// contents of the memory being compared.
|
||||
compare_byte_ptrs_constant_time :: proc "contextless" (a, b: ^byte, n: int) -> int {
|
||||
x := mem.slice_ptr(a, n)
|
||||
y := mem.slice_ptr(b, n)
|
||||
|
||||
v: byte
|
||||
for i in 0..<n {
|
||||
v |= x[i] ~ y[i]
|
||||
}
|
||||
|
||||
// After the loop, v == 0 iff a == b. The subtraction will underflow
|
||||
// iff v == 0, setting the sign-bit, which gets returned.
|
||||
return int((u32(v)-1) >> 31)
|
||||
}
|
||||
|
||||
// rand_bytes fills the dst buffer with cryptographic entropy taken from
|
||||
// the system entropy source. This routine will block if the system entropy
|
||||
// source is not ready yet. All system entropy source failures are treated
|
||||
// as catastrophic, resulting in a panic.
|
||||
rand_bytes :: proc (dst: []byte) {
|
||||
// zero-fill the buffer first
|
||||
mem.zero_explicit(raw_data(dst), len(dst))
|
||||
|
||||
_rand_bytes(dst)
|
||||
}
|
||||
+139
-214
@@ -6,7 +6,6 @@ package gost
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the GOST hashing algorithm, as defined in RFC 5831 <https://datatracker.ietf.org/doc/html/rfc5831>
|
||||
*/
|
||||
@@ -15,71 +14,77 @@ import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
_assign_hash_vtable(ctx)
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin
|
||||
ctx.hash_file_32 = hash_file_odin
|
||||
ctx.hash_stream_32 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since MD2 is not available in Botan
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_GOST)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 32
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [32]byte {
|
||||
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) -> [32]byte {
|
||||
_create_gost_ctx()
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Gost_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Gost_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_gost_ctx()
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Gost_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([32]byte, bool) {
|
||||
_create_gost_ctx()
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
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 {
|
||||
@@ -87,91 +92,85 @@ hash :: proc {
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
init :: proc "contextless" (ctx: ^Gost_Context) {
|
||||
sbox: [8][16]u32 = {
|
||||
{ 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15 },
|
||||
{ 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8 },
|
||||
{ 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13 },
|
||||
{ 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3 },
|
||||
{ 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5 },
|
||||
{ 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3 },
|
||||
{ 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11 },
|
||||
{ 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 },
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
i := 0
|
||||
for a := 0; a < 16; a += 1 {
|
||||
ax := sbox[1][a] << 15
|
||||
bx := sbox[3][a] << 23
|
||||
cx := sbox[5][a]
|
||||
cx = (cx >> 1) | (cx << 31)
|
||||
dx := sbox[7][a] << 7
|
||||
for b := 0; b < 16; b, i = b + 1, i + 1 {
|
||||
SBOX_1[i] = ax | (sbox[0][b] << 11)
|
||||
SBOX_2[i] = bx | (sbox[2][b] << 19)
|
||||
SBOX_3[i] = cx | (sbox[4][b] << 27)
|
||||
SBOX_4[i] = dx | (sbox[6][b] << 3)
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_gost_ctx :: #force_inline proc() {
|
||||
ctx: Gost_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._32
|
||||
}
|
||||
update :: proc(ctx: ^Gost_Context, data: []byte) {
|
||||
length := byte(len(data))
|
||||
j: byte
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_gost_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
init_odin(&c)
|
||||
i := ctx.partial_bytes
|
||||
for i < 32 && j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
update_odin(&c, data)
|
||||
if i < 32 {
|
||||
ctx.partial_bytes = i
|
||||
return
|
||||
}
|
||||
bytes(ctx, ctx.partial[:], 256)
|
||||
|
||||
for (j + 32) < length {
|
||||
bytes(ctx, data[j:], 256)
|
||||
j += 32
|
||||
}
|
||||
|
||||
i = 0
|
||||
for j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
ctx.partial_bytes = i
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
final :: proc(ctx: ^Gost_Context, hash: []byte) {
|
||||
if ctx.partial_bytes > 0 {
|
||||
mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
|
||||
bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
|
||||
}
|
||||
|
||||
compress(ctx.hash[:], ctx.len[:])
|
||||
compress(ctx.hash[:], ctx.sum[:])
|
||||
|
||||
for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 {
|
||||
hash[j] = byte(ctx.hash[i])
|
||||
hash[j + 1] = byte(ctx.hash[i] >> 8)
|
||||
hash[j + 2] = byte(ctx.hash[i] >> 16)
|
||||
hash[j + 3] = byte(ctx.hash[i] >> 24)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,12 +186,12 @@ Gost_Context :: struct {
|
||||
partial_bytes: byte,
|
||||
}
|
||||
|
||||
SBOX_1 : [256]u32
|
||||
SBOX_2 : [256]u32
|
||||
SBOX_3 : [256]u32
|
||||
SBOX_4 : [256]u32
|
||||
SBOX_1: [256]u32
|
||||
SBOX_2: [256]u32
|
||||
SBOX_3: [256]u32
|
||||
SBOX_4: [256]u32
|
||||
|
||||
GOST_ENCRYPT_ROUND :: #force_inline proc "contextless"(l, r, t, k1, k2: u32) -> (u32, u32, u32) {
|
||||
ENCRYPT_ROUND :: #force_inline proc "contextless" (l, r, t, k1, k2: u32) -> (u32, u32, u32) {
|
||||
l, r, t := l, r, t
|
||||
t = (k1) + r
|
||||
l ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
|
||||
@@ -201,30 +200,30 @@ GOST_ENCRYPT_ROUND :: #force_inline proc "contextless"(l, r, t, k1, k2: u32) ->
|
||||
return l, r, t
|
||||
}
|
||||
|
||||
GOST_ENCRYPT :: #force_inline proc "contextless"(a, b, c: u32, key: []u32) -> (l, r, t: u32) {
|
||||
l, r, t = GOST_ENCRYPT_ROUND(a, b, c, key[0], key[1])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[7], key[6])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[5], key[4])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[3], key[2])
|
||||
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[1], key[0])
|
||||
ENCRYPT :: #force_inline proc "contextless" (a, b, c: u32, key: []u32) -> (l, r, t: u32) {
|
||||
l, r, t = ENCRYPT_ROUND(a, b, c, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[7], key[6])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[5], key[4])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[3], key[2])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[1], key[0])
|
||||
t = r
|
||||
r = l
|
||||
l = t
|
||||
return
|
||||
}
|
||||
|
||||
gost_bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
|
||||
bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
|
||||
a, c: u32
|
||||
m: [8]u32
|
||||
|
||||
@@ -237,14 +236,14 @@ gost_bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
|
||||
c = c < a ? 1 : 0
|
||||
}
|
||||
|
||||
gost_compress(ctx.hash[:], m[:])
|
||||
compress(ctx.hash[:], m[:])
|
||||
ctx.len[0] += bits
|
||||
if ctx.len[0] < bits {
|
||||
ctx.len[1] += 1
|
||||
}
|
||||
}
|
||||
|
||||
gost_compress :: proc(h, m: []u32) {
|
||||
compress :: proc(h, m: []u32) {
|
||||
key, u, v, w, s: [8]u32
|
||||
|
||||
copy(u[:], h)
|
||||
@@ -272,7 +271,7 @@ gost_compress :: proc(h, m: []u32) {
|
||||
r := h[i]
|
||||
l := h[i + 1]
|
||||
t: u32
|
||||
l, r, t = GOST_ENCRYPT(l, r, 0, key[:])
|
||||
l, r, t = ENCRYPT(l, r, 0, key[:])
|
||||
|
||||
s[i] = r
|
||||
s[i + 1] = l
|
||||
@@ -380,78 +379,4 @@ gost_compress :: proc(h, m: []u32) {
|
||||
h[7] = v[0] ~ (v[0] >> 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~ (v[2] << 16) ~
|
||||
(v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ v[4] ~ (v[5] >> 16) ~ v[5] ~
|
||||
(v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16) ~ v[7]
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Gost_Context) {
|
||||
sbox: [8][16]u32 = {
|
||||
{ 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15 },
|
||||
{ 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8 },
|
||||
{ 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13 },
|
||||
{ 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3 },
|
||||
{ 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5 },
|
||||
{ 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3 },
|
||||
{ 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11 },
|
||||
{ 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 },
|
||||
}
|
||||
|
||||
i := 0
|
||||
for a := 0; a < 16; a += 1 {
|
||||
ax := sbox[1][a] << 15
|
||||
bx := sbox[3][a] << 23
|
||||
cx := sbox[5][a]
|
||||
cx = (cx >> 1) | (cx << 31)
|
||||
dx := sbox[7][a] << 7
|
||||
for b := 0; b < 16; b, i = b + 1, i + 1 {
|
||||
SBOX_1[i] = ax | (sbox[0][b] << 11)
|
||||
SBOX_2[i] = bx | (sbox[2][b] << 19)
|
||||
SBOX_3[i] = cx | (sbox[4][b] << 27)
|
||||
SBOX_4[i] = dx | (sbox[6][b] << 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Gost_Context, data: []byte) {
|
||||
length := byte(len(data))
|
||||
j: byte
|
||||
|
||||
i := ctx.partial_bytes
|
||||
for i < 32 && j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
|
||||
if i < 32 {
|
||||
ctx.partial_bytes = i
|
||||
return
|
||||
}
|
||||
gost_bytes(ctx, ctx.partial[:], 256)
|
||||
|
||||
for (j + 32) < length {
|
||||
gost_bytes(ctx, data[j:], 256)
|
||||
j += 32
|
||||
}
|
||||
|
||||
i = 0
|
||||
for j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
ctx.partial_bytes = i
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Gost_Context, hash: []byte) {
|
||||
if ctx.partial_bytes > 0 {
|
||||
mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
|
||||
gost_bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
|
||||
}
|
||||
|
||||
gost_compress(ctx.hash[:], ctx.len[:])
|
||||
gost_compress(ctx.hash[:], ctx.sum[:])
|
||||
|
||||
for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 {
|
||||
hash[j] = byte(ctx.hash[i])
|
||||
hash[j + 1] = byte(ctx.hash[i] >> 8)
|
||||
hash[j + 2] = byte(ctx.hash[i] >> 16)
|
||||
hash[j + 3] = byte(ctx.hash[i] >> 24)
|
||||
}
|
||||
}
|
||||
+301
-378
@@ -6,7 +6,6 @@ package groestl
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the GROESTL hashing algorithm, as defined in <http://www.groestl.info/Groestl.zip>
|
||||
*/
|
||||
@@ -14,99 +13,83 @@ package groestl
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since GROESTL is not available in Botan
|
||||
@(warning="GROESTL is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_groestl_ctx :: #force_inline proc(size: _ctx.Hash_Size) {
|
||||
ctx: Groestl_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = size
|
||||
#partial switch size {
|
||||
case ._28: ctx.hashbitlen = 224
|
||||
case ._32: ctx.hashbitlen = 256
|
||||
case ._48: ctx.hashbitlen = 384
|
||||
case ._64: ctx.hashbitlen = 512
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_224 :: 28
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_384 :: 48
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_groestl_ctx(._28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_groestl_ctx(._28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([28]byte, bool) {
|
||||
_create_groestl_ctx(._28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
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 {
|
||||
@@ -114,33 +97,78 @@ hash_224 :: proc {
|
||||
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) -> [32]byte {
|
||||
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) -> [32]byte {
|
||||
_create_groestl_ctx(._32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_groestl_ctx(._32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([32]byte, bool) {
|
||||
_create_groestl_ctx(._32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
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 {
|
||||
@@ -148,33 +176,78 @@ hash_256 :: proc {
|
||||
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) -> [48]byte {
|
||||
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) -> [48]byte {
|
||||
_create_groestl_ctx(._48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_groestl_ctx(._48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([48]byte, bool) {
|
||||
_create_groestl_ctx(._48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
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 {
|
||||
@@ -182,33 +255,78 @@ hash_384 :: proc {
|
||||
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) -> [64]byte {
|
||||
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) -> [64]byte {
|
||||
_create_groestl_ctx(._64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_groestl_ctx(._64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([64]byte, bool) {
|
||||
_create_groestl_ctx(._64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
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 {
|
||||
@@ -216,207 +334,109 @@ hash_512 :: proc {
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
init :: proc(ctx: ^Groestl_Context) {
|
||||
assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
|
||||
if ctx.hashbitlen <= 256 {
|
||||
ctx.rounds = 10
|
||||
ctx.columns = 8
|
||||
ctx.statesize = 64
|
||||
} else {
|
||||
return hash, false
|
||||
ctx.rounds = 14
|
||||
ctx.columns = 16
|
||||
ctx.statesize = 128
|
||||
}
|
||||
for i := 8 - size_of(i32); i < 8; i += 1 {
|
||||
ctx.chaining[i][ctx.columns - 1] = byte(ctx.hashbitlen >> (8 * (7 - uint(i))))
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_28(ctx, buf[:]), ok
|
||||
update :: proc(ctx: ^Groestl_Context, data: []byte) {
|
||||
databitlen := len(data) * 8
|
||||
msglen := databitlen / 8
|
||||
rem := databitlen % 8
|
||||
|
||||
i: int
|
||||
assert(ctx.bits_in_last_byte == 0)
|
||||
|
||||
if ctx.buf_ptr != 0 {
|
||||
for i = 0; ctx.buf_ptr < ctx.statesize && i < msglen; i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1 {
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
if ctx.buf_ptr < ctx.statesize {
|
||||
if rem != 0 {
|
||||
ctx.bits_in_last_byte = rem
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
|
||||
ctx.buf_ptr = 0
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
}
|
||||
|
||||
transform(ctx, data[i:], u32(msglen - i))
|
||||
i += ((msglen - i) / ctx.statesize) * ctx.statesize
|
||||
for i < msglen {
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1
|
||||
}
|
||||
|
||||
if rem != 0 {
|
||||
ctx.bits_in_last_byte = rem
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
final :: proc(ctx: ^Groestl_Context, hash: []byte) {
|
||||
hashbytelen := ctx.hashbitlen / 8
|
||||
|
||||
if ctx.bits_in_last_byte != 0 {
|
||||
ctx.buffer[ctx.buf_ptr - 1] &= ((1 << uint(ctx.bits_in_last_byte)) - 1) << (8 - uint(ctx.bits_in_last_byte))
|
||||
ctx.buffer[ctx.buf_ptr - 1] ~= 0x1 << (7 - uint(ctx.bits_in_last_byte))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
ctx.buffer[ctx.buf_ptr] = 0x80
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
|
||||
if ctx.buf_ptr > ctx.statesize - 8 {
|
||||
for ctx.buf_ptr < ctx.statesize {
|
||||
ctx.buffer[ctx.buf_ptr] = 0
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
ctx.buf_ptr = 0
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
for ctx.buf_ptr < ctx.statesize - 8 {
|
||||
ctx.buffer[ctx.buf_ptr] = 0
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
ctx.block_counter += 1
|
||||
ctx.buf_ptr = ctx.statesize
|
||||
|
||||
for ctx.buf_ptr > ctx.statesize - 8 {
|
||||
ctx.buf_ptr -= 1
|
||||
ctx.buffer[ctx.buf_ptr] = byte(ctx.block_counter)
|
||||
ctx.block_counter >>= 8
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
output_transformation(ctx)
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_groestl_ctx(ctx.hash_size)
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 {
|
||||
hash[j] = ctx.chaining[i % 8][i / 8]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,100 +651,3 @@ add_roundconstant :: proc(x: [][16]byte, columns: int, round: byte, v: Groestl_V
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Groestl_Context) {
|
||||
if ctx.hashbitlen <= 256 {
|
||||
ctx.rounds = 10
|
||||
ctx.columns = 8
|
||||
ctx.statesize = 64
|
||||
} else {
|
||||
ctx.rounds = 14
|
||||
ctx.columns = 16
|
||||
ctx.statesize = 128
|
||||
}
|
||||
for i := 8 - size_of(i32); i < 8; i += 1 {
|
||||
ctx.chaining[i][ctx.columns - 1] = byte(ctx.hashbitlen >> (8 * (7 - uint(i))))
|
||||
}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Groestl_Context, data: []byte) {
|
||||
databitlen := len(data) * 8
|
||||
msglen := databitlen / 8
|
||||
rem := databitlen % 8
|
||||
|
||||
i: int
|
||||
assert(ctx.bits_in_last_byte == 0)
|
||||
|
||||
if ctx.buf_ptr != 0 {
|
||||
for i = 0; ctx.buf_ptr < ctx.statesize && i < msglen; i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1 {
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
}
|
||||
|
||||
if ctx.buf_ptr < ctx.statesize {
|
||||
if rem != 0 {
|
||||
ctx.bits_in_last_byte = rem
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.buf_ptr = 0
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
}
|
||||
|
||||
transform(ctx, data[i:], u32(msglen - i))
|
||||
i += ((msglen - i) / ctx.statesize) * ctx.statesize
|
||||
for i < msglen {
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1
|
||||
}
|
||||
|
||||
if rem != 0 {
|
||||
ctx.bits_in_last_byte = rem
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Groestl_Context, hash: []byte) {
|
||||
hashbytelen := ctx.hashbitlen / 8
|
||||
|
||||
if ctx.bits_in_last_byte != 0 {
|
||||
ctx.buffer[ctx.buf_ptr - 1] &= ((1 << uint(ctx.bits_in_last_byte)) - 1) << (8 - uint(ctx.bits_in_last_byte))
|
||||
ctx.buffer[ctx.buf_ptr - 1] ~= 0x1 << (7 - uint(ctx.bits_in_last_byte))
|
||||
} else {
|
||||
ctx.buffer[ctx.buf_ptr] = 0x80
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
|
||||
if ctx.buf_ptr > ctx.statesize - 8 {
|
||||
for ctx.buf_ptr < ctx.statesize {
|
||||
ctx.buffer[ctx.buf_ptr] = 0
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
ctx.buf_ptr = 0
|
||||
}
|
||||
|
||||
for ctx.buf_ptr < ctx.statesize - 8 {
|
||||
ctx.buffer[ctx.buf_ptr] = 0
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
|
||||
ctx.block_counter += 1
|
||||
ctx.buf_ptr = ctx.statesize
|
||||
|
||||
for ctx.buf_ptr > ctx.statesize - 8 {
|
||||
ctx.buf_ptr -= 1
|
||||
ctx.buffer[ctx.buf_ptr] = byte(ctx.block_counter)
|
||||
ctx.block_counter >>= 8
|
||||
}
|
||||
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
output_transformation(ctx)
|
||||
|
||||
for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 {
|
||||
hash[j] = ctx.chaining[i % 8][i / 8]
|
||||
}
|
||||
}
|
||||
+997
-540
File diff suppressed because it is too large
Load Diff
+319
-396
@@ -6,7 +6,6 @@ package jh
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the JH hashing algorithm, as defined in <https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html>
|
||||
*/
|
||||
@@ -14,99 +13,83 @@ package jh
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since JH is not available in Botan
|
||||
@(warning="JH is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_jh_ctx :: #force_inline proc(size: _ctx.Hash_Size) {
|
||||
ctx: Jh_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = size
|
||||
#partial switch size {
|
||||
case ._28: ctx.hashbitlen = 224
|
||||
case ._32: ctx.hashbitlen = 256
|
||||
case ._48: ctx.hashbitlen = 384
|
||||
case ._64: ctx.hashbitlen = 512
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_224 :: 28
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_384 :: 48
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_jh_ctx(._28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_jh_ctx(._28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([28]byte, bool) {
|
||||
_create_jh_ctx(._28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
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 {
|
||||
@@ -114,33 +97,78 @@ hash_224 :: proc {
|
||||
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) -> [32]byte {
|
||||
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) -> [32]byte {
|
||||
_create_jh_ctx(._32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_jh_ctx(._32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([32]byte, bool) {
|
||||
_create_jh_ctx(._32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
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 {
|
||||
@@ -148,33 +176,78 @@ hash_256 :: proc {
|
||||
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) -> [48]byte {
|
||||
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) -> [48]byte {
|
||||
_create_jh_ctx(._48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_jh_ctx(._48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([48]byte, bool) {
|
||||
_create_jh_ctx(._48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
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 {
|
||||
@@ -182,33 +255,78 @@ hash_384 :: proc {
|
||||
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) -> [64]byte {
|
||||
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) -> [64]byte {
|
||||
_create_jh_ctx(._64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_jh_ctx(._64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Jh_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([64]byte, bool) {
|
||||
_create_jh_ctx(._64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
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 {
|
||||
@@ -216,207 +334,106 @@ hash_512 :: proc {
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: proc(ctx: ^Jh_Context) {
|
||||
assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
|
||||
ctx.H[1] = byte(ctx.hashbitlen) & 0xff
|
||||
ctx.H[0] = byte(ctx.hashbitlen >> 8) & 0xff
|
||||
F8(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
update :: proc(ctx: ^Jh_Context, data: []byte) {
|
||||
databitlen := u64(len(data)) * 8
|
||||
ctx.databitlen += databitlen
|
||||
i := u64(0)
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
if (ctx.buffer_size > 0) && ((ctx.buffer_size + databitlen) < 512) {
|
||||
if (databitlen & 7) == 0 {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
|
||||
} else {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3) + 1])
|
||||
}
|
||||
ctx.buffer_size += databitlen
|
||||
databitlen = 0
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
if (ctx.buffer_size > 0 ) && ((ctx.buffer_size + databitlen) >= 512) {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
|
||||
i = 64 - (ctx.buffer_size >> 3)
|
||||
databitlen = databitlen - (512 - ctx.buffer_size)
|
||||
F8(ctx)
|
||||
ctx.buffer_size = 0
|
||||
}
|
||||
|
||||
for databitlen >= 512 {
|
||||
copy(ctx.buffer[:], data[i:i + 64])
|
||||
F8(ctx)
|
||||
i += 64
|
||||
databitlen -= 512
|
||||
}
|
||||
|
||||
if databitlen > 0 {
|
||||
if (databitlen & 7) == 0 {
|
||||
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3)])
|
||||
} else {
|
||||
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3) + 1])
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
ctx.buffer_size = databitlen
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_28(ctx, buf[:]), ok
|
||||
final :: proc(ctx: ^Jh_Context, hash: []byte) {
|
||||
if ctx.databitlen & 0x1ff == 0 {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
ctx.buffer[0] = 0x80
|
||||
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
|
||||
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
|
||||
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
|
||||
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
|
||||
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
|
||||
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
|
||||
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
|
||||
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
|
||||
F8(ctx)
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
if ctx.buffer_size & 7 == 0 {
|
||||
for i := (ctx.databitlen & 0x1ff) >> 3; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
} else {
|
||||
for i := ((ctx.databitlen & 0x1ff) >> 3) + 1; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7))
|
||||
F8(ctx)
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
|
||||
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
|
||||
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
|
||||
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
|
||||
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
|
||||
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
|
||||
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
|
||||
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
|
||||
F8(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_jh_ctx(ctx.hash_size)
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
switch ctx.hashbitlen {
|
||||
case 224: copy(hash[:], ctx.H[100:128])
|
||||
case 256: copy(hash[:], ctx.H[96:128])
|
||||
case 384: copy(hash[:], ctx.H[80:128])
|
||||
case 512: copy(hash[:], ctx.H[64:128])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,7 +441,7 @@ _final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
JH implementation
|
||||
*/
|
||||
|
||||
JH_ROUNDCONSTANT_ZERO := [64]byte {
|
||||
ROUNDCONSTANT_ZERO := [64]byte {
|
||||
0x6, 0xa, 0x0, 0x9, 0xe, 0x6, 0x6, 0x7,
|
||||
0xf, 0x3, 0xb, 0xc, 0xc, 0x9, 0x0, 0x8,
|
||||
0xb, 0x2, 0xf, 0xb, 0x1, 0x3, 0x6, 0x6,
|
||||
@@ -435,7 +452,7 @@ JH_ROUNDCONSTANT_ZERO := [64]byte {
|
||||
0x0, 0x6, 0x6, 0x7, 0x3, 0x2, 0x2, 0xa,
|
||||
}
|
||||
|
||||
JH_S := [2][16]byte {
|
||||
SBOX := [2][16]byte {
|
||||
{9, 0, 4, 11, 13, 12, 3, 15, 1, 10, 2, 6, 7, 5, 8, 14},
|
||||
{3, 12, 6, 13, 5, 7, 1, 9, 15, 2, 0, 4, 11, 10, 14, 8},
|
||||
}
|
||||
@@ -450,7 +467,7 @@ Jh_Context :: struct {
|
||||
buffer: [64]byte,
|
||||
}
|
||||
|
||||
JH_E8_finaldegroup :: proc(ctx: ^Jh_Context) {
|
||||
E8_finaldegroup :: proc(ctx: ^Jh_Context) {
|
||||
t0,t1,t2,t3: byte
|
||||
tem: [256]byte
|
||||
for i := 0; i < 128; i += 1 {
|
||||
@@ -473,11 +490,11 @@ JH_E8_finaldegroup :: proc(ctx: ^Jh_Context) {
|
||||
}
|
||||
}
|
||||
|
||||
jh_update_roundconstant :: proc(ctx: ^Jh_Context) {
|
||||
update_roundconstant :: proc(ctx: ^Jh_Context) {
|
||||
tem: [64]byte
|
||||
t: byte
|
||||
for i := 0; i < 64; i += 1 {
|
||||
tem[i] = JH_S[0][ctx.roundconstant[i]]
|
||||
tem[i] = SBOX[0][ctx.roundconstant[i]]
|
||||
}
|
||||
for i := 0; i < 64; i += 2 {
|
||||
tem[i + 1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf
|
||||
@@ -499,14 +516,14 @@ jh_update_roundconstant :: proc(ctx: ^Jh_Context) {
|
||||
}
|
||||
}
|
||||
|
||||
JH_R8 :: proc(ctx: ^Jh_Context) {
|
||||
R8 :: proc(ctx: ^Jh_Context) {
|
||||
t: byte
|
||||
tem, roundconstant_expanded: [256]byte
|
||||
for i := u32(0); i < 256; i += 1 {
|
||||
roundconstant_expanded[i] = (ctx.roundconstant[i >> 2] >> (3 - (i & 3)) ) & 1
|
||||
}
|
||||
for i := 0; i < 256; i += 1 {
|
||||
tem[i] = JH_S[roundconstant_expanded[i]][ctx.A[i]]
|
||||
tem[i] = SBOX[roundconstant_expanded[i]][ctx.A[i]]
|
||||
}
|
||||
for i := 0; i < 256; i += 2 {
|
||||
tem[i+1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf
|
||||
@@ -528,7 +545,7 @@ JH_R8 :: proc(ctx: ^Jh_Context) {
|
||||
}
|
||||
}
|
||||
|
||||
JH_E8_initialgroup :: proc(ctx: ^Jh_Context) {
|
||||
E8_initialgroup :: proc(ctx: ^Jh_Context) {
|
||||
t0, t1, t2, t3: byte
|
||||
tem: [256]byte
|
||||
for i := u32(0); i < 256; i += 1 {
|
||||
@@ -544,118 +561,24 @@ JH_E8_initialgroup :: proc(ctx: ^Jh_Context) {
|
||||
}
|
||||
}
|
||||
|
||||
JH_E8 :: proc(ctx: ^Jh_Context) {
|
||||
E8 :: proc(ctx: ^Jh_Context) {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.roundconstant[i] = JH_ROUNDCONSTANT_ZERO[i]
|
||||
ctx.roundconstant[i] = ROUNDCONSTANT_ZERO[i]
|
||||
}
|
||||
JH_E8_initialgroup(ctx)
|
||||
E8_initialgroup(ctx)
|
||||
for i := 0; i < 42; i += 1 {
|
||||
JH_R8(ctx)
|
||||
jh_update_roundconstant(ctx)
|
||||
R8(ctx)
|
||||
update_roundconstant(ctx)
|
||||
}
|
||||
JH_E8_finaldegroup(ctx)
|
||||
E8_finaldegroup(ctx)
|
||||
}
|
||||
|
||||
JH_F8 :: proc(ctx: ^Jh_Context) {
|
||||
F8 :: proc(ctx: ^Jh_Context) {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.H[i] ~= ctx.buffer[i]
|
||||
}
|
||||
JH_E8(ctx)
|
||||
E8(ctx)
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.H[i + 64] ~= ctx.buffer[i]
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Jh_Context) {
|
||||
ctx.H[1] = byte(ctx.hashbitlen) & 0xff
|
||||
ctx.H[0] = byte(ctx.hashbitlen >> 8) & 0xff
|
||||
JH_F8(ctx)
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Jh_Context, data: []byte) {
|
||||
databitlen := u64(len(data)) * 8
|
||||
ctx.databitlen += databitlen
|
||||
i := u64(0)
|
||||
|
||||
if (ctx.buffer_size > 0) && ((ctx.buffer_size + databitlen) < 512) {
|
||||
if (databitlen & 7) == 0 {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
|
||||
} else {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3) + 1])
|
||||
}
|
||||
ctx.buffer_size += databitlen
|
||||
databitlen = 0
|
||||
}
|
||||
|
||||
if (ctx.buffer_size > 0 ) && ((ctx.buffer_size + databitlen) >= 512) {
|
||||
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
|
||||
i = 64 - (ctx.buffer_size >> 3)
|
||||
databitlen = databitlen - (512 - ctx.buffer_size)
|
||||
JH_F8(ctx)
|
||||
ctx.buffer_size = 0
|
||||
}
|
||||
|
||||
for databitlen >= 512 {
|
||||
copy(ctx.buffer[:], data[i:i + 64])
|
||||
JH_F8(ctx)
|
||||
i += 64
|
||||
databitlen -= 512
|
||||
}
|
||||
|
||||
if databitlen > 0 {
|
||||
if (databitlen & 7) == 0 {
|
||||
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3)])
|
||||
} else {
|
||||
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3) + 1])
|
||||
}
|
||||
ctx.buffer_size = databitlen
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Jh_Context, hash: []byte) {
|
||||
if ctx.databitlen & 0x1ff == 0 {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
ctx.buffer[0] = 0x80
|
||||
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
|
||||
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
|
||||
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
|
||||
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
|
||||
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
|
||||
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
|
||||
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
|
||||
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
|
||||
JH_F8(ctx)
|
||||
} else {
|
||||
if ctx.buffer_size & 7 == 0 {
|
||||
for i := (ctx.databitlen & 0x1ff) >> 3; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
} else {
|
||||
for i := ((ctx.databitlen & 0x1ff) >> 3) + 1; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
}
|
||||
ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7))
|
||||
JH_F8(ctx)
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.buffer[i] = 0
|
||||
}
|
||||
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
|
||||
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
|
||||
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
|
||||
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
|
||||
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
|
||||
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
|
||||
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
|
||||
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
|
||||
JH_F8(ctx)
|
||||
}
|
||||
switch ctx.hashbitlen {
|
||||
case 224: copy(hash[:], ctx.H[100:128])
|
||||
case 256: copy(hash[:], ctx.H[96:128])
|
||||
case 384: copy(hash[:], ctx.H[80:128])
|
||||
case 512: copy(hash[:], ctx.H[64:128])
|
||||
}
|
||||
}
|
||||
|
||||
+246
-301
@@ -6,7 +6,6 @@ package keccak
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
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.
|
||||
@@ -15,87 +14,89 @@ package keccak
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_sha3"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_KECCAK)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_224 :: 28
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_384 :: 48
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_keccak_ctx(28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_224
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_224
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.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) -> ([28]byte, bool) {
|
||||
_create_keccak_ctx(28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_224
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.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) -> ([28]byte, bool) {
|
||||
_create_keccak_ctx(28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
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 {
|
||||
@@ -103,33 +104,81 @@ hash_224 :: proc {
|
||||
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) -> [32]byte {
|
||||
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) -> [32]byte {
|
||||
_create_keccak_ctx(32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.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) -> ([32]byte, bool) {
|
||||
_create_keccak_ctx(32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.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) -> ([32]byte, bool) {
|
||||
_create_keccak_ctx(32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
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 {
|
||||
@@ -137,33 +186,81 @@ hash_256 :: proc {
|
||||
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) -> [48]byte {
|
||||
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) -> [48]byte {
|
||||
_create_keccak_ctx(48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_384
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_384
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.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) -> ([48]byte, bool) {
|
||||
_create_keccak_ctx(48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_384
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.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) -> ([48]byte, bool) {
|
||||
_create_keccak_ctx(48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
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 {
|
||||
@@ -171,33 +268,81 @@ hash_384 :: proc {
|
||||
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) -> [64]byte {
|
||||
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) -> [64]byte {
|
||||
_create_keccak_ctx(64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_512
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_512
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.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) -> ([64]byte, bool) {
|
||||
_create_keccak_ctx(64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_512
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.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) -> ([64]byte, bool) {
|
||||
_create_keccak_ctx(64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
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 {
|
||||
@@ -205,225 +350,25 @@ hash_512 :: proc {
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
Keccak_Context :: _sha3.Sha3_Context
|
||||
|
||||
init :: proc(ctx: ^_sha3.Sha3_Context) {
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
|
||||
_sha3.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_28(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_keccak_ctx :: #force_inline proc(mdlen: int) {
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = mdlen
|
||||
ctx.is_keccak = true
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch mdlen {
|
||||
case 28: _hash_impl.hash_size = ._28
|
||||
case 32: _hash_impl.hash_size = ._32
|
||||
case 48: _hash_impl.hash_size = ._48
|
||||
case 64: _hash_impl.hash_size = ._64
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28: _create_keccak_ctx(28)
|
||||
case ._32: _create_keccak_ctx(32)
|
||||
case ._48: _create_keccak_ctx(48)
|
||||
case ._64: _create_keccak_ctx(64)
|
||||
}
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.final_odin(&c, hash)
|
||||
}
|
||||
final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
|
||||
_sha3.final(ctx, hash)
|
||||
}
|
||||
|
||||
+85
-165
@@ -6,7 +6,6 @@ package md2
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the MD2 hashing algorithm, as defined in RFC 1319 <https://datatracker.ietf.org/doc/html/rfc1319>
|
||||
*/
|
||||
@@ -14,77 +13,77 @@ package md2
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin
|
||||
ctx.hash_file_16 = hash_file_odin
|
||||
ctx.hash_stream_16 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since MD2 is not available in Botan
|
||||
@(warning="MD2 is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 16
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [16]byte {
|
||||
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) -> [16]byte {
|
||||
_create_md2_ctx()
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Md2_Context
|
||||
// init(&ctx) No-op
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Md2_Context
|
||||
// init(&ctx) No-op
|
||||
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) -> ([16]byte, bool) {
|
||||
_create_md2_ctx()
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Md2_Context
|
||||
// init(&ctx) No-op
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([16]byte, bool) {
|
||||
_create_md2_ctx()
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
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 {
|
||||
@@ -92,91 +91,40 @@ hash :: proc {
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
@(warning="Init is a no-op for MD2")
|
||||
init :: proc(ctx: ^Md2_Context) {
|
||||
// No action needed here
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc(ctx: ^Md2_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if (ctx.datalen == DIGEST_SIZE) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
final :: proc(ctx: ^Md2_Context, hash: []byte) {
|
||||
to_pad := byte(DIGEST_SIZE - ctx.datalen)
|
||||
for ctx.datalen < DIGEST_SIZE {
|
||||
ctx.data[ctx.datalen] = to_pad
|
||||
ctx.datalen += 1
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_md2_ctx :: #force_inline proc() {
|
||||
ctx: Md2_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._16
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_md2_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
transform(ctx, ctx.data[:])
|
||||
transform(ctx, ctx.checksum[:])
|
||||
for i := 0; i < DIGEST_SIZE; i += 1 {
|
||||
hash[i] = ctx.state[i]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,9 +133,9 @@ _final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
*/
|
||||
|
||||
Md2_Context :: struct {
|
||||
data: [16]byte,
|
||||
state: [16 * 3]byte,
|
||||
checksum: [16]byte,
|
||||
data: [DIGEST_SIZE]byte,
|
||||
state: [DIGEST_SIZE * 3]byte,
|
||||
checksum: [DIGEST_SIZE]byte,
|
||||
datalen: int,
|
||||
}
|
||||
|
||||
@@ -214,49 +162,21 @@ PI_TABLE := [?]byte {
|
||||
|
||||
transform :: proc(ctx: ^Md2_Context, data: []byte) {
|
||||
j,k,t: byte
|
||||
for j = 0; j < 16; j += 1 {
|
||||
ctx.state[j + 16] = data[j]
|
||||
ctx.state[j + 16 * 2] = (ctx.state[j + 16] ~ ctx.state[j])
|
||||
for j = 0; j < DIGEST_SIZE; j += 1 {
|
||||
ctx.state[j + DIGEST_SIZE] = data[j]
|
||||
ctx.state[j + DIGEST_SIZE * 2] = (ctx.state[j + DIGEST_SIZE] ~ ctx.state[j])
|
||||
}
|
||||
t = 0
|
||||
for j = 0; j < 16 + 2; j += 1 {
|
||||
for k = 0; k < 16 * 3; k += 1 {
|
||||
for j = 0; j < DIGEST_SIZE + 2; j += 1 {
|
||||
for k = 0; k < DIGEST_SIZE * 3; k += 1 {
|
||||
ctx.state[k] ~= PI_TABLE[t]
|
||||
t = ctx.state[k]
|
||||
}
|
||||
t = (t + j) & 0xff
|
||||
}
|
||||
t = ctx.checksum[16 - 1]
|
||||
for j = 0; j < 16; j += 1 {
|
||||
t = ctx.checksum[DIGEST_SIZE - 1]
|
||||
for j = 0; j < DIGEST_SIZE; j += 1 {
|
||||
ctx.checksum[j] ~= PI_TABLE[data[j] ~ t]
|
||||
t = ctx.checksum[j]
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Md2_Context) {
|
||||
// No action needed here
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Md2_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if (ctx.datalen == 16) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Md2_Context, hash: []byte) {
|
||||
to_pad := byte(16 - ctx.datalen)
|
||||
for ctx.datalen < 16 {
|
||||
ctx.data[ctx.datalen] = to_pad
|
||||
ctx.datalen += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
transform(ctx, ctx.checksum[:])
|
||||
for i := 0; i < 16; i += 1 {
|
||||
hash[i] = ctx.state[i]
|
||||
}
|
||||
}
|
||||
+102
-181
@@ -16,77 +16,78 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin
|
||||
ctx.hash_file_16 = hash_file_odin
|
||||
ctx.hash_stream_16 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_MD4)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 16
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [16]byte {
|
||||
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) -> [16]byte {
|
||||
_create_md4_ctx()
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Md4_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Md4_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) -> ([16]byte, bool) {
|
||||
_create_md4_ctx()
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Md4_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([16]byte, bool) {
|
||||
_create_md4_ctx()
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
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 {
|
||||
@@ -94,91 +95,69 @@ hash :: proc {
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: proc(ctx: ^Md4_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
update :: proc(ctx: ^Md4_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if(ctx.datalen == BLOCK_SIZE) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.bitlen += 512
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_md4_ctx :: #force_inline proc() {
|
||||
ctx: Md4_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._16
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_md4_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
init_odin(&c)
|
||||
final :: proc(ctx: ^Md4_Context, hash: []byte) {
|
||||
i := ctx.datalen
|
||||
if ctx.datalen < 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < 56 {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
} else if ctx.datalen >= 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < BLOCK_SIZE {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
mem.set(&ctx.data, 0, 56)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
ctx.bitlen += u64(ctx.datalen * 8)
|
||||
ctx.data[56] = byte(ctx.bitlen)
|
||||
ctx.data[57] = byte(ctx.bitlen >> 8)
|
||||
ctx.data[58] = byte(ctx.bitlen >> 16)
|
||||
ctx.data[59] = byte(ctx.bitlen >> 24)
|
||||
ctx.data[60] = byte(ctx.bitlen >> 32)
|
||||
ctx.data[61] = byte(ctx.bitlen >> 40)
|
||||
ctx.data[62] = byte(ctx.bitlen >> 48)
|
||||
ctx.data[63] = byte(ctx.bitlen >> 56)
|
||||
transform(ctx, ctx.data[:])
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
for i = 0; i < 4; i += 1 {
|
||||
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,9 +193,9 @@ HH :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
|
||||
|
||||
transform :: proc(ctx: ^Md4_Context, data: []byte) {
|
||||
a, b, c, d, i, j: u32
|
||||
m: [16]u32
|
||||
m: [DIGEST_SIZE]u32
|
||||
|
||||
for i, j = 0, 0; i < 16; i += 1 {
|
||||
for i, j = 0, 0; i < DIGEST_SIZE; i += 1 {
|
||||
m[i] = u32(data[j]) | (u32(data[j + 1]) << 8) | (u32(data[j + 2]) << 16) | (u32(data[j + 3]) << 24)
|
||||
j += 4
|
||||
}
|
||||
@@ -282,61 +261,3 @@ transform :: proc(ctx: ^Md4_Context, data: []byte) {
|
||||
ctx.state[2] += c
|
||||
ctx.state[3] += d
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Md4_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Md4_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if(ctx.datalen == BLOCK_SIZE) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.bitlen += 512
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Md4_Context, hash: []byte) {
|
||||
i := ctx.datalen
|
||||
if ctx.datalen < 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < 56 {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
} else if ctx.datalen >= 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < BLOCK_SIZE {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
mem.set(&ctx.data, 0, 56)
|
||||
}
|
||||
|
||||
ctx.bitlen += u64(ctx.datalen * 8)
|
||||
ctx.data[56] = byte(ctx.bitlen)
|
||||
ctx.data[57] = byte(ctx.bitlen >> 8)
|
||||
ctx.data[58] = byte(ctx.bitlen >> 16)
|
||||
ctx.data[59] = byte(ctx.bitlen >> 24)
|
||||
ctx.data[60] = byte(ctx.bitlen >> 32)
|
||||
ctx.data[61] = byte(ctx.bitlen >> 40)
|
||||
ctx.data[62] = byte(ctx.bitlen >> 48)
|
||||
ctx.data[63] = byte(ctx.bitlen >> 56)
|
||||
transform(ctx, ctx.data[:])
|
||||
|
||||
for i = 0; i < 4; i += 1 {
|
||||
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
|
||||
}
|
||||
}
|
||||
|
||||
+103
-183
@@ -6,7 +6,6 @@ package md5
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the MD5 hashing algorithm, as defined in RFC 1321 <https://datatracker.ietf.org/doc/html/rfc1321>
|
||||
*/
|
||||
@@ -16,77 +15,78 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin
|
||||
ctx.hash_file_16 = hash_file_odin
|
||||
ctx.hash_stream_16 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_MD5)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 16
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [16]byte {
|
||||
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) -> [16]byte {
|
||||
_create_md5_ctx()
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Md5_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Md5_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) -> ([16]byte, bool) {
|
||||
_create_md5_ctx()
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Md5_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([16]byte, bool) {
|
||||
_create_md5_ctx()
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
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 {
|
||||
@@ -94,91 +94,71 @@ hash :: proc {
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: proc(ctx: ^Md5_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
update :: proc(ctx: ^Md5_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if(ctx.datalen == BLOCK_SIZE) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.bitlen += 512
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_md5_ctx :: #force_inline proc() {
|
||||
ctx: Md5_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._16
|
||||
}
|
||||
final :: proc(ctx: ^Md5_Context, hash: []byte){
|
||||
i : u32
|
||||
i = ctx.datalen
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_md5_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
init_odin(&c)
|
||||
if ctx.datalen < 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < 56 {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
} else if ctx.datalen >= 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < BLOCK_SIZE {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
mem.set(&ctx.data, 0, 56)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
ctx.bitlen += u64(ctx.datalen * 8)
|
||||
ctx.data[56] = byte(ctx.bitlen)
|
||||
ctx.data[57] = byte(ctx.bitlen >> 8)
|
||||
ctx.data[58] = byte(ctx.bitlen >> 16)
|
||||
ctx.data[59] = byte(ctx.bitlen >> 24)
|
||||
ctx.data[60] = byte(ctx.bitlen >> 32)
|
||||
ctx.data[61] = byte(ctx.bitlen >> 40)
|
||||
ctx.data[62] = byte(ctx.bitlen >> 48)
|
||||
ctx.data[63] = byte(ctx.bitlen >> 56)
|
||||
transform(ctx, ctx.data[:])
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
for i = 0; i < 4; i += 1 {
|
||||
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,9 +198,9 @@ II :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u
|
||||
|
||||
transform :: proc(ctx: ^Md5_Context, data: []byte) {
|
||||
i, j: u32
|
||||
m: [16]u32
|
||||
m: [DIGEST_SIZE]u32
|
||||
|
||||
for i, j = 0, 0; i < 16; i+=1 {
|
||||
for i, j = 0, 0; i < DIGEST_SIZE; i+=1 {
|
||||
m[i] = u32(data[j]) + u32(data[j + 1]) << 8 + u32(data[j + 2]) << 16 + u32(data[j + 3]) << 24
|
||||
j += 4
|
||||
}
|
||||
@@ -303,63 +283,3 @@ transform :: proc(ctx: ^Md5_Context, data: []byte) {
|
||||
ctx.state[2] += c
|
||||
ctx.state[3] += d
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Md5_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Md5_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if(ctx.datalen == BLOCK_SIZE) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.bitlen += 512
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Md5_Context, hash: []byte){
|
||||
i : u32
|
||||
i = ctx.datalen
|
||||
|
||||
if ctx.datalen < 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < 56 {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
} else if ctx.datalen >= 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < BLOCK_SIZE {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
mem.set(&ctx.data, 0, 56)
|
||||
}
|
||||
|
||||
ctx.bitlen += u64(ctx.datalen * 8)
|
||||
ctx.data[56] = byte(ctx.bitlen)
|
||||
ctx.data[57] = byte(ctx.bitlen >> 8)
|
||||
ctx.data[58] = byte(ctx.bitlen >> 16)
|
||||
ctx.data[59] = byte(ctx.bitlen >> 24)
|
||||
ctx.data[60] = byte(ctx.bitlen >> 32)
|
||||
ctx.data[61] = byte(ctx.bitlen >> 40)
|
||||
ctx.data[62] = byte(ctx.bitlen >> 48)
|
||||
ctx.data[63] = byte(ctx.bitlen >> 56)
|
||||
transform(ctx, ctx.data[:])
|
||||
|
||||
for i = 0; i < 4; i += 1 {
|
||||
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
|
||||
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package poly1305
|
||||
|
||||
import "core:crypto"
|
||||
import "core:crypto/util"
|
||||
import field "core:crypto/_fiat/field_poly1305"
|
||||
import "core:mem"
|
||||
|
||||
KEY_SIZE :: 32
|
||||
TAG_SIZE :: 16
|
||||
|
||||
_BLOCK_SIZE :: 16
|
||||
|
||||
sum :: proc (dst, msg, key: []byte) {
|
||||
ctx: Context = ---
|
||||
|
||||
init(&ctx, key)
|
||||
update(&ctx, msg)
|
||||
final(&ctx, dst)
|
||||
}
|
||||
|
||||
verify :: proc (tag, msg, key: []byte) -> bool {
|
||||
ctx: Context = ---
|
||||
derived_tag: [16]byte = ---
|
||||
|
||||
if len(tag) != TAG_SIZE {
|
||||
panic("crypto/poly1305: invalid tag size")
|
||||
}
|
||||
|
||||
init(&ctx, key)
|
||||
update(&ctx, msg)
|
||||
final(&ctx, derived_tag[:])
|
||||
|
||||
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||
}
|
||||
|
||||
Context :: struct {
|
||||
_r: field.Tight_Field_Element,
|
||||
_a: field.Tight_Field_Element,
|
||||
_s: field.Tight_Field_Element,
|
||||
|
||||
_buffer: [_BLOCK_SIZE]byte,
|
||||
_leftover: int,
|
||||
|
||||
_is_initialized: bool,
|
||||
}
|
||||
|
||||
init :: proc (ctx: ^Context, key: []byte) {
|
||||
if len(key) != KEY_SIZE {
|
||||
panic("crypto/poly1305: invalid key size")
|
||||
}
|
||||
|
||||
// r = le_bytes_to_num(key[0..15])
|
||||
// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
|
||||
tmp_lo := util.U64_LE(key[0:8]) & 0x0ffffffc0fffffff
|
||||
tmp_hi := util.U64_LE(key[8:16]) & 0xffffffc0ffffffc
|
||||
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)
|
||||
|
||||
// a = 0
|
||||
field.fe_zero(&ctx._a)
|
||||
|
||||
// No leftover in buffer
|
||||
ctx._leftover = 0
|
||||
|
||||
ctx._is_initialized = true
|
||||
}
|
||||
|
||||
update :: proc (ctx: ^Context, data: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
msg := data
|
||||
msg_len := len(data)
|
||||
|
||||
// Handle leftover
|
||||
if ctx._leftover > 0 {
|
||||
want := min(_BLOCK_SIZE - ctx._leftover, msg_len)
|
||||
copy_slice(ctx._buffer[ctx._leftover:], msg[:want])
|
||||
msg_len = msg_len - want
|
||||
msg = msg[want:]
|
||||
ctx._leftover = ctx._leftover + want
|
||||
if ctx._leftover < _BLOCK_SIZE {
|
||||
return
|
||||
}
|
||||
_blocks(ctx, ctx._buffer[:])
|
||||
ctx._leftover = 0
|
||||
}
|
||||
|
||||
// Process full blocks
|
||||
if msg_len >= _BLOCK_SIZE {
|
||||
want := msg_len & (~int(_BLOCK_SIZE - 1))
|
||||
_blocks(ctx, msg[:want])
|
||||
msg = msg[want:]
|
||||
msg_len = msg_len - want
|
||||
}
|
||||
|
||||
// Store leftover
|
||||
if msg_len > 0 {
|
||||
// TODO: While -donna does it this way, I'm fairly sure that
|
||||
// `ctx._leftover == 0` is an invariant at this point.
|
||||
copy(ctx._buffer[ctx._leftover:], msg)
|
||||
ctx._leftover = ctx._leftover + msg_len
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc (ctx: ^Context, dst: []byte) {
|
||||
assert(ctx._is_initialized)
|
||||
|
||||
if len(dst) != TAG_SIZE {
|
||||
panic("poly1305: invalid destination tag size")
|
||||
}
|
||||
|
||||
// Process remaining block
|
||||
if ctx._leftover > 0 {
|
||||
ctx._buffer[ctx._leftover] = 1
|
||||
for i := ctx._leftover + 1; i < _BLOCK_SIZE; i = i + 1 {
|
||||
ctx._buffer[i] = 0
|
||||
}
|
||||
_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)
|
||||
tmp: [32]byte = ---
|
||||
field.fe_to_bytes(&tmp, &ctx._a)
|
||||
copy_slice(dst, tmp[0:16])
|
||||
|
||||
reset(ctx)
|
||||
}
|
||||
|
||||
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))
|
||||
mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
|
||||
|
||||
ctx._is_initialized = false
|
||||
}
|
||||
|
||||
_blocks :: proc (ctx: ^Context, msg: []byte, final := false) {
|
||||
n: field.Tight_Field_Element = ---
|
||||
final_byte := byte(!final)
|
||||
|
||||
data := msg
|
||||
data_len := len(data)
|
||||
for data_len >= _BLOCK_SIZE {
|
||||
// n = le_bytes_to_num(msg[((i-1)*16)..*i*16] | [0x01])
|
||||
field.fe_from_bytes(&n, data[:_BLOCK_SIZE], final_byte, false)
|
||||
|
||||
// a += n
|
||||
field.fe_add(field.fe_relax_cast(&ctx._a), &ctx._a, &n) // _a unreduced
|
||||
|
||||
// a = (r * a) % p
|
||||
field.fe_carry_mul(&ctx._a, field.fe_relax_cast(&ctx._a), field.fe_relax_cast(&ctx._r)) // _a reduced
|
||||
|
||||
data = data[_BLOCK_SIZE:]
|
||||
data_len = data_len - _BLOCK_SIZE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package crypto
|
||||
|
||||
when ODIN_OS != .Linux && ODIN_OS != .OpenBSD && ODIN_OS != .Windows {
|
||||
_rand_bytes :: proc (dst: []byte) {
|
||||
unimplemented("crypto: rand_bytes not supported on this OS")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package crypto
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:sys/unix"
|
||||
|
||||
_MAX_PER_CALL_BYTES :: 33554431 // 2^25 - 1
|
||||
|
||||
_rand_bytes :: proc (dst: []byte) {
|
||||
dst := dst
|
||||
l := len(dst)
|
||||
|
||||
for l > 0 {
|
||||
to_read := min(l, _MAX_PER_CALL_BYTES)
|
||||
ret := unix.sys_getrandom(raw_data(dst), to_read, 0)
|
||||
if ret < 0 {
|
||||
switch os.Errno(-ret) {
|
||||
case os.EINTR:
|
||||
// Call interupted by a signal handler, just retry the
|
||||
// request.
|
||||
continue
|
||||
case os.ENOSYS:
|
||||
// The kernel is apparently prehistoric (< 3.17 circa 2014)
|
||||
// and does not support getrandom.
|
||||
panic("crypto: getrandom not available in kernel")
|
||||
case:
|
||||
// All other failures are things that should NEVER happen
|
||||
// unless the kernel interface changes (ie: the Linux
|
||||
// developers break userland).
|
||||
panic(fmt.tprintf("crypto: getrandom failed: %d", ret))
|
||||
}
|
||||
}
|
||||
|
||||
l -= ret
|
||||
dst = dst[ret:]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
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))
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package crypto
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:os"
|
||||
import "core:fmt"
|
||||
|
||||
_rand_bytes :: proc(dst: []byte) {
|
||||
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
||||
if ret != os.ERROR_NONE {
|
||||
switch ret {
|
||||
case os.ERROR_INVALID_HANDLE:
|
||||
// The handle to the first parameter is invalid.
|
||||
// This should not happen here, since we explicitly pass nil to it
|
||||
panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm")
|
||||
case os.ERROR_INVALID_PARAMETER:
|
||||
// One of the parameters was invalid
|
||||
panic("crypto: BCryptGenRandom Invalid parameter")
|
||||
case:
|
||||
// Unknown error
|
||||
panic(fmt.tprintf("crypto: BCryptGenRandom failed: %d\n", ret))
|
||||
}
|
||||
}
|
||||
}
|
||||
+317
-446
@@ -6,7 +6,6 @@ package ripemd
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation for the RIPEMD hashing algorithm as defined in <https://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
|
||||
*/
|
||||
@@ -15,86 +14,81 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin_16
|
||||
ctx.hash_file_16 = hash_file_odin_16
|
||||
ctx.hash_stream_16 = hash_stream_odin_16
|
||||
ctx.hash_bytes_20 = hash_bytes_odin_20
|
||||
ctx.hash_file_20 = hash_file_odin_20
|
||||
ctx.hash_stream_20 = hash_stream_odin_20
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_40 = hash_bytes_odin_40
|
||||
ctx.hash_file_40 = hash_file_odin_40
|
||||
ctx.hash_stream_40 = hash_stream_odin_40
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_RIPEMD_160)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_128 :: 16
|
||||
DIGEST_SIZE_160 :: 20
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_320 :: 40
|
||||
|
||||
// hash_string_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_128 :: proc(data: string) -> [16]byte {
|
||||
hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
|
||||
return hash_bytes_128(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
|
||||
_create_ripemd_ctx(16)
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
|
||||
hash: [DIGEST_SIZE_128]byte
|
||||
ctx: Ripemd128_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Ripemd128_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_128 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_ripemd_ctx(16)
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
|
||||
hash: [DIGEST_SIZE_128]byte
|
||||
ctx: Ripemd128_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// 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) -> ([16]byte, bool) {
|
||||
_create_ripemd_ctx(16)
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
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
|
||||
}
|
||||
|
||||
hash_128 :: proc {
|
||||
@@ -102,33 +96,75 @@ hash_128 :: proc {
|
||||
hash_file_128,
|
||||
hash_bytes_128,
|
||||
hash_string_128,
|
||||
hash_bytes_to_buffer_128,
|
||||
hash_string_to_buffer_128,
|
||||
}
|
||||
|
||||
// hash_string_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_160 :: proc(data: string) -> [20]byte {
|
||||
hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
|
||||
return hash_bytes_160(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_160 :: proc(data: []byte) -> [20]byte {
|
||||
_create_ripemd_ctx(20)
|
||||
return _hash_impl->hash_bytes_20(data)
|
||||
hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
|
||||
hash: [DIGEST_SIZE_160]byte
|
||||
ctx: Ripemd160_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_160 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_160 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_160 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_160 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Ripemd160_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_160 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
|
||||
_create_ripemd_ctx(20)
|
||||
return _hash_impl->hash_stream_20(s)
|
||||
hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
|
||||
hash: [DIGEST_SIZE_160]byte
|
||||
ctx: Ripemd160_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_160 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
_create_ripemd_ctx(20)
|
||||
return _hash_impl->hash_file_20(hd, load_at_once)
|
||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_160(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_160(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_160]byte{}, false
|
||||
}
|
||||
|
||||
hash_160 :: proc {
|
||||
@@ -136,33 +172,75 @@ hash_160 :: proc {
|
||||
hash_file_160,
|
||||
hash_bytes_160,
|
||||
hash_string_160,
|
||||
hash_bytes_to_buffer_160,
|
||||
hash_string_to_buffer_160,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
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) -> [32]byte {
|
||||
_create_ripemd_ctx(32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Ripemd256_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Ripemd256_Context
|
||||
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) -> ([32]byte, bool) {
|
||||
_create_ripemd_ctx(32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Ripemd256_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([32]byte, bool) {
|
||||
_create_ripemd_ctx(32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
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 {
|
||||
@@ -170,33 +248,75 @@ hash_256 :: proc {
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
hash_bytes_to_buffer_256,
|
||||
hash_string_to_buffer_256,
|
||||
}
|
||||
|
||||
// hash_string_320 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_320 :: proc(data: string) -> [40]byte {
|
||||
hash_string_320 :: proc(data: string) -> [DIGEST_SIZE_320]byte {
|
||||
return hash_bytes_320(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_320 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_320 :: proc(data: []byte) -> [40]byte {
|
||||
_create_ripemd_ctx(40)
|
||||
return _hash_impl->hash_bytes_40(data)
|
||||
hash_bytes_320 :: proc(data: []byte) -> [DIGEST_SIZE_320]byte {
|
||||
hash: [DIGEST_SIZE_320]byte
|
||||
ctx: Ripemd320_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_320 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_320 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_320(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_320 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_320 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_320, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Ripemd320_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_320 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_320 :: proc(s: io.Stream) -> ([40]byte, bool) {
|
||||
_create_ripemd_ctx(40)
|
||||
return _hash_impl->hash_stream_40(s)
|
||||
hash_stream_320 :: proc(s: io.Stream) -> ([DIGEST_SIZE_320]byte, bool) {
|
||||
hash: [DIGEST_SIZE_320]byte
|
||||
ctx: Ripemd320_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_320 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([40]byte, bool) {
|
||||
_create_ripemd_ctx(40)
|
||||
return _hash_impl->hash_file_40(hd, load_at_once)
|
||||
hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_320]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_320(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_320(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_320]byte{}, false
|
||||
}
|
||||
|
||||
hash_320 :: proc {
|
||||
@@ -204,263 +324,126 @@ hash_320 :: proc {
|
||||
hash_file_320,
|
||||
hash_bytes_320,
|
||||
hash_string_320,
|
||||
hash_bytes_to_buffer_320,
|
||||
hash_string_to_buffer_320,
|
||||
}
|
||||
|
||||
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^$T) {
|
||||
when T == Ripemd128_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
|
||||
} else when T == Ripemd160_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
|
||||
} else when T == Ripemd256_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
|
||||
ctx.s[4], ctx.s[5], ctx.s[6], ctx.s[7] = S5, S6, S7, S8
|
||||
} else when T == Ripemd320_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
|
||||
ctx.s[5], ctx.s[6], ctx.s[7], ctx.s[8], ctx.s[9] = S5, S6, S7, S8, S9
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
update :: proc(ctx: ^$T, data: []byte) {
|
||||
ctx.tc += u64(len(data))
|
||||
data := data
|
||||
if ctx.nx > 0 {
|
||||
n := len(data)
|
||||
|
||||
when T == Ripemd128_Context {
|
||||
if n > RIPEMD_128_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_128_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
} else when T == Ripemd160_Context {
|
||||
if n > RIPEMD_160_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_160_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
} else when T == Ripemd256_Context{
|
||||
if n > RIPEMD_256_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_256_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
} else when T == Ripemd320_Context{
|
||||
if n > RIPEMD_320_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_320_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_16(ctx, buf[:]), ok
|
||||
for i := 0; i < n; i += 1 {
|
||||
ctx.x[ctx.nx + i] = data[i]
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
ctx.nx += n
|
||||
when T == Ripemd128_Context {
|
||||
if ctx.nx == RIPEMD_128_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
} else when T == Ripemd160_Context {
|
||||
if ctx.nx == RIPEMD_160_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
} else when T == Ripemd256_Context{
|
||||
if ctx.nx == RIPEMD_256_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
} else when T == Ripemd320_Context{
|
||||
if ctx.nx == RIPEMD_320_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
data = data[n:]
|
||||
}
|
||||
n := block(ctx, data)
|
||||
data = data[n:]
|
||||
if len(data) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], data)
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^$T, hash: []byte) {
|
||||
d := ctx
|
||||
tc := d.tc
|
||||
tmp: [64]byte
|
||||
tmp[0] = 0x80
|
||||
|
||||
if tc % 64 < 56 {
|
||||
update(d, tmp[0:56 - tc % 64])
|
||||
} else {
|
||||
return hash, false
|
||||
update(d, tmp[0:64 + 56 - tc % 64])
|
||||
}
|
||||
|
||||
tc <<= 3
|
||||
for i : u32 = 0; i < 8; i += 1 {
|
||||
tmp[i] = byte(tc >> (8 * i))
|
||||
}
|
||||
|
||||
update(d, tmp[0:8])
|
||||
|
||||
when T == Ripemd128_Context {
|
||||
size :: RIPEMD_128_SIZE
|
||||
} else when T == Ripemd160_Context {
|
||||
size :: RIPEMD_160_SIZE
|
||||
} else when T == Ripemd256_Context{
|
||||
size :: RIPEMD_256_SIZE
|
||||
} else when T == Ripemd320_Context{
|
||||
size :: RIPEMD_320_SIZE
|
||||
}
|
||||
|
||||
digest: [size]byte
|
||||
for s, i in d.s {
|
||||
digest[i * 4] = byte(s)
|
||||
digest[i * 4 + 1] = byte(s >> 8)
|
||||
digest[i * 4 + 2] = byte(s >> 16)
|
||||
digest[i * 4 + 3] = byte(s >> 24)
|
||||
}
|
||||
copy(hash[:], digest[:])
|
||||
}
|
||||
|
||||
hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_20(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [20]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [40]byte {
|
||||
hash: [40]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([40]byte, bool) {
|
||||
hash: [40]byte
|
||||
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([40]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_40(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_40(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [40]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_ripemd_ctx :: #force_inline proc(hash_size: int) {
|
||||
switch hash_size {
|
||||
case 16:
|
||||
ctx: Ripemd128_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._16
|
||||
case 20:
|
||||
ctx: Ripemd160_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._20
|
||||
case 32:
|
||||
ctx: Ripemd256_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._32
|
||||
case 40:
|
||||
ctx: Ripemd320_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._40
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16:
|
||||
_create_ripemd_ctx(16)
|
||||
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
case ._20:
|
||||
_create_ripemd_ctx(20)
|
||||
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
case ._32:
|
||||
_create_ripemd_ctx(32)
|
||||
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
case ._40:
|
||||
_create_ripemd_ctx(40)
|
||||
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
case ._20:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
case ._32:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
case ._40:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
case ._20:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
case ._32:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
case ._40:
|
||||
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
RIPEMD implementation
|
||||
@@ -574,20 +557,6 @@ RIPEMD_160_R1 := [80]uint {
|
||||
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11,
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
when T == Ripemd128_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
|
||||
} else when T == Ripemd160_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
|
||||
} else when T == Ripemd256_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
|
||||
ctx.s[4], ctx.s[5], ctx.s[6], ctx.s[7] = S5, S6, S7, S8
|
||||
} else when T == Ripemd320_Context {
|
||||
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
|
||||
ctx.s[5], ctx.s[6], ctx.s[7], ctx.s[8], ctx.s[9] = S5, S6, S7, S8, S9
|
||||
}
|
||||
}
|
||||
|
||||
block :: #force_inline proc (ctx: ^$T, p: []byte) -> int {
|
||||
when T == Ripemd128_Context {
|
||||
return ripemd_128_block(ctx, p)
|
||||
@@ -948,101 +917,3 @@ ripemd_320_block :: proc(ctx: ^$T, p: []byte) -> int {
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^$T, p: []byte) {
|
||||
ctx.tc += u64(len(p))
|
||||
p := p
|
||||
if ctx.nx > 0 {
|
||||
n := len(p)
|
||||
|
||||
when T == Ripemd128_Context {
|
||||
if n > RIPEMD_128_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_128_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
} else when T == Ripemd160_Context {
|
||||
if n > RIPEMD_160_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_160_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
} else when T == Ripemd256_Context{
|
||||
if n > RIPEMD_256_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_256_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
} else when T == Ripemd320_Context{
|
||||
if n > RIPEMD_320_BLOCK_SIZE - ctx.nx {
|
||||
n = RIPEMD_320_BLOCK_SIZE - ctx.nx
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < n; i += 1 {
|
||||
ctx.x[ctx.nx + i] = p[i]
|
||||
}
|
||||
|
||||
ctx.nx += n
|
||||
when T == Ripemd128_Context {
|
||||
if ctx.nx == RIPEMD_128_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
} else when T == Ripemd160_Context {
|
||||
if ctx.nx == RIPEMD_160_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
} else when T == Ripemd256_Context{
|
||||
if ctx.nx == RIPEMD_256_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
} else when T == Ripemd320_Context{
|
||||
if ctx.nx == RIPEMD_320_BLOCK_SIZE {
|
||||
block(ctx, ctx.x[0:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
n := block(ctx, p)
|
||||
p = p[n:]
|
||||
if len(p) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], p)
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^$T, hash: []byte) {
|
||||
d := ctx
|
||||
tc := d.tc
|
||||
tmp: [64]byte
|
||||
tmp[0] = 0x80
|
||||
|
||||
if tc % 64 < 56 {
|
||||
update_odin(d, tmp[0:56 - tc % 64])
|
||||
} else {
|
||||
update_odin(d, tmp[0:64 + 56 - tc % 64])
|
||||
}
|
||||
|
||||
tc <<= 3
|
||||
for i : u32 = 0; i < 8; i += 1 {
|
||||
tmp[i] = byte(tc >> (8 * i))
|
||||
}
|
||||
|
||||
update_odin(d, tmp[0:8])
|
||||
|
||||
when T == Ripemd128_Context {
|
||||
size :: RIPEMD_128_SIZE
|
||||
} else when T == Ripemd160_Context {
|
||||
size :: RIPEMD_160_SIZE
|
||||
} else when T == Ripemd256_Context{
|
||||
size :: RIPEMD_256_SIZE
|
||||
} else when T == Ripemd320_Context{
|
||||
size :: RIPEMD_320_SIZE
|
||||
}
|
||||
|
||||
digest: [size]byte
|
||||
for s, i in d.s {
|
||||
digest[i * 4] = byte(s)
|
||||
digest[i * 4 + 1] = byte(s >> 8)
|
||||
digest[i * 4 + 2] = byte(s >> 16)
|
||||
digest[i * 4 + 3] = byte(s >> 24)
|
||||
}
|
||||
copy(hash[:], digest[:])
|
||||
}
|
||||
|
||||
+110
-190
@@ -6,7 +6,6 @@ package sha1
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SHA1 hashing algorithm, as defined in RFC 3174 <https://datatracker.ietf.org/doc/html/rfc3174>
|
||||
*/
|
||||
@@ -16,77 +15,78 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_20 = hash_bytes_odin
|
||||
ctx.hash_file_20 = hash_file_odin
|
||||
ctx.hash_stream_20 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA1)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 20
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [20]byte {
|
||||
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) -> [20]byte {
|
||||
_create_sha1_ctx()
|
||||
return _hash_impl->hash_bytes_20(data)
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Sha1_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Sha1_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) -> ([20]byte, bool) {
|
||||
_create_sha1_ctx()
|
||||
return _hash_impl->hash_stream_20(s)
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Sha1_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([20]byte, bool) {
|
||||
_create_sha1_ctx()
|
||||
return _hash_impl->hash_file_20(hd, load_at_once)
|
||||
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 {
|
||||
@@ -94,92 +94,78 @@ hash :: proc {
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: proc(ctx: ^Sha1_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
ctx.state[4] = 0xc3d2e1f0
|
||||
ctx.k[0] = 0x5a827999
|
||||
ctx.k[1] = 0x6ed9eba1
|
||||
ctx.k[2] = 0x8f1bbcdc
|
||||
ctx.k[3] = 0xca62c1d6
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc(ctx: ^Sha1_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if (ctx.datalen == BLOCK_SIZE) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.bitlen += 512
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
final :: proc(ctx: ^Sha1_Context, hash: []byte) {
|
||||
i := ctx.datalen
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
if ctx.datalen < 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < 56 {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
return [20]byte{}, false
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < BLOCK_SIZE {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
mem.set(&ctx.data, 0, 56)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sha1_ctx :: #force_inline proc() {
|
||||
ctx: Sha1_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._20
|
||||
}
|
||||
ctx.bitlen += u64(ctx.datalen * 8)
|
||||
ctx.data[63] = u8(ctx.bitlen)
|
||||
ctx.data[62] = u8(ctx.bitlen >> 8)
|
||||
ctx.data[61] = u8(ctx.bitlen >> 16)
|
||||
ctx.data[60] = u8(ctx.bitlen >> 24)
|
||||
ctx.data[59] = u8(ctx.bitlen >> 32)
|
||||
ctx.data[58] = u8(ctx.bitlen >> 40)
|
||||
ctx.data[57] = u8(ctx.bitlen >> 48)
|
||||
ctx.data[56] = u8(ctx.bitlen >> 56)
|
||||
transform(ctx, ctx.data[:])
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_sha1_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
for j: u32 = 0; j < 4; j += 1 {
|
||||
hash[j] = u8(ctx.state[0] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 4] = u8(ctx.state[1] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 8] = u8(ctx.state[2] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 12] = u8(ctx.state[3] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 16] = u8(ctx.state[4] >> (24 - j * 8)) & 0x000000ff
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -258,69 +244,3 @@ transform :: proc(ctx: ^Sha1_Context, data: []byte) {
|
||||
ctx.state[3] += d
|
||||
ctx.state[4] += e
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Sha1_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
ctx.state[4] = 0xc3d2e1f0
|
||||
ctx.k[0] = 0x5a827999
|
||||
ctx.k[1] = 0x6ed9eba1
|
||||
ctx.k[2] = 0x8f1bbcdc
|
||||
ctx.k[3] = 0xca62c1d6
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Sha1_Context, data: []byte) {
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
ctx.data[ctx.datalen] = data[i]
|
||||
ctx.datalen += 1
|
||||
if (ctx.datalen == BLOCK_SIZE) {
|
||||
transform(ctx, ctx.data[:])
|
||||
ctx.bitlen += 512
|
||||
ctx.datalen = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Sha1_Context, hash: []byte) {
|
||||
i := ctx.datalen
|
||||
|
||||
if ctx.datalen < 56 {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < 56 {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
else {
|
||||
ctx.data[i] = 0x80
|
||||
i += 1
|
||||
for i < BLOCK_SIZE {
|
||||
ctx.data[i] = 0x00
|
||||
i += 1
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
mem.set(&ctx.data, 0, 56)
|
||||
}
|
||||
|
||||
ctx.bitlen += u64(ctx.datalen * 8)
|
||||
ctx.data[63] = u8(ctx.bitlen)
|
||||
ctx.data[62] = u8(ctx.bitlen >> 8)
|
||||
ctx.data[61] = u8(ctx.bitlen >> 16)
|
||||
ctx.data[60] = u8(ctx.bitlen >> 24)
|
||||
ctx.data[59] = u8(ctx.bitlen >> 32)
|
||||
ctx.data[58] = u8(ctx.bitlen >> 40)
|
||||
ctx.data[57] = u8(ctx.bitlen >> 48)
|
||||
ctx.data[56] = u8(ctx.bitlen >> 56)
|
||||
transform(ctx, ctx.data[:])
|
||||
|
||||
for j: u32 = 0; j < 4; j += 1 {
|
||||
hash[j] = u8(ctx.state[0] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 4] = u8(ctx.state[1] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 8] = u8(ctx.state[2] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 12] = u8(ctx.state[3] >> (24 - j * 8)) & 0x000000ff
|
||||
hash[j + 16] = u8(ctx.state[4] >> (24 - j * 8)) & 0x000000ff
|
||||
}
|
||||
}
|
||||
+328
-429
@@ -6,7 +6,6 @@ package sha2
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SHA2 hashing algorithm, as defined in <https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf>
|
||||
and in RFC 3874 <https://datatracker.ietf.org/doc/html/rfc3874>
|
||||
@@ -17,102 +16,84 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA2)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sha256_ctx :: #force_inline proc(is224: bool) {
|
||||
ctx: Sha256_Context
|
||||
ctx.is224 = is224
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = is224 ? ._28 : ._32
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sha512_ctx :: #force_inline proc(is384: bool) {
|
||||
ctx: Sha512_Context
|
||||
ctx.is384 = is384
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = is384 ? ._48 : ._64
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_224 :: 28
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_384 :: 48
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_sha256_ctx(true)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Sha256_Context
|
||||
ctx.is224 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Sha256_Context
|
||||
ctx.is224 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
|
||||
_create_sha256_ctx(true)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Sha512_Context
|
||||
ctx.is384 = false
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([28]byte, bool) {
|
||||
_create_sha256_ctx(true)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
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 {
|
||||
@@ -120,33 +101,78 @@ hash_224 :: proc {
|
||||
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) -> [32]byte {
|
||||
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) -> [32]byte {
|
||||
_create_sha256_ctx(false)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Sha256_Context
|
||||
ctx.is224 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Sha256_Context
|
||||
ctx.is224 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
|
||||
_create_sha256_ctx(false)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Sha512_Context
|
||||
ctx.is384 = false
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([32]byte, bool) {
|
||||
_create_sha256_ctx(false)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
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 {
|
||||
@@ -154,33 +180,78 @@ hash_256 :: proc {
|
||||
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) -> [48]byte {
|
||||
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) -> [48]byte {
|
||||
_create_sha512_ctx(true)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Sha512_Context
|
||||
ctx.is384 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Sha512_Context
|
||||
ctx.is384 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
|
||||
_create_sha512_ctx(true)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Sha512_Context
|
||||
ctx.is384 = true
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([48]byte, bool) {
|
||||
_create_sha512_ctx(true)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
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 {
|
||||
@@ -188,33 +259,78 @@ hash_384 :: proc {
|
||||
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) -> [64]byte {
|
||||
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) -> [64]byte {
|
||||
_create_sha512_ctx(false)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Sha512_Context
|
||||
ctx.is384 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Sha512_Context
|
||||
ctx.is384 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
|
||||
_create_sha512_ctx(false)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Sha512_Context
|
||||
ctx.is384 = false
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([64]byte, bool) {
|
||||
_create_sha512_ctx(false)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
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 {
|
||||
@@ -222,231 +338,131 @@ hash_512 :: proc {
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
init :: proc(ctx: ^$T) {
|
||||
when T == Sha256_Context {
|
||||
if ctx.is224 {
|
||||
ctx.h[0] = 0xc1059ed8
|
||||
ctx.h[1] = 0x367cd507
|
||||
ctx.h[2] = 0x3070dd17
|
||||
ctx.h[3] = 0xf70e5939
|
||||
ctx.h[4] = 0xffc00b31
|
||||
ctx.h[5] = 0x68581511
|
||||
ctx.h[6] = 0x64f98fa7
|
||||
ctx.h[7] = 0xbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667
|
||||
ctx.h[1] = 0xbb67ae85
|
||||
ctx.h[2] = 0x3c6ef372
|
||||
ctx.h[3] = 0xa54ff53a
|
||||
ctx.h[4] = 0x510e527f
|
||||
ctx.h[5] = 0x9b05688c
|
||||
ctx.h[6] = 0x1f83d9ab
|
||||
ctx.h[7] = 0x5be0cd19
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_28(ctx, buf[:]), ok
|
||||
} else when T == Sha512_Context {
|
||||
if ctx.is384 {
|
||||
ctx.h[0] = 0xcbbb9d5dc1059ed8
|
||||
ctx.h[1] = 0x629a292a367cd507
|
||||
ctx.h[2] = 0x9159015a3070dd17
|
||||
ctx.h[3] = 0x152fecd8f70e5939
|
||||
ctx.h[4] = 0x67332667ffc00b31
|
||||
ctx.h[5] = 0x8eb44a8768581511
|
||||
ctx.h[6] = 0xdb0c2e0d64f98fa7
|
||||
ctx.h[7] = 0x47b5481dbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667f3bcc908
|
||||
ctx.h[1] = 0xbb67ae8584caa73b
|
||||
ctx.h[2] = 0x3c6ef372fe94f82b
|
||||
ctx.h[3] = 0xa54ff53a5f1d36f1
|
||||
ctx.h[4] = 0x510e527fade682d1
|
||||
ctx.h[5] = 0x9b05688c2b3e6c1f
|
||||
ctx.h[6] = 0x1f83d9abfb41bd6b
|
||||
ctx.h[7] = 0x5be0cd19137e2179
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
update :: proc(ctx: ^$T, data: []byte) {
|
||||
length := uint(len(data))
|
||||
block_nb: uint
|
||||
new_len, rem_len, tmp_len: uint
|
||||
shifted_message := make([]byte, length)
|
||||
|
||||
when T == Sha256_Context {
|
||||
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
|
||||
} else when T == Sha512_Context {
|
||||
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
tmp_len = CURR_BLOCK_SIZE - ctx.length
|
||||
rem_len = length < tmp_len ? length : tmp_len
|
||||
copy(ctx.block[ctx.length:], data[:rem_len])
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
if ctx.hash_size == ._28 || ctx.hash_size == ._32 {
|
||||
_create_sha256_ctx(ctx.hash_size == ._28)
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
if ctx.length + length < CURR_BLOCK_SIZE {
|
||||
ctx.length += length
|
||||
return
|
||||
}
|
||||
if ctx.hash_size == ._48 || ctx.hash_size == ._64 {
|
||||
_create_sha512_ctx(ctx.hash_size == ._48)
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
|
||||
new_len = length - rem_len
|
||||
block_nb = new_len / CURR_BLOCK_SIZE
|
||||
shifted_message = data[rem_len:]
|
||||
|
||||
sha2_transf(ctx, ctx.block[:], 1)
|
||||
sha2_transf(ctx, shifted_message, block_nb)
|
||||
|
||||
rem_len = new_len % CURR_BLOCK_SIZE
|
||||
if rem_len > 0 {
|
||||
when T == Sha256_Context {copy(ctx.block[:], shifted_message[block_nb << 6:rem_len])}
|
||||
else when T == Sha512_Context {copy(ctx.block[:], shifted_message[block_nb << 7:rem_len])}
|
||||
}
|
||||
|
||||
ctx.length = rem_len
|
||||
when T == Sha256_Context {ctx.tot_len += (block_nb + 1) << 6}
|
||||
else when T == Sha512_Context {ctx.tot_len += (block_nb + 1) << 7}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28, ._32:
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
case ._48, ._64:
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
final :: proc(ctx: ^$T, hash: []byte) {
|
||||
block_nb, pm_len, len_b: u32
|
||||
i: i32
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28, ._32:
|
||||
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
case ._48, ._64:
|
||||
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
when T == Sha256_Context {CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE}
|
||||
else when T == Sha512_Context {CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE}
|
||||
|
||||
when T == Sha256_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 9) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
|
||||
else when T == Sha512_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 17) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
|
||||
|
||||
len_b = u32(ctx.tot_len + ctx.length) << 3
|
||||
when T == Sha256_Context {pm_len = block_nb << 6}
|
||||
else when T == Sha512_Context {pm_len = block_nb << 7}
|
||||
|
||||
mem.set(rawptr(&(ctx.block[ctx.length:])[0]), 0, int(uint(pm_len) - ctx.length))
|
||||
ctx.block[ctx.length] = 0x80
|
||||
|
||||
util.PUT_U32_BE(ctx.block[pm_len - 4:], len_b)
|
||||
|
||||
sha2_transf(ctx, ctx.block[:], uint(block_nb))
|
||||
|
||||
when T == Sha256_Context {
|
||||
if ctx.is224 {
|
||||
for i = 0; i < 7; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])}
|
||||
} else {
|
||||
for i = 0; i < 8; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])}
|
||||
}
|
||||
} else when T == Sha512_Context {
|
||||
if ctx.is384 {
|
||||
for i = 0; i < 6; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])}
|
||||
} else {
|
||||
for i = 0; i < 8; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -590,50 +606,6 @@ PACK64 :: #force_inline proc "contextless"(b: []byte, x: ^u64) {
|
||||
x^ = u64(b[7]) | u64(b[6]) << 8 | u64(b[5]) << 16 | u64(b[4]) << 24 | u64(b[3]) << 32 | u64(b[2]) << 40 | u64(b[1]) << 48 | u64(b[0]) << 56
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
when T == Sha256_Context {
|
||||
if ctx.is224 {
|
||||
ctx.h[0] = 0xc1059ed8
|
||||
ctx.h[1] = 0x367cd507
|
||||
ctx.h[2] = 0x3070dd17
|
||||
ctx.h[3] = 0xf70e5939
|
||||
ctx.h[4] = 0xffc00b31
|
||||
ctx.h[5] = 0x68581511
|
||||
ctx.h[6] = 0x64f98fa7
|
||||
ctx.h[7] = 0xbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667
|
||||
ctx.h[1] = 0xbb67ae85
|
||||
ctx.h[2] = 0x3c6ef372
|
||||
ctx.h[3] = 0xa54ff53a
|
||||
ctx.h[4] = 0x510e527f
|
||||
ctx.h[5] = 0x9b05688c
|
||||
ctx.h[6] = 0x1f83d9ab
|
||||
ctx.h[7] = 0x5be0cd19
|
||||
}
|
||||
} else when T == Sha512_Context {
|
||||
if ctx.is384 {
|
||||
ctx.h[0] = 0xcbbb9d5dc1059ed8
|
||||
ctx.h[1] = 0x629a292a367cd507
|
||||
ctx.h[2] = 0x9159015a3070dd17
|
||||
ctx.h[3] = 0x152fecd8f70e5939
|
||||
ctx.h[4] = 0x67332667ffc00b31
|
||||
ctx.h[5] = 0x8eb44a8768581511
|
||||
ctx.h[6] = 0xdb0c2e0d64f98fa7
|
||||
ctx.h[7] = 0x47b5481dbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667f3bcc908
|
||||
ctx.h[1] = 0xbb67ae8584caa73b
|
||||
ctx.h[2] = 0x3c6ef372fe94f82b
|
||||
ctx.h[3] = 0xa54ff53a5f1d36f1
|
||||
ctx.h[4] = 0x510e527fade682d1
|
||||
ctx.h[5] = 0x9b05688c2b3e6c1f
|
||||
ctx.h[6] = 0x1f83d9abfb41bd6b
|
||||
ctx.h[7] = 0x5be0cd19137e2179
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sha2_transf :: proc(ctx: ^$T, data: []byte, block_nb: uint) {
|
||||
when T == Sha256_Context {
|
||||
w: [64]u32
|
||||
@@ -710,76 +682,3 @@ sha2_transf :: proc(ctx: ^$T, data: []byte, block_nb: uint) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^$T, data: []byte) {
|
||||
length := uint(len(data))
|
||||
block_nb: uint
|
||||
new_len, rem_len, tmp_len: uint
|
||||
shifted_message := make([]byte, length)
|
||||
|
||||
when T == Sha256_Context {
|
||||
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
|
||||
} else when T == Sha512_Context {
|
||||
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
|
||||
}
|
||||
|
||||
tmp_len = CURR_BLOCK_SIZE - ctx.length
|
||||
rem_len = length < tmp_len ? length : tmp_len
|
||||
copy(ctx.block[ctx.length:], data[:rem_len])
|
||||
|
||||
if ctx.length + length < CURR_BLOCK_SIZE {
|
||||
ctx.length += length
|
||||
return
|
||||
}
|
||||
|
||||
new_len = length - rem_len
|
||||
block_nb = new_len / CURR_BLOCK_SIZE
|
||||
shifted_message = data[rem_len:]
|
||||
|
||||
sha2_transf(ctx, ctx.block[:], 1)
|
||||
sha2_transf(ctx, shifted_message, block_nb)
|
||||
|
||||
rem_len = new_len % CURR_BLOCK_SIZE
|
||||
when T == Sha256_Context {copy(ctx.block[:], shifted_message[block_nb << 6:rem_len])}
|
||||
else when T == Sha512_Context {copy(ctx.block[:], shifted_message[block_nb << 7:rem_len])}
|
||||
|
||||
ctx.length = rem_len
|
||||
when T == Sha256_Context {ctx.tot_len += (block_nb + 1) << 6}
|
||||
else when T == Sha512_Context {ctx.tot_len += (block_nb + 1) << 7}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^$T, hash: []byte) {
|
||||
block_nb, pm_len, len_b: u32
|
||||
i: i32
|
||||
|
||||
when T == Sha256_Context {CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE}
|
||||
else when T == Sha512_Context {CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE}
|
||||
|
||||
when T == Sha256_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 9) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
|
||||
else when T == Sha512_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 17) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
|
||||
|
||||
len_b = u32(ctx.tot_len + ctx.length) << 3
|
||||
when T == Sha256_Context {pm_len = block_nb << 6}
|
||||
else when T == Sha512_Context {pm_len = block_nb << 7}
|
||||
|
||||
mem.set(rawptr(&(ctx.block[ctx.length:])[0]), 0, int(uint(pm_len) - ctx.length))
|
||||
ctx.block[ctx.length] = 0x80
|
||||
|
||||
util.PUT_U32_BE(ctx.block[pm_len - 4:], len_b)
|
||||
|
||||
sha2_transf(ctx, ctx.block[:], uint(block_nb))
|
||||
|
||||
when T == Sha256_Context {
|
||||
if ctx.is224 {
|
||||
for i = 0; i < 7; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])}
|
||||
} else {
|
||||
for i = 0; i < 8; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])}
|
||||
}
|
||||
} else when T == Sha512_Context {
|
||||
if ctx.is384 {
|
||||
for i = 0; i < 6; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])}
|
||||
} else {
|
||||
for i = 0; i < 8; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])}
|
||||
}
|
||||
}
|
||||
}
|
||||
+233
-301
@@ -6,7 +6,6 @@ package sha3
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
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.
|
||||
@@ -15,87 +14,85 @@ package sha3
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_sha3"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_28 = hash_bytes_odin_28
|
||||
ctx.hash_file_28 = hash_file_odin_28
|
||||
ctx.hash_stream_28 = hash_stream_odin_28
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_48 = hash_bytes_odin_48
|
||||
ctx.hash_file_48 = hash_file_odin_48
|
||||
ctx.hash_stream_48 = hash_stream_odin_48
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA3)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_224 :: 28
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_384 :: 48
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [28]byte {
|
||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
|
||||
_create_sha3_ctx(28)
|
||||
return _hash_impl->hash_bytes_28(data)
|
||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_224
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_224
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.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) -> ([28]byte, bool) {
|
||||
_create_sha3_ctx(28)
|
||||
return _hash_impl->hash_stream_28(s)
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_224
|
||||
_sha3.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.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) -> ([28]byte, bool) {
|
||||
_create_sha3_ctx(28)
|
||||
return _hash_impl->hash_file_28(hd, load_at_once)
|
||||
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 {
|
||||
@@ -103,33 +100,78 @@ hash_224 :: proc {
|
||||
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) -> [32]byte {
|
||||
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) -> [32]byte {
|
||||
_create_sha3_ctx(32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.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) -> ([32]byte, bool) {
|
||||
_create_sha3_ctx(32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
_sha3.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.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) -> ([32]byte, bool) {
|
||||
_create_sha3_ctx(32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
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 {
|
||||
@@ -137,33 +179,78 @@ hash_256 :: proc {
|
||||
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) -> [48]byte {
|
||||
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) -> [48]byte {
|
||||
_create_sha3_ctx(48)
|
||||
return _hash_impl->hash_bytes_48(data)
|
||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_384
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_384
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.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) -> ([48]byte, bool) {
|
||||
_create_sha3_ctx(48)
|
||||
return _hash_impl->hash_stream_48(s)
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_384
|
||||
_sha3.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.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) -> ([48]byte, bool) {
|
||||
_create_sha3_ctx(48)
|
||||
return _hash_impl->hash_file_48(hd, load_at_once)
|
||||
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 {
|
||||
@@ -171,33 +258,78 @@ hash_384 :: proc {
|
||||
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) -> [64]byte {
|
||||
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) -> [64]byte {
|
||||
_create_sha3_ctx(64)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_512
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_512
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.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) -> ([64]byte, bool) {
|
||||
_create_sha3_ctx(64)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_512
|
||||
_sha3.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.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) -> ([64]byte, bool) {
|
||||
_create_sha3_ctx(64)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
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 {
|
||||
@@ -205,224 +337,24 @@ hash_512 :: proc {
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
Sha3_Context :: _sha3.Sha3_Context
|
||||
|
||||
init :: proc(ctx: ^_sha3.Sha3_Context) {
|
||||
_sha3.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
|
||||
_sha3.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
|
||||
hash: [28]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_28(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [28]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
|
||||
hash: [48]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_48(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [48]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_sha3_ctx :: #force_inline proc(mdlen: int) {
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = mdlen
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch mdlen {
|
||||
case 28: _hash_impl.hash_size = ._28
|
||||
case 32: _hash_impl.hash_size = ._32
|
||||
case 48: _hash_impl.hash_size = ._48
|
||||
case 64: _hash_impl.hash_size = ._64
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._28: _create_sha3_ctx(28)
|
||||
case ._32: _create_sha3_ctx(32)
|
||||
case ._48: _create_sha3_ctx(48)
|
||||
case ._64: _create_sha3_ctx(64)
|
||||
}
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.final_odin(&c, hash)
|
||||
}
|
||||
final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
|
||||
_sha3.final(ctx, hash)
|
||||
}
|
||||
|
||||
+128
-194
@@ -6,7 +6,6 @@ package shake
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the SHAKE hashing algorithm.
|
||||
The SHA3 functionality can be found in package sha3.
|
||||
@@ -15,81 +14,86 @@ package shake
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_sha3"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin_16
|
||||
ctx.hash_file_16 = hash_file_odin_16
|
||||
ctx.hash_stream_16 = hash_stream_odin_16
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHAKE)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
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) -> [16]byte {
|
||||
hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
|
||||
return hash_bytes_128(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
|
||||
_create_shake_ctx(16)
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
|
||||
hash: [DIGEST_SIZE_128]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_128
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.shake_xof(&ctx)
|
||||
_sha3.shake_out(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_128
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.shake_xof(&ctx)
|
||||
_sha3.shake_out(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_128 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_shake_ctx(16)
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
|
||||
hash: [DIGEST_SIZE_128]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_128
|
||||
_sha3.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.shake_xof(&ctx)
|
||||
_sha3.shake_out(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// 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) -> ([16]byte, bool) {
|
||||
_create_shake_ctx(16)
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
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
|
||||
}
|
||||
|
||||
hash_128 :: proc {
|
||||
@@ -97,33 +101,81 @@ hash_128 :: proc {
|
||||
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) -> [32]byte {
|
||||
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) -> [32]byte {
|
||||
_create_shake_ctx(32)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.shake_xof(&ctx)
|
||||
_sha3.shake_out(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
_sha3.init(&ctx)
|
||||
_sha3.update(&ctx, data)
|
||||
_sha3.shake_xof(&ctx)
|
||||
_sha3.shake_out(&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) -> ([32]byte, bool) {
|
||||
_create_shake_ctx(32)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = DIGEST_SIZE_256
|
||||
_sha3.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.shake_xof(&ctx)
|
||||
_sha3.shake_out(&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) -> ([32]byte, bool) {
|
||||
_create_shake_ctx(32)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
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 {
|
||||
@@ -131,143 +183,25 @@ hash_256 :: proc {
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
hash_bytes_to_buffer_256,
|
||||
hash_string_to_buffer_256,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
Shake_Context :: _sha3.Sha3_Context
|
||||
|
||||
init :: proc(ctx: ^_sha3.Sha3_Context) {
|
||||
_sha3.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
|
||||
_sha3.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_16(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
_sha3.update_odin(&c, data)
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_sha3.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_shake_ctx :: #force_inline proc(mdlen: int) {
|
||||
ctx: _sha3.Sha3_Context
|
||||
ctx.mdlen = mdlen
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch mdlen {
|
||||
case 16: _hash_impl.hash_size = ._16
|
||||
case 32: _hash_impl.hash_size = ._32
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16: _create_shake_ctx(16)
|
||||
case ._32: _create_shake_ctx(32)
|
||||
}
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
|
||||
_sha3.shake_xof_odin(&c)
|
||||
_sha3.shake_out_odin(&c, hash[:])
|
||||
}
|
||||
final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
|
||||
_sha3.shake_xof(ctx)
|
||||
_sha3.shake_out(ctx, hash[:])
|
||||
}
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
package siphash
|
||||
|
||||
/*
|
||||
Copyright 2022 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog: Initial implementation.
|
||||
|
||||
Implementation of the SipHash hashing algorithm, as defined at <https://github.com/veorq/SipHash> and <https://www.aumasson.jp/siphash/siphash.pdf>
|
||||
|
||||
Use the specific procedures for a certain setup. The generic procdedures will default to Siphash 2-4
|
||||
*/
|
||||
|
||||
import "core:crypto"
|
||||
import "core:crypto/util"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
KEY_SIZE :: 16
|
||||
DIGEST_SIZE :: 8
|
||||
|
||||
// sum_string_1_3 will hash the given message with the key and return
|
||||
// the computed hash as a u64
|
||||
sum_string_1_3 :: proc(msg, key: string) -> u64 {
|
||||
return sum_bytes_1_3(transmute([]byte)(msg), transmute([]byte)(key))
|
||||
}
|
||||
|
||||
// sum_bytes_1_3 will hash the given message with the key and return
|
||||
// the computed hash as a u64
|
||||
sum_bytes_1_3 :: proc (msg, key: []byte) -> u64 {
|
||||
ctx: Context
|
||||
hash: u64
|
||||
init(&ctx, key, 1, 3)
|
||||
update(&ctx, msg)
|
||||
final(&ctx, &hash)
|
||||
return hash
|
||||
}
|
||||
|
||||
// sum_string_to_buffer_1_3 will hash the given message with the key and write
|
||||
// the computed hash into the provided destination buffer
|
||||
sum_string_to_buffer_1_3 :: proc(msg, key: string, dst: []byte) {
|
||||
sum_bytes_to_buffer_1_3(transmute([]byte)(msg), transmute([]byte)(key), dst)
|
||||
}
|
||||
|
||||
// sum_bytes_to_buffer_1_3 will hash the given message with the key and write
|
||||
// the computed hash into the provided destination buffer
|
||||
sum_bytes_to_buffer_1_3 :: proc(msg, key, dst: []byte) {
|
||||
assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
|
||||
hash := sum_bytes_1_3(msg, key)
|
||||
_collect_output(dst[:], hash)
|
||||
}
|
||||
|
||||
sum_1_3 :: proc {
|
||||
sum_string_1_3,
|
||||
sum_bytes_1_3,
|
||||
sum_string_to_buffer_1_3,
|
||||
sum_bytes_to_buffer_1_3,
|
||||
}
|
||||
|
||||
// verify_u64_1_3 will check if the supplied tag matches with the output you
|
||||
// will get from the provided message and key
|
||||
verify_u64_1_3 :: proc (tag: u64 msg, key: []byte) -> bool {
|
||||
return sum_bytes_1_3(msg, key) == tag
|
||||
}
|
||||
|
||||
// verify_bytes will check if the supplied tag matches with the output you
|
||||
// will get from the provided message and key
|
||||
verify_bytes_1_3 :: proc (tag, msg, key: []byte) -> bool {
|
||||
derived_tag: [8]byte
|
||||
sum_bytes_to_buffer_1_3(msg, key, derived_tag[:])
|
||||
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||
}
|
||||
|
||||
verify_1_3 :: proc {
|
||||
verify_bytes_1_3,
|
||||
verify_u64_1_3,
|
||||
}
|
||||
|
||||
// sum_string_2_4 will hash the given message with the key and return
|
||||
// the computed hash as a u64
|
||||
sum_string_2_4 :: proc(msg, key: string) -> u64 {
|
||||
return sum_bytes_2_4(transmute([]byte)(msg), transmute([]byte)(key))
|
||||
}
|
||||
|
||||
// sum_bytes_2_4 will hash the given message with the key and return
|
||||
// the computed hash as a u64
|
||||
sum_bytes_2_4 :: proc (msg, key: []byte) -> u64 {
|
||||
ctx: Context
|
||||
hash: u64
|
||||
init(&ctx, key, 2, 4)
|
||||
update(&ctx, msg)
|
||||
final(&ctx, &hash)
|
||||
return hash
|
||||
}
|
||||
|
||||
// sum_string_to_buffer_2_4 will hash the given message with the key and write
|
||||
// the computed hash into the provided destination buffer
|
||||
sum_string_to_buffer_2_4 :: proc(msg, key: string, dst: []byte) {
|
||||
sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst)
|
||||
}
|
||||
|
||||
// sum_bytes_to_buffer_2_4 will hash the given message with the key and write
|
||||
// the computed hash into the provided destination buffer
|
||||
sum_bytes_to_buffer_2_4 :: proc(msg, key, dst: []byte) {
|
||||
assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
|
||||
hash := sum_bytes_2_4(msg, key)
|
||||
_collect_output(dst[:], hash)
|
||||
}
|
||||
|
||||
sum_2_4 :: proc {
|
||||
sum_string_2_4,
|
||||
sum_bytes_2_4,
|
||||
sum_string_to_buffer_2_4,
|
||||
sum_bytes_to_buffer_2_4,
|
||||
}
|
||||
|
||||
sum_string :: sum_string_2_4
|
||||
sum_bytes :: sum_bytes_2_4
|
||||
sum_string_to_buffer :: sum_string_to_buffer_2_4
|
||||
sum_bytes_to_buffer :: sum_bytes_to_buffer_2_4
|
||||
sum :: proc {
|
||||
sum_string,
|
||||
sum_bytes,
|
||||
sum_string_to_buffer,
|
||||
sum_bytes_to_buffer,
|
||||
}
|
||||
|
||||
// verify_u64_2_4 will check if the supplied tag matches with the output you
|
||||
// will get from the provided message and key
|
||||
verify_u64_2_4 :: proc (tag: u64 msg, key: []byte) -> bool {
|
||||
return sum_bytes_2_4(msg, key) == tag
|
||||
}
|
||||
|
||||
// verify_bytes will check if the supplied tag matches with the output you
|
||||
// will get from the provided message and key
|
||||
verify_bytes_2_4 :: proc (tag, msg, key: []byte) -> bool {
|
||||
derived_tag: [8]byte
|
||||
sum_bytes_to_buffer_2_4(msg, key, derived_tag[:])
|
||||
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||
}
|
||||
|
||||
verify_2_4 :: proc {
|
||||
verify_bytes_2_4,
|
||||
verify_u64_2_4,
|
||||
}
|
||||
|
||||
verify_bytes :: verify_bytes_2_4
|
||||
verify_u64 :: verify_u64_2_4
|
||||
verify :: proc {
|
||||
verify_bytes,
|
||||
verify_u64,
|
||||
}
|
||||
|
||||
// sum_string_4_8 will hash the given message with the key and return
|
||||
// the computed hash as a u64
|
||||
sum_string_4_8 :: proc(msg, key: string) -> u64 {
|
||||
return sum_bytes_4_8(transmute([]byte)(msg), transmute([]byte)(key))
|
||||
}
|
||||
|
||||
// sum_bytes_4_8 will hash the given message with the key and return
|
||||
// the computed hash as a u64
|
||||
sum_bytes_4_8 :: proc (msg, key: []byte) -> u64 {
|
||||
ctx: Context
|
||||
hash: u64
|
||||
init(&ctx, key, 4, 8)
|
||||
update(&ctx, msg)
|
||||
final(&ctx, &hash)
|
||||
return hash
|
||||
}
|
||||
|
||||
// sum_string_to_buffer_4_8 will hash the given message with the key and write
|
||||
// the computed hash into the provided destination buffer
|
||||
sum_string_to_buffer_4_8 :: proc(msg, key: string, dst: []byte) {
|
||||
sum_bytes_to_buffer_4_8(transmute([]byte)(msg), transmute([]byte)(key), dst)
|
||||
}
|
||||
|
||||
// sum_bytes_to_buffer_4_8 will hash the given message with the key and write
|
||||
// the computed hash into the provided destination buffer
|
||||
sum_bytes_to_buffer_4_8 :: proc(msg, key, dst: []byte) {
|
||||
assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
|
||||
hash := sum_bytes_4_8(msg, key)
|
||||
_collect_output(dst[:], hash)
|
||||
}
|
||||
|
||||
sum_4_8 :: proc {
|
||||
sum_string_4_8,
|
||||
sum_bytes_4_8,
|
||||
sum_string_to_buffer_4_8,
|
||||
sum_bytes_to_buffer_4_8,
|
||||
}
|
||||
|
||||
// verify_u64_4_8 will check if the supplied tag matches with the output you
|
||||
// will get from the provided message and key
|
||||
verify_u64_4_8 :: proc (tag: u64 msg, key: []byte) -> bool {
|
||||
return sum_bytes_4_8(msg, key) == tag
|
||||
}
|
||||
|
||||
// verify_bytes will check if the supplied tag matches with the output you
|
||||
// will get from the provided message and key
|
||||
verify_bytes_4_8 :: proc (tag, msg, key: []byte) -> bool {
|
||||
derived_tag: [8]byte
|
||||
sum_bytes_to_buffer_4_8(msg, key, derived_tag[:])
|
||||
return crypto.compare_constant_time(derived_tag[:], tag) == 1
|
||||
}
|
||||
|
||||
verify_4_8 :: proc {
|
||||
verify_bytes_4_8,
|
||||
verify_u64_4_8,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
|
||||
assert(len(key) == KEY_SIZE, "crypto/siphash: Invalid key size, want 16")
|
||||
ctx.c_rounds = c_rounds
|
||||
ctx.d_rounds = d_rounds
|
||||
is_valid_setting := (ctx.c_rounds == 1 && ctx.d_rounds == 3) ||
|
||||
(ctx.c_rounds == 2 && ctx.d_rounds == 4) ||
|
||||
(ctx.c_rounds == 4 && ctx.d_rounds == 8)
|
||||
assert(is_valid_setting, "crypto/siphash: Incorrect rounds set up. Valid pairs are (1,3), (2,4) and (4,8)")
|
||||
ctx.k0 = util.U64_LE(key[:8])
|
||||
ctx.k1 = util.U64_LE(key[8:])
|
||||
ctx.v0 = 0x736f6d6570736575 ~ ctx.k0
|
||||
ctx.v1 = 0x646f72616e646f6d ~ ctx.k1
|
||||
ctx.v2 = 0x6c7967656e657261 ~ ctx.k0
|
||||
ctx.v3 = 0x7465646279746573 ~ ctx.k1
|
||||
ctx.is_initialized = true
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx.is_initialized, "crypto/siphash: Context is not initalized")
|
||||
ctx.last_block = len(data) / 8 * 8
|
||||
ctx.buf = data
|
||||
i := 0
|
||||
m: u64
|
||||
for i < ctx.last_block {
|
||||
m = u64(ctx.buf[i] & 0xff)
|
||||
i += 1
|
||||
|
||||
for r in u64(1)..<8 {
|
||||
m |= u64(ctx.buf[i] & 0xff) << (r * 8)
|
||||
i += 1
|
||||
}
|
||||
|
||||
ctx.v3 ~= m
|
||||
for _ in 0..<ctx.c_rounds {
|
||||
_compress(ctx)
|
||||
}
|
||||
|
||||
ctx.v0 ~= m
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Context, dst: ^u64) {
|
||||
m: u64
|
||||
for i := len(ctx.buf) - 1; i >= ctx.last_block; i -= 1 {
|
||||
m <<= 8
|
||||
m |= u64(ctx.buf[i] & 0xff)
|
||||
}
|
||||
m |= u64(len(ctx.buf) << 56)
|
||||
|
||||
ctx.v3 ~= m
|
||||
|
||||
for _ in 0..<ctx.c_rounds {
|
||||
_compress(ctx)
|
||||
}
|
||||
|
||||
ctx.v0 ~= m
|
||||
ctx.v2 ~= 0xff
|
||||
|
||||
for _ in 0..<ctx.d_rounds {
|
||||
_compress(ctx)
|
||||
}
|
||||
|
||||
dst^ = ctx.v0 ~ ctx.v1 ~ ctx.v2 ~ ctx.v3
|
||||
|
||||
reset(ctx)
|
||||
}
|
||||
|
||||
reset :: proc(ctx: ^Context) {
|
||||
ctx.k0, ctx.k1 = 0, 0
|
||||
ctx.v0, ctx.v1 = 0, 0
|
||||
ctx.v2, ctx.v3 = 0, 0
|
||||
ctx.last_block = 0
|
||||
ctx.c_rounds = 0
|
||||
ctx.d_rounds = 0
|
||||
ctx.is_initialized = false
|
||||
}
|
||||
|
||||
Context :: struct {
|
||||
v0, v1, v2, v3: u64, // State values
|
||||
k0, k1: u64, // Split key
|
||||
c_rounds: int, // Number of message rounds
|
||||
d_rounds: int, // Number of finalization rounds
|
||||
buf: []byte, // Provided data
|
||||
last_block: int, // Offset from the last block
|
||||
is_initialized: bool,
|
||||
}
|
||||
|
||||
_get_byte :: #force_inline proc "contextless" (byte_num: byte, into: u64) -> byte {
|
||||
return byte(into >> (((~byte_num) & (size_of(u64) - 1)) << 3))
|
||||
}
|
||||
|
||||
_collect_output :: #force_inline proc "contextless" (dst: []byte, hash: u64) {
|
||||
dst[0] = _get_byte(7, hash)
|
||||
dst[1] = _get_byte(6, hash)
|
||||
dst[2] = _get_byte(5, hash)
|
||||
dst[3] = _get_byte(4, hash)
|
||||
dst[4] = _get_byte(3, hash)
|
||||
dst[5] = _get_byte(2, hash)
|
||||
dst[6] = _get_byte(1, hash)
|
||||
dst[7] = _get_byte(0, hash)
|
||||
}
|
||||
|
||||
_compress :: #force_inline proc "contextless" (ctx: ^Context) {
|
||||
ctx.v0 += ctx.v1
|
||||
ctx.v1 = util.ROTL64(ctx.v1, 13)
|
||||
ctx.v1 ~= ctx.v0
|
||||
ctx.v0 = util.ROTL64(ctx.v0, 32)
|
||||
ctx.v2 += ctx.v3
|
||||
ctx.v3 = util.ROTL64(ctx.v3, 16)
|
||||
ctx.v3 ~= ctx.v2
|
||||
ctx.v0 += ctx.v3
|
||||
ctx.v3 = util.ROTL64(ctx.v3, 21)
|
||||
ctx.v3 ~= ctx.v0
|
||||
ctx.v2 += ctx.v1
|
||||
ctx.v1 = util.ROTL64(ctx.v1, 17)
|
||||
ctx.v1 ~= ctx.v2
|
||||
ctx.v2 = util.ROTL64(ctx.v2, 32)
|
||||
}
|
||||
@@ -1,487 +0,0 @@
|
||||
package skein
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SKEIN hashing algorithm, as defined in <https://www.schneier.com/academic/skein/>
|
||||
|
||||
This package offers the internal state sizes of 256, 512 and 1024 bits and arbitrary output size.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
ctx.is_using_odin = false
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
ctx.is_using_odin = true
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
// @note(zh): Default to SKEIN-512
|
||||
ctx.hash_bytes_slice = hash_bytes_skein512_odin
|
||||
ctx.hash_file_slice = hash_file_skein512_odin
|
||||
ctx.hash_stream_slice = hash_stream_skein512_odin
|
||||
ctx.init = _init_skein512_odin
|
||||
ctx.update = _update_skein512_odin
|
||||
ctx.final = _final_skein512_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
_hash_impl.is_using_odin = false
|
||||
// @note(zh): Botan only supports SKEIN-512.
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SKEIN_512)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
@(warning="SKEIN is not yet implemented in Odin. Botan bindings will be used")
|
||||
use_odin :: #force_inline proc() {
|
||||
// _hash_impl.is_using_odin = true
|
||||
// _assign_hash_vtable(_hash_impl)
|
||||
use_botan()
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_skein256_ctx :: #force_inline proc(size: int) {
|
||||
_hash_impl.hash_size_val = size
|
||||
if _hash_impl.is_using_odin {
|
||||
ctx: Skein256_Context
|
||||
ctx.h.bit_length = u64(size)
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_bytes_slice = hash_bytes_skein256_odin
|
||||
_hash_impl.hash_file_slice = hash_file_skein256_odin
|
||||
_hash_impl.hash_stream_slice = hash_stream_skein256_odin
|
||||
_hash_impl.init = _init_skein256_odin
|
||||
_hash_impl.update = _update_skein256_odin
|
||||
_hash_impl.final = _final_skein256_odin
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_skein512_ctx :: #force_inline proc(size: int) {
|
||||
_hash_impl.hash_size_val = size
|
||||
if _hash_impl.is_using_odin {
|
||||
ctx: Skein512_Context
|
||||
ctx.h.bit_length = u64(size)
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_bytes_slice = hash_bytes_skein512_odin
|
||||
_hash_impl.hash_file_slice = hash_file_skein512_odin
|
||||
_hash_impl.hash_stream_slice = hash_stream_skein512_odin
|
||||
_hash_impl.init = _init_skein512_odin
|
||||
_hash_impl.update = _update_skein512_odin
|
||||
_hash_impl.final = _final_skein512_odin
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_skein1024_ctx :: #force_inline proc(size: int) {
|
||||
_hash_impl.hash_size_val = size
|
||||
if _hash_impl.is_using_odin {
|
||||
ctx: Skein1024_Context
|
||||
ctx.h.bit_length = u64(size)
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_bytes_slice = hash_bytes_skein1024_odin
|
||||
_hash_impl.hash_file_slice = hash_file_skein1024_odin
|
||||
_hash_impl.hash_stream_slice = hash_stream_skein1024_odin
|
||||
_hash_impl.init = _init_skein1024_odin
|
||||
_hash_impl.update = _update_skein1024_odin
|
||||
_hash_impl.final = _final_skein1024_odin
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
// hash_skein256_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein256_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
return hash_skein256_bytes(transmute([]byte)(data), bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein256_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein256_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
_create_skein256_ctx(bit_size)
|
||||
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein256_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_skein256_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein256_ctx(bit_size)
|
||||
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein256_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_skein256_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein256_ctx(bit_size)
|
||||
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
|
||||
}
|
||||
|
||||
hash_skein256 :: proc {
|
||||
hash_skein256_stream,
|
||||
hash_skein256_file,
|
||||
hash_skein256_bytes,
|
||||
hash_skein256_string,
|
||||
}
|
||||
|
||||
// hash_skein512_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein512_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
return hash_skein512_bytes(transmute([]byte)(data), bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein512_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein512_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
_create_skein512_ctx(bit_size)
|
||||
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein512_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_skein512_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein512_ctx(bit_size)
|
||||
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein512_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_skein512_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein512_ctx(bit_size)
|
||||
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
|
||||
}
|
||||
|
||||
hash_skein512 :: proc {
|
||||
hash_skein512_stream,
|
||||
hash_skein512_file,
|
||||
hash_skein512_bytes,
|
||||
hash_skein512_string,
|
||||
}
|
||||
|
||||
// hash_skein1024_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein1024_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
return hash_skein1024_bytes(transmute([]byte)(data), bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein1024_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_skein1024_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
_create_skein1024_ctx(bit_size)
|
||||
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein1024_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_skein1024_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein1024_ctx(bit_size)
|
||||
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
|
||||
}
|
||||
|
||||
// hash_skein1024_file will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_skein1024_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
_create_skein1024_ctx(bit_size)
|
||||
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
|
||||
}
|
||||
|
||||
hash_skein1024 :: proc {
|
||||
hash_skein1024_stream,
|
||||
hash_skein1024_file,
|
||||
hash_skein1024_bytes,
|
||||
hash_skein1024_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
return hash
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
hash_stream_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_skein256_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_skein256_odin(ctx, buf[:], bit_size, allocator), ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
hash_bytes_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
return hash
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
hash_stream_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
hash_bytes_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
return hash
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
hash_stream_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
|
||||
hash := make([]byte, bit_size, allocator)
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
delete(hash)
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), ok
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_skein256_ctx(ctx.hash_size_val)
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_skein512_ctx(ctx.hash_size_val)
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_skein1024_ctx(ctx.hash_size_val)
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
SKEIN implementation
|
||||
*/
|
||||
|
||||
STATE_WORDS_256 :: 4
|
||||
STATE_WORDS_512 :: 8
|
||||
STATE_WORDS_1024 :: 16
|
||||
|
||||
STATE_BYTES_256 :: 32
|
||||
STATE_BYTES_512 :: 64
|
||||
STATE_BYTES_1024 :: 128
|
||||
|
||||
Skein_Header :: struct {
|
||||
bit_length: u64,
|
||||
bcnt: u64,
|
||||
t: [2]u64,
|
||||
}
|
||||
|
||||
Skein256_Context :: struct {
|
||||
h: Skein_Header,
|
||||
x: [STATE_WORDS_256]u64,
|
||||
b: [STATE_BYTES_256]byte,
|
||||
}
|
||||
|
||||
Skein512_Context :: struct {
|
||||
h: Skein_Header,
|
||||
x: [STATE_WORDS_512]u64,
|
||||
b: [STATE_BYTES_512]byte,
|
||||
}
|
||||
|
||||
Skein1024_Context :: struct {
|
||||
h: Skein_Header,
|
||||
x: [STATE_WORDS_1024]u64,
|
||||
b: [STATE_BYTES_1024]byte,
|
||||
}
|
||||
|
||||
|
||||
init_odin :: proc(ctx: ^$T) {
|
||||
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^$T, data: []byte) {
|
||||
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^$T, hash: []byte) {
|
||||
|
||||
}
|
||||
+104
-187
@@ -6,7 +6,6 @@ package sm3
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the SM3 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02>
|
||||
*/
|
||||
@@ -15,77 +14,78 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin
|
||||
ctx.hash_file_32 = hash_file_odin
|
||||
ctx.hash_stream_32 = hash_stream_odin
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_SM3)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 32
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [32]byte {
|
||||
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) -> [32]byte {
|
||||
_create_sm3_ctx()
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Sm3_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Sm3_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) -> ([32]byte, bool) {
|
||||
_create_sm3_ctx()
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Sm3_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([32]byte, bool) {
|
||||
_create_sm3_ctx()
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
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 {
|
||||
@@ -93,92 +93,72 @@ hash :: proc {
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: proc(ctx: ^Sm3_Context) {
|
||||
ctx.state[0] = IV[0]
|
||||
ctx.state[1] = IV[1]
|
||||
ctx.state[2] = IV[2]
|
||||
ctx.state[3] = IV[3]
|
||||
ctx.state[4] = IV[4]
|
||||
ctx.state[5] = IV[5]
|
||||
ctx.state[6] = IV[6]
|
||||
ctx.state[7] = IV[7]
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
}
|
||||
update :: proc(ctx: ^Sm3_Context, data: []byte) {
|
||||
data := data
|
||||
ctx.length += u64(len(data))
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
if ctx.bitlength > 0 {
|
||||
n := copy(ctx.x[ctx.bitlength:], data[:])
|
||||
ctx.bitlength += u64(n)
|
||||
if ctx.bitlength == 64 {
|
||||
block(ctx, ctx.x[:])
|
||||
ctx.bitlength = 0
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) >= 64 {
|
||||
n := len(data) &~ (64 - 1)
|
||||
block(ctx, data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
ctx.bitlength = u64(copy(ctx.x[:], data[:]))
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Sm3_Context, hash: []byte) {
|
||||
length := ctx.length
|
||||
|
||||
pad: [64]byte
|
||||
pad[0] = 0x80
|
||||
if length % 64 < 56 {
|
||||
update(ctx, pad[0: 56 - length % 64])
|
||||
} else {
|
||||
return hash, false
|
||||
update(ctx, pad[0: 64 + 56 - length % 64])
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
length <<= 3
|
||||
util.PUT_U64_BE(pad[:], length)
|
||||
update(ctx, pad[0: 8])
|
||||
assert(ctx.bitlength == 0)
|
||||
|
||||
@(private)
|
||||
_create_sm3_ctx :: #force_inline proc() {
|
||||
ctx: Sm3_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._32
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_sm3_ctx()
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
util.PUT_U32_BE(hash[0:], ctx.state[0])
|
||||
util.PUT_U32_BE(hash[4:], ctx.state[1])
|
||||
util.PUT_U32_BE(hash[8:], ctx.state[2])
|
||||
util.PUT_U32_BE(hash[12:], ctx.state[3])
|
||||
util.PUT_U32_BE(hash[16:], ctx.state[4])
|
||||
util.PUT_U32_BE(hash[20:], ctx.state[5])
|
||||
util.PUT_U32_BE(hash[24:], ctx.state[6])
|
||||
util.PUT_U32_BE(hash[28:], ctx.state[7])
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -192,25 +172,11 @@ Sm3_Context :: struct {
|
||||
length: u64,
|
||||
}
|
||||
|
||||
BLOCK_SIZE_IN_BYTES :: 64
|
||||
BLOCK_SIZE_IN_32 :: 16
|
||||
|
||||
IV := [8]u32 {
|
||||
0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
|
||||
0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Sm3_Context) {
|
||||
ctx.state[0] = IV[0]
|
||||
ctx.state[1] = IV[1]
|
||||
ctx.state[2] = IV[2]
|
||||
ctx.state[3] = IV[3]
|
||||
ctx.state[4] = IV[4]
|
||||
ctx.state[5] = IV[5]
|
||||
ctx.state[6] = IV[6]
|
||||
ctx.state[7] = IV[7]
|
||||
}
|
||||
|
||||
block :: proc "contextless" (ctx: ^Sm3_Context, buf: []byte) {
|
||||
buf := buf
|
||||
|
||||
@@ -282,52 +248,3 @@ block :: proc "contextless" (ctx: ^Sm3_Context, buf: []byte) {
|
||||
ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3] = state0, state1, state2, state3
|
||||
ctx.state[4], ctx.state[5], ctx.state[6], ctx.state[7] = state4, state5, state6, state7
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Sm3_Context, data: []byte) {
|
||||
data := data
|
||||
ctx.length += u64(len(data))
|
||||
|
||||
if ctx.bitlength > 0 {
|
||||
n := copy(ctx.x[ctx.bitlength:], data[:])
|
||||
ctx.bitlength += u64(n)
|
||||
if ctx.bitlength == 64 {
|
||||
block(ctx, ctx.x[:])
|
||||
ctx.bitlength = 0
|
||||
}
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) >= 64 {
|
||||
n := len(data) &~ (64 - 1)
|
||||
block(ctx, data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
ctx.bitlength = u64(copy(ctx.x[:], data[:]))
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Sm3_Context, hash: []byte) {
|
||||
length := ctx.length
|
||||
|
||||
pad: [64]byte
|
||||
pad[0] = 0x80
|
||||
if length % 64 < 56 {
|
||||
update_odin(ctx, pad[0: 56 - length % 64])
|
||||
} else {
|
||||
update_odin(ctx, pad[0: 64 + 56 - length % 64])
|
||||
}
|
||||
|
||||
length <<= 3
|
||||
util.PUT_U64_BE(pad[:], length)
|
||||
update_odin(ctx, pad[0: 8])
|
||||
assert(ctx.bitlength == 0)
|
||||
|
||||
util.PUT_U32_BE(hash[0:], ctx.state[0])
|
||||
util.PUT_U32_BE(hash[4:], ctx.state[1])
|
||||
util.PUT_U32_BE(hash[8:], ctx.state[2])
|
||||
util.PUT_U32_BE(hash[12:], ctx.state[3])
|
||||
util.PUT_U32_BE(hash[16:], ctx.state[4])
|
||||
util.PUT_U32_BE(hash[20:], ctx.state[5])
|
||||
util.PUT_U32_BE(hash[24:], ctx.state[6])
|
||||
util.PUT_U32_BE(hash[28:], ctx.state[7])
|
||||
}
|
||||
|
||||
+159
-238
@@ -6,7 +6,6 @@ package streebog
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the Streebog hashing algorithm, standardized as GOST R 34.11-2012 in RFC 6986 <https://datatracker.ietf.org/doc/html/rfc6986>
|
||||
*/
|
||||
@@ -15,88 +14,82 @@ import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_32 = hash_bytes_odin_32
|
||||
ctx.hash_file_32 = hash_file_odin_32
|
||||
ctx.hash_stream_32 = hash_stream_odin_32
|
||||
ctx.hash_bytes_64 = hash_bytes_odin_64
|
||||
ctx.hash_file_64 = hash_file_odin_64
|
||||
ctx.hash_stream_64 = hash_stream_odin_64
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_STREEBOG)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_streebog_ctx :: #force_inline proc(is256: bool) {
|
||||
ctx: Streebog_Context
|
||||
ctx.is256 = is256
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = is256 ? ._32 : ._64
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [32]byte {
|
||||
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) -> [32]byte {
|
||||
_create_streebog_ctx(true)
|
||||
return _hash_impl->hash_bytes_32(data)
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Streebog_Context
|
||||
ctx.is256 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Streebog_Context
|
||||
ctx.is256 = 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) -> ([32]byte, bool) {
|
||||
_create_streebog_ctx(true)
|
||||
return _hash_impl->hash_stream_32(s)
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Streebog_Context
|
||||
ctx.is256 = true
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([32]byte, bool) {
|
||||
_create_streebog_ctx(true)
|
||||
return _hash_impl->hash_file_32(hd, load_at_once)
|
||||
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 {
|
||||
@@ -104,33 +97,75 @@ hash_256 :: proc {
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
hash_bytes_to_buffer_256,
|
||||
hash_string_to_buffer_256,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [64]byte {
|
||||
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) -> [64]byte {
|
||||
_create_streebog_ctx(false)
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Streebog_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Streebog_Context
|
||||
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) -> ([64]byte, bool) {
|
||||
_create_streebog_ctx(false)
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Streebog_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([64]byte, bool) {
|
||||
_create_streebog_ctx(false)
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
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 {
|
||||
@@ -138,126 +173,72 @@ hash_512 :: proc {
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
init :: proc(ctx: ^Streebog_Context) {
|
||||
if ctx.is256 {
|
||||
ctx.hash_size = 256
|
||||
for _, i in ctx.h {
|
||||
ctx.h[i] = 0x01
|
||||
}
|
||||
} else {
|
||||
ctx.hash_size = 512
|
||||
}
|
||||
ctx.v_512[1] = 0x02
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc(ctx: ^Streebog_Context, data: []byte) {
|
||||
length := u64(len(data))
|
||||
chk_size: u64
|
||||
data := data
|
||||
for (length > 63) && (ctx.buf_size == 0) {
|
||||
stage2(ctx, data)
|
||||
data = data[64:]
|
||||
length -= 64
|
||||
}
|
||||
|
||||
for length != 0 {
|
||||
chk_size = 64 - ctx.buf_size
|
||||
if chk_size > length {
|
||||
chk_size = length
|
||||
}
|
||||
copy(ctx.buffer[ctx.buf_size:], data[:chk_size])
|
||||
ctx.buf_size += chk_size
|
||||
length -= chk_size
|
||||
data = data[chk_size:]
|
||||
if ctx.buf_size == 64 {
|
||||
stage2(ctx, ctx.buffer[:])
|
||||
ctx.buf_size = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
final :: proc(ctx: ^Streebog_Context, hash: []byte) {
|
||||
t: [64]byte
|
||||
t[1] = byte((ctx.buf_size * 8) >> 8) & 0xff
|
||||
t[0] = byte((ctx.buf_size) * 8) & 0xff
|
||||
|
||||
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
padding(ctx)
|
||||
|
||||
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
|
||||
hash: [32]byte
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
G(ctx.h[:], ctx.n[:], ctx.buffer[:])
|
||||
|
||||
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_32(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [32]byte{}, false
|
||||
}
|
||||
add_mod_512(ctx.n[:], t[:], ctx.n[:])
|
||||
add_mod_512(ctx.sigma[:], ctx.buffer[:], ctx.sigma[:])
|
||||
|
||||
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
init_odin(&c)
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
G(ctx.h[:], ctx.v_0[:], ctx.n[:])
|
||||
G(ctx.h[:], ctx.v_0[:], ctx.sigma[:])
|
||||
|
||||
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_64(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
_create_streebog_ctx(ctx.hash_size == ._32)
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
init_odin(&c)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
if ctx.is256 {
|
||||
copy(hash[:], ctx.h[32:])
|
||||
} else {
|
||||
copy(hash[:], ctx.h[:])
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -534,63 +515,3 @@ padding :: proc(ctx: ^Streebog_Context) {
|
||||
copy(ctx.buffer[:], t[:])
|
||||
}
|
||||
}
|
||||
|
||||
init_odin :: proc(ctx: ^Streebog_Context) {
|
||||
if ctx.is256 {
|
||||
ctx.hash_size = 256
|
||||
for _, i in ctx.h {
|
||||
ctx.h[i] = 0x01
|
||||
}
|
||||
} else {
|
||||
ctx.hash_size = 512
|
||||
}
|
||||
ctx.v_512[1] = 0x02
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Streebog_Context, data: []byte) {
|
||||
length := u64(len(data))
|
||||
chk_size: u64
|
||||
data := data
|
||||
for (length > 63) && (ctx.buf_size == 0) {
|
||||
stage2(ctx, data)
|
||||
data = data[64:]
|
||||
length -= 64
|
||||
}
|
||||
|
||||
for length != 0 {
|
||||
chk_size = 64 - ctx.buf_size
|
||||
if chk_size > length {
|
||||
chk_size = length
|
||||
}
|
||||
copy(ctx.buffer[ctx.buf_size:], data[:chk_size])
|
||||
ctx.buf_size += chk_size
|
||||
length -= chk_size
|
||||
data = data[chk_size:]
|
||||
if ctx.buf_size == 64 {
|
||||
stage2(ctx, ctx.buffer[:])
|
||||
ctx.buf_size = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Streebog_Context, hash: []byte) {
|
||||
t: [64]byte
|
||||
t[1] = byte((ctx.buf_size * 8) >> 8) & 0xff
|
||||
t[0] = byte((ctx.buf_size) * 8) & 0xff
|
||||
|
||||
padding(ctx)
|
||||
|
||||
G(ctx.h[:], ctx.n[:], ctx.buffer[:])
|
||||
|
||||
add_mod_512(ctx.n[:], t[:], ctx.n[:])
|
||||
add_mod_512(ctx.sigma[:], ctx.buffer[:], ctx.sigma[:])
|
||||
|
||||
G(ctx.h[:], ctx.v_0[:], ctx.n[:])
|
||||
G(ctx.h[:], ctx.v_0[:], ctx.sigma[:])
|
||||
|
||||
if ctx.is256 {
|
||||
copy(hash[:], ctx.h[32:])
|
||||
} else {
|
||||
copy(hash[:], ctx.h[:])
|
||||
}
|
||||
}
|
||||
+180
-231
@@ -6,7 +6,6 @@ package tiger
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the Tiger1 variant of the Tiger hashing algorithm as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
||||
*/
|
||||
@@ -14,84 +13,84 @@ package tiger
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../_tiger"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin_16
|
||||
ctx.hash_file_16 = hash_file_odin_16
|
||||
ctx.hash_stream_16 = hash_stream_odin_16
|
||||
ctx.hash_bytes_20 = hash_bytes_odin_20
|
||||
ctx.hash_file_20 = hash_file_odin_20
|
||||
ctx.hash_stream_20 = hash_stream_odin_20
|
||||
ctx.hash_bytes_24 = hash_bytes_odin_24
|
||||
ctx.hash_file_24 = hash_file_odin_24
|
||||
ctx.hash_stream_24 = hash_stream_odin_24
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_TIGER)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_128 :: 16
|
||||
DIGEST_SIZE_160 :: 20
|
||||
DIGEST_SIZE_192 :: 24
|
||||
|
||||
// hash_string_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_128 :: proc(data: string) -> [16]byte {
|
||||
hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
|
||||
return hash_bytes_128(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
|
||||
_create_tiger_ctx(16)
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
|
||||
hash: [DIGEST_SIZE_128]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 1
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 1
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_128 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_tiger_ctx(16)
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
|
||||
hash: [DIGEST_SIZE_128]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 1
|
||||
_tiger.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// 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) -> ([16]byte, bool) {
|
||||
_create_tiger_ctx(16)
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
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
|
||||
}
|
||||
|
||||
hash_128 :: proc {
|
||||
@@ -99,33 +98,78 @@ hash_128 :: proc {
|
||||
hash_file_128,
|
||||
hash_bytes_128,
|
||||
hash_string_128,
|
||||
hash_bytes_to_buffer_128,
|
||||
hash_string_to_buffer_128,
|
||||
}
|
||||
|
||||
// hash_string_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_160 :: proc(data: string) -> [20]byte {
|
||||
hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
|
||||
return hash_bytes_160(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_160 :: proc(data: []byte) -> [20]byte {
|
||||
_create_tiger_ctx(20)
|
||||
return _hash_impl->hash_bytes_20(data)
|
||||
hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
|
||||
hash: [DIGEST_SIZE_160]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 1
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_160 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_160 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_160 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_160 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 1
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_160 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
|
||||
_create_tiger_ctx(20)
|
||||
return _hash_impl->hash_stream_20(s)
|
||||
hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
|
||||
hash: [DIGEST_SIZE_160]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 1
|
||||
_tiger.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_160 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
_create_tiger_ctx(20)
|
||||
return _hash_impl->hash_file_20(hd, load_at_once)
|
||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_160(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_160(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_160]byte{}, false
|
||||
}
|
||||
|
||||
hash_160 :: proc {
|
||||
@@ -133,33 +177,78 @@ hash_160 :: proc {
|
||||
hash_file_160,
|
||||
hash_bytes_160,
|
||||
hash_string_160,
|
||||
hash_bytes_to_buffer_160,
|
||||
hash_string_to_buffer_160,
|
||||
}
|
||||
|
||||
// hash_string_192 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_192 :: proc(data: string) -> [24]byte {
|
||||
hash_string_192 :: proc(data: string) -> [DIGEST_SIZE_192]byte {
|
||||
return hash_bytes_192(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_192 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_192 :: proc(data: []byte) -> [24]byte {
|
||||
_create_tiger_ctx(24)
|
||||
return _hash_impl->hash_bytes_24(data)
|
||||
hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
|
||||
hash: [DIGEST_SIZE_192]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 1
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_192 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_192 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_192(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_192 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_192 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_192, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 1
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_192 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
|
||||
_create_tiger_ctx(24)
|
||||
return _hash_impl->hash_stream_24(s)
|
||||
hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
|
||||
hash: [DIGEST_SIZE_192]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 1
|
||||
_tiger.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_192 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
_create_tiger_ctx(24)
|
||||
return _hash_impl->hash_file_24(hd, load_at_once)
|
||||
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_192]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_192(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_192(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_192]byte{}, false
|
||||
}
|
||||
|
||||
hash_192 :: proc {
|
||||
@@ -167,165 +256,25 @@ hash_192 :: proc {
|
||||
hash_file_192,
|
||||
hash_bytes_192,
|
||||
hash_string_192,
|
||||
hash_bytes_to_buffer_192,
|
||||
hash_string_to_buffer_192,
|
||||
}
|
||||
|
||||
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
Tiger_Context :: _tiger.Tiger_Context
|
||||
|
||||
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_16(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_20(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [20]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte {
|
||||
hash: [24]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([24]byte, bool) {
|
||||
hash: [24]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_24(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_24(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [24]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_tiger_ctx :: #force_inline proc(hash_size: int) {
|
||||
ctx: _tiger.Tiger_Context
|
||||
init :: proc(ctx: ^_tiger.Tiger_Context) {
|
||||
ctx.ver = 1
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch hash_size {
|
||||
case 16: _hash_impl.hash_size = ._16
|
||||
case 20: _hash_impl.hash_size = ._20
|
||||
case 24: _hash_impl.hash_size = ._24
|
||||
}
|
||||
_tiger.init(ctx)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16: _create_tiger_ctx(16)
|
||||
case ._20: _create_tiger_ctx(20)
|
||||
case ._24: _create_tiger_ctx(24)
|
||||
}
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
}
|
||||
update :: proc(ctx: ^_tiger.Tiger_Context, data: []byte) {
|
||||
_tiger.update(ctx, data)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.final_odin(&c, hash)
|
||||
}
|
||||
}
|
||||
final :: proc(ctx: ^_tiger.Tiger_Context, hash: []byte) {
|
||||
_tiger.final(ctx, hash)
|
||||
}
|
||||
+179
-230
@@ -6,7 +6,6 @@ package tiger2
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Interface for the Tiger2 variant of the Tiger hashing algorithm as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
||||
*/
|
||||
@@ -14,84 +13,84 @@ package tiger2
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_ctx"
|
||||
import "../_tiger"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_16 = hash_bytes_odin_16
|
||||
ctx.hash_file_16 = hash_file_odin_16
|
||||
ctx.hash_stream_16 = hash_stream_odin_16
|
||||
ctx.hash_bytes_20 = hash_bytes_odin_20
|
||||
ctx.hash_file_20 = hash_file_odin_20
|
||||
ctx.hash_stream_20 = hash_stream_odin_20
|
||||
ctx.hash_bytes_24 = hash_bytes_odin_24
|
||||
ctx.hash_file_24 = hash_file_odin_24
|
||||
ctx.hash_stream_24 = hash_stream_odin_24
|
||||
ctx.init = _init_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan does nothing, since Tiger2 is not available in Botan
|
||||
@(warning="Tiger2 is not provided by the Botan API. Odin implementation will be used")
|
||||
use_botan :: #force_inline proc() {
|
||||
use_odin()
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_128 :: 16
|
||||
DIGEST_SIZE_160 :: 20
|
||||
DIGEST_SIZE_192 :: 24
|
||||
|
||||
// hash_string_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_128 :: proc(data: string) -> [16]byte {
|
||||
hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
|
||||
return hash_bytes_128(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_128 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
|
||||
_create_tiger2_ctx(16)
|
||||
return _hash_impl->hash_bytes_16(data)
|
||||
hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
|
||||
hash: [DIGEST_SIZE_128]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 2
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 2
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_128 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
|
||||
_create_tiger2_ctx(16)
|
||||
return _hash_impl->hash_stream_16(s)
|
||||
hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
|
||||
hash: [DIGEST_SIZE_128]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 2
|
||||
_tiger.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// 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) -> ([16]byte, bool) {
|
||||
_create_tiger2_ctx(16)
|
||||
return _hash_impl->hash_file_16(hd, load_at_once)
|
||||
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
|
||||
}
|
||||
|
||||
hash_128 :: proc {
|
||||
@@ -99,33 +98,78 @@ hash_128 :: proc {
|
||||
hash_file_128,
|
||||
hash_bytes_128,
|
||||
hash_string_128,
|
||||
hash_bytes_to_buffer_128,
|
||||
hash_string_to_buffer_128,
|
||||
}
|
||||
|
||||
// hash_string_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_160 :: proc(data: string) -> [20]byte {
|
||||
hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
|
||||
return hash_bytes_160(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_160 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_160 :: proc(data: []byte) -> [20]byte {
|
||||
_create_tiger2_ctx(20)
|
||||
return _hash_impl->hash_bytes_20(data)
|
||||
hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
|
||||
hash: [DIGEST_SIZE_160]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 2
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_160 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_160 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_160 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_160 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 2
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_160 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
|
||||
_create_tiger2_ctx(20)
|
||||
return _hash_impl->hash_stream_20(s)
|
||||
hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
|
||||
hash: [DIGEST_SIZE_160]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 2
|
||||
_tiger.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_160 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
_create_tiger2_ctx(20)
|
||||
return _hash_impl->hash_file_20(hd, load_at_once)
|
||||
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_160(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_160(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_160]byte{}, false
|
||||
}
|
||||
|
||||
hash_160 :: proc {
|
||||
@@ -133,33 +177,78 @@ hash_160 :: proc {
|
||||
hash_file_160,
|
||||
hash_bytes_160,
|
||||
hash_string_160,
|
||||
hash_bytes_to_buffer_160,
|
||||
hash_string_to_buffer_160,
|
||||
}
|
||||
|
||||
// hash_string_192 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_192 :: proc(data: string) -> [24]byte {
|
||||
hash_string_192 :: proc(data: string) -> [DIGEST_SIZE_192]byte {
|
||||
return hash_bytes_192(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_192 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_192 :: proc(data: []byte) -> [24]byte {
|
||||
_create_tiger2_ctx(24)
|
||||
return _hash_impl->hash_bytes_24(data)
|
||||
hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
|
||||
hash: [DIGEST_SIZE_192]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 2
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_192 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_192 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_192(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_192 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_192 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_192, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 2
|
||||
_tiger.init(&ctx)
|
||||
_tiger.update(&ctx, data)
|
||||
_tiger.final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_192 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
|
||||
_create_tiger2_ctx(24)
|
||||
return _hash_impl->hash_stream_24(s)
|
||||
hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
|
||||
hash: [DIGEST_SIZE_192]byte
|
||||
ctx: _tiger.Tiger_Context
|
||||
ctx.ver = 2
|
||||
_tiger.init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update(&ctx, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final(&ctx, hash[:])
|
||||
return hash, true
|
||||
}
|
||||
|
||||
// hash_file_192 will read the file provided by the given handle
|
||||
// and compute a hash
|
||||
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
_create_tiger2_ctx(24)
|
||||
return _hash_impl->hash_file_24(hd, load_at_once)
|
||||
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_192]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_192(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_192(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_192]byte{}, false
|
||||
}
|
||||
|
||||
hash_192 :: proc {
|
||||
@@ -167,165 +256,25 @@ hash_192 :: proc {
|
||||
hash_file_192,
|
||||
hash_bytes_192,
|
||||
hash_string_192,
|
||||
hash_bytes_to_buffer_192,
|
||||
hash_string_to_buffer_192,
|
||||
}
|
||||
|
||||
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
|
||||
hash: [16]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
Tiger_Context :: _tiger.Tiger_Context
|
||||
|
||||
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_16(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [16]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
|
||||
hash: [20]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_20(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [20]byte{}, false
|
||||
}
|
||||
|
||||
hash_bytes_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte {
|
||||
hash: [24]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
_tiger.update_odin(&c, data)
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
hash_stream_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([24]byte, bool) {
|
||||
hash: [24]byte
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
_tiger.update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
_tiger.final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
|
||||
hash_file_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin_24(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin_24(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [24]byte{}, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_create_tiger2_ctx :: #force_inline proc(hash_size: int) {
|
||||
ctx: _tiger.Tiger_Context
|
||||
init :: proc(ctx: ^_tiger.Tiger_Context) {
|
||||
ctx.ver = 2
|
||||
_hash_impl.internal_ctx = ctx
|
||||
switch hash_size {
|
||||
case 16: _hash_impl.hash_size = ._16
|
||||
case 20: _hash_impl.hash_size = ._20
|
||||
case 24: _hash_impl.hash_size = ._24
|
||||
}
|
||||
_tiger.init(ctx)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
#partial switch ctx.hash_size {
|
||||
case ._16: _create_tiger2_ctx(16)
|
||||
case ._20: _create_tiger2_ctx(20)
|
||||
case ._24: _create_tiger2_ctx(24)
|
||||
}
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.init_odin(&c)
|
||||
}
|
||||
update :: proc(ctx: ^_tiger.Tiger_Context, data: []byte) {
|
||||
_tiger.update(ctx, data)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
|
||||
_tiger.final_odin(&c, hash)
|
||||
}
|
||||
final :: proc(ctx: ^_tiger.Tiger_Context, hash: []byte) {
|
||||
_tiger.final(ctx, hash)
|
||||
}
|
||||
@@ -6,7 +6,6 @@ package whirlpool
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
|
||||
|
||||
Implementation of the Whirlpool hashing algorithm, as defined in <https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html>
|
||||
*/
|
||||
@@ -14,77 +13,79 @@ package whirlpool
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../botan"
|
||||
import "../_ctx"
|
||||
import "../util"
|
||||
|
||||
/*
|
||||
Context initialization and switching between the Odin implementation and the bindings
|
||||
*/
|
||||
|
||||
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
|
||||
|
||||
@(private)
|
||||
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
|
||||
ctx := _ctx._init_vtable()
|
||||
when USE_BOTAN_LIB {
|
||||
use_botan()
|
||||
} else {
|
||||
_assign_hash_vtable(ctx)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
@(private)
|
||||
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
|
||||
ctx.hash_bytes_64 = hash_bytes_odin
|
||||
ctx.hash_file_64 = hash_file_odin
|
||||
ctx.hash_stream_64 = hash_stream_odin
|
||||
ctx.update = _update_odin
|
||||
ctx.final = _final_odin
|
||||
}
|
||||
|
||||
_hash_impl := _init_vtable()
|
||||
|
||||
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
|
||||
use_botan :: #force_inline proc() {
|
||||
botan.assign_hash_vtable(_hash_impl, botan.HASH_WHIRLPOOL)
|
||||
}
|
||||
|
||||
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
|
||||
use_odin :: #force_inline proc() {
|
||||
_assign_hash_vtable(_hash_impl)
|
||||
}
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 64
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [64]byte {
|
||||
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) -> [64]byte {
|
||||
_create_whirlpool_ctx()
|
||||
return _hash_impl->hash_bytes_64(data)
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Whirlpool_Context
|
||||
// init(&ctx) No-op
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Whirlpool_Context
|
||||
// init(&ctx) No-op
|
||||
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) -> ([64]byte, bool) {
|
||||
_create_whirlpool_ctx()
|
||||
return _hash_impl->hash_stream_64(s)
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Whirlpool_Context
|
||||
// init(&ctx) No-op
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = s->impl_read(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) -> ([64]byte, bool) {
|
||||
_create_whirlpool_ctx()
|
||||
return _hash_impl->hash_file_64(hd, load_at_once)
|
||||
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 {
|
||||
@@ -92,82 +93,111 @@ hash :: proc {
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^_ctx.Hash_Context) {
|
||||
_hash_impl->init()
|
||||
@(warning="Init is a no-op for Whirlpool")
|
||||
init :: proc(ctx: ^Whirlpool_Context) {
|
||||
// No action needed here
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
_hash_impl->update(data)
|
||||
update :: proc(ctx: ^Whirlpool_Context, source: []byte) {
|
||||
source_pos: int
|
||||
nn := len(source)
|
||||
source_bits := u64(nn * 8)
|
||||
source_gap := u32((8 - (int(source_bits & 7))) & 7)
|
||||
buffer_rem := uint(ctx.buffer_bits & 7)
|
||||
b: u32
|
||||
|
||||
for i, carry, value := 31, u32(0), u32(source_bits); i >= 0 && (carry != 0 || value != 0); i -= 1 {
|
||||
carry += u32(ctx.bitlength[i]) + (u32(value & 0xff))
|
||||
ctx.bitlength[i] = byte(carry)
|
||||
carry >>= 8
|
||||
value >>= 8
|
||||
}
|
||||
|
||||
for source_bits > 8 {
|
||||
b = u32(u32((source[source_pos] << source_gap) & 0xff) | u32((source[source_pos+1] & 0xff) >> (8 - source_gap)))
|
||||
|
||||
ctx.buffer[ctx.buffer_pos] |= u8(b >> buffer_rem)
|
||||
ctx.buffer_pos += 1
|
||||
ctx.buffer_bits += int(8 - buffer_rem)
|
||||
|
||||
if ctx.buffer_bits == 512 {
|
||||
transform(ctx)
|
||||
ctx.buffer_bits = 0
|
||||
ctx.buffer_pos = 0
|
||||
}
|
||||
ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem))
|
||||
ctx.buffer_bits += int(buffer_rem)
|
||||
source_bits -= 8
|
||||
source_pos += 1
|
||||
}
|
||||
|
||||
if source_bits > 0 {
|
||||
b = u32((source[source_pos] << source_gap) & 0xff)
|
||||
ctx.buffer[ctx.buffer_pos] |= byte(b) >> buffer_rem
|
||||
} else {b = 0}
|
||||
|
||||
if u64(buffer_rem) + source_bits < 8 {
|
||||
ctx.buffer_bits += int(source_bits)
|
||||
} else {
|
||||
ctx.buffer_pos += 1
|
||||
ctx.buffer_bits += 8 - int(buffer_rem)
|
||||
source_bits -= u64(8 - buffer_rem)
|
||||
|
||||
if ctx.buffer_bits == 512 {
|
||||
transform(ctx)
|
||||
ctx.buffer_bits = 0
|
||||
ctx.buffer_pos = 0
|
||||
}
|
||||
ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem))
|
||||
ctx.buffer_bits += int(source_bits)
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
_hash_impl->final(hash)
|
||||
}
|
||||
final :: proc(ctx: ^Whirlpool_Context, hash: []byte) {
|
||||
n := ctx
|
||||
n.buffer[n.buffer_pos] |= 0x80 >> (uint(n.buffer_bits) & 7)
|
||||
n.buffer_pos += 1
|
||||
|
||||
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
update_odin(&c, data)
|
||||
final_odin(&c, hash[:])
|
||||
}
|
||||
return hash
|
||||
}
|
||||
if n.buffer_pos > 64 - 32 {
|
||||
if n.buffer_pos < 64 {
|
||||
for i := 0; i < 64 - n.buffer_pos; i += 1 {
|
||||
n.buffer[n.buffer_pos + i] = 0
|
||||
}
|
||||
}
|
||||
transform(ctx)
|
||||
n.buffer_pos = 0
|
||||
}
|
||||
|
||||
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
|
||||
hash: [64]byte
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = fs->impl_read(buf)
|
||||
if read > 0 {
|
||||
update_odin(&c, buf[:read])
|
||||
}
|
||||
}
|
||||
final_odin(&c, hash[:])
|
||||
return hash, true
|
||||
} else {
|
||||
return hash, false
|
||||
}
|
||||
}
|
||||
if n.buffer_pos < 64 - 32 {
|
||||
for i := 0; i < (64 - 32) - n.buffer_pos; i += 1 {
|
||||
n.buffer[n.buffer_pos + i] = 0
|
||||
}
|
||||
}
|
||||
n.buffer_pos = 64 - 32
|
||||
|
||||
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_odin(ctx, os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_odin(ctx, buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [64]byte{}, false
|
||||
}
|
||||
for i := 0; i < 32; i += 1 {
|
||||
n.buffer[n.buffer_pos + i] = n.bitlength[i]
|
||||
}
|
||||
transform(ctx)
|
||||
|
||||
@(private)
|
||||
_create_whirlpool_ctx :: #force_inline proc() {
|
||||
ctx: Whirlpool_Context
|
||||
_hash_impl.internal_ctx = ctx
|
||||
_hash_impl.hash_size = ._64
|
||||
}
|
||||
|
||||
@(private)
|
||||
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
update_odin(&c, data)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
|
||||
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
|
||||
final_odin(&c, hash)
|
||||
}
|
||||
for i := 0; i < 8; i += 1 {
|
||||
hash[i * 8] = byte(n.hash[i] >> 56)
|
||||
hash[i * 8 + 1] = byte(n.hash[i] >> 48)
|
||||
hash[i * 8 + 2] = byte(n.hash[i] >> 40)
|
||||
hash[i * 8 + 3] = byte(n.hash[i] >> 32)
|
||||
hash[i * 8 + 4] = byte(n.hash[i] >> 24)
|
||||
hash[i * 8 + 5] = byte(n.hash[i] >> 16)
|
||||
hash[i * 8 + 6] = byte(n.hash[i] >> 8)
|
||||
hash[i * 8 + 7] = byte(n.hash[i])
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -774,97 +804,3 @@ transform :: proc (ctx: ^Whirlpool_Context) {
|
||||
}
|
||||
for i := 0; i < 8; i += 1 {ctx.hash[i] ~= state[i] ~ block[i]}
|
||||
}
|
||||
|
||||
update_odin :: proc(ctx: ^Whirlpool_Context, source: []byte) {
|
||||
source_pos: int
|
||||
nn := len(source)
|
||||
source_bits := u64(nn * 8)
|
||||
source_gap := u32((8 - (int(source_bits & 7))) & 7)
|
||||
buffer_rem := uint(ctx.buffer_bits & 7)
|
||||
b: u32
|
||||
|
||||
for i, carry, value := 31, u32(0), u32(source_bits); i >= 0 && (carry != 0 || value != 0); i -= 1 {
|
||||
carry += u32(ctx.bitlength[i]) + (u32(value & 0xff))
|
||||
ctx.bitlength[i] = byte(carry)
|
||||
carry >>= 8
|
||||
value >>= 8
|
||||
}
|
||||
|
||||
for source_bits > 8 {
|
||||
b = u32(u32((source[source_pos] << source_gap) & 0xff) | u32((source[source_pos+1] & 0xff) >> (8 - source_gap)))
|
||||
|
||||
ctx.buffer[ctx.buffer_pos] |= u8(b >> buffer_rem)
|
||||
ctx.buffer_pos += 1
|
||||
ctx.buffer_bits += int(8 - buffer_rem)
|
||||
|
||||
if ctx.buffer_bits == 512 {
|
||||
transform(ctx)
|
||||
ctx.buffer_bits = 0
|
||||
ctx.buffer_pos = 0
|
||||
}
|
||||
ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem))
|
||||
ctx.buffer_bits += int(buffer_rem)
|
||||
source_bits -= 8
|
||||
source_pos += 1
|
||||
}
|
||||
|
||||
if source_bits > 0 {
|
||||
b = u32((source[source_pos] << source_gap) & 0xff)
|
||||
ctx.buffer[ctx.buffer_pos] |= byte(b) >> buffer_rem
|
||||
} else {b = 0}
|
||||
|
||||
if u64(buffer_rem) + source_bits < 8 {
|
||||
ctx.buffer_bits += int(source_bits)
|
||||
} else {
|
||||
ctx.buffer_pos += 1
|
||||
ctx.buffer_bits += 8 - int(buffer_rem)
|
||||
source_bits -= u64(8 - buffer_rem)
|
||||
|
||||
if ctx.buffer_bits == 512 {
|
||||
transform(ctx)
|
||||
ctx.buffer_bits = 0
|
||||
ctx.buffer_pos = 0
|
||||
}
|
||||
ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem))
|
||||
ctx.buffer_bits += int(source_bits)
|
||||
}
|
||||
}
|
||||
|
||||
final_odin :: proc(ctx: ^Whirlpool_Context, hash: []byte) {
|
||||
n := ctx
|
||||
n.buffer[n.buffer_pos] |= 0x80 >> (uint(n.buffer_bits) & 7)
|
||||
n.buffer_pos += 1
|
||||
|
||||
if n.buffer_pos > 64 - 32 {
|
||||
if n.buffer_pos < 64 {
|
||||
for i := 0; i < 64 - n.buffer_pos; i += 1 {
|
||||
n.buffer[n.buffer_pos + i] = 0
|
||||
}
|
||||
}
|
||||
transform(ctx)
|
||||
n.buffer_pos = 0
|
||||
}
|
||||
|
||||
if n.buffer_pos < 64 - 32 {
|
||||
for i := 0; i < (64 - 32) - n.buffer_pos; i += 1 {
|
||||
n.buffer[n.buffer_pos + i] = 0
|
||||
}
|
||||
}
|
||||
n.buffer_pos = 64 - 32
|
||||
|
||||
for i := 0; i < 32; i += 1 {
|
||||
n.buffer[n.buffer_pos + i] = n.bitlength[i]
|
||||
}
|
||||
transform(ctx)
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
hash[i * 8] = byte(n.hash[i] >> 56)
|
||||
hash[i * 8 + 1] = byte(n.hash[i] >> 48)
|
||||
hash[i * 8 + 2] = byte(n.hash[i] >> 40)
|
||||
hash[i * 8 + 3] = byte(n.hash[i] >> 32)
|
||||
hash[i * 8 + 4] = byte(n.hash[i] >> 24)
|
||||
hash[i * 8 + 5] = byte(n.hash[i] >> 16)
|
||||
hash[i * 8 + 6] = byte(n.hash[i] >> 8)
|
||||
hash[i * 8 + 7] = byte(n.hash[i])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package x25519
|
||||
|
||||
import field "core:crypto/_fiat/field_curve25519"
|
||||
import "core:mem"
|
||||
|
||||
SCALAR_SIZE :: 32
|
||||
POINT_SIZE :: 32
|
||||
|
||||
_BASE_POINT: [32]byte = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
_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
|
||||
}
|
||||
|
||||
_scalarmult :: proc (out, scalar, point: ^[32]byte) {
|
||||
// Montgomery pseduo-multiplication taken from Monocypher.
|
||||
|
||||
// computes the scalar product
|
||||
x1: field.Tight_Field_Element = ---
|
||||
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 = ---, ---, ---, ---
|
||||
t0, t1: field.Loose_Field_Element = ---, ---
|
||||
|
||||
// Montgomery ladder
|
||||
// In projective coordinates, to avoid divisions: x = X / Z
|
||||
// We don't care about the y coordinate, it's only 1 bit of information
|
||||
field.fe_one(&x2) // "zero" point
|
||||
field.fe_zero(&z2)
|
||||
field.fe_set(&x3, &x1) // "one" point
|
||||
field.fe_one(&z3)
|
||||
|
||||
swap: int
|
||||
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
|
||||
field.fe_cond_swap(&x2, &x3, swap)
|
||||
field.fe_cond_swap(&z2, &z3, swap)
|
||||
swap = b // anticipates one last swap after the loop
|
||||
|
||||
// Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3)
|
||||
// with differential addition
|
||||
//
|
||||
// Note: This deliberately omits reductions after add/sub operations
|
||||
// if the result is only ever used as the input to a mul/square since
|
||||
// the implementations of those can deal with non-reduced inputs.
|
||||
//
|
||||
// fe_tighten_cast is only used to store a fully reduced
|
||||
// output in a Loose_Field_Element, or to provide such a
|
||||
// Loose_Field_Element as a Tight_Field_Element argument.
|
||||
field.fe_sub(&t0, &x3, &z3)
|
||||
field.fe_sub(&t1, &x2, &z2)
|
||||
field.fe_add(field.fe_relax_cast(&x2), &x2, &z2) // x2 - unreduced
|
||||
field.fe_add(field.fe_relax_cast(&z2), &x3, &z3) // z2 - unreduced
|
||||
field.fe_carry_mul(&z3, &t0, field.fe_relax_cast(&x2))
|
||||
field.fe_carry_mul(&z2, field.fe_relax_cast(&z2), &t1) // z2 - reduced
|
||||
field.fe_carry_square(field.fe_tighten_cast(&t0), &t1) // t0 - reduced
|
||||
field.fe_carry_square(field.fe_tighten_cast(&t1), field.fe_relax_cast(&x2)) // t1 - reduced
|
||||
field.fe_add(field.fe_relax_cast(&x3), &z3, &z2) // x3 - unreduced
|
||||
field.fe_sub(field.fe_relax_cast(&z2), &z3, &z2) // z2 - unreduced
|
||||
field.fe_carry_mul(&x2, &t1, &t0) // x2 - reduced
|
||||
field.fe_sub(&t1, field.fe_tighten_cast(&t1), field.fe_tighten_cast(&t0)) // safe - t1/t0 is reduced
|
||||
field.fe_carry_square(&z2, field.fe_relax_cast(&z2)) // z2 - reduced
|
||||
field.fe_carry_scmul_121666(&z3, &t1)
|
||||
field.fe_carry_square(&x3, field.fe_relax_cast(&x3)) // x3 - reduced
|
||||
field.fe_add(&t0, field.fe_tighten_cast(&t0), &z3) // safe - t0 is reduced
|
||||
field.fe_carry_mul(&z3, field.fe_relax_cast(&x1), field.fe_relax_cast(&z2))
|
||||
field.fe_carry_mul(&z2, &t1, &t0)
|
||||
}
|
||||
// last swap is necessary to compensate for the xor trick
|
||||
// Note: after this swap, P3 == P2 + P1.
|
||||
field.fe_cond_swap(&x2, &x3, swap)
|
||||
field.fe_cond_swap(&z2, &z3, swap)
|
||||
|
||||
// normalises the coordinates: x == X / Z
|
||||
field.fe_carry_inv(&z2, field.fe_relax_cast(&z2))
|
||||
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))
|
||||
}
|
||||
|
||||
scalarmult :: proc (dst, scalar, point: []byte) {
|
||||
if len(scalar) != SCALAR_SIZE {
|
||||
panic("crypto/x25519: invalid scalar size")
|
||||
}
|
||||
if len(point) != POINT_SIZE {
|
||||
panic("crypto/x25519: invalid point size")
|
||||
}
|
||||
if len(dst) != POINT_SIZE {
|
||||
panic("crypto/x25519: invalid destination point size")
|
||||
}
|
||||
|
||||
// "clamp" the scalar
|
||||
e: [32]byte = ---
|
||||
copy_slice(e[:], scalar)
|
||||
e[0] &= 248
|
||||
e[31] &= 127
|
||||
e[31] |= 64
|
||||
|
||||
p: [32]byte = ---
|
||||
copy_slice(p[:], point)
|
||||
|
||||
d: [32]byte = ---
|
||||
_scalarmult(&d, &e, &p)
|
||||
copy_slice(dst, d[:])
|
||||
|
||||
mem.zero_explicit(&e, size_of(e))
|
||||
mem.zero_explicit(&d, size_of(d))
|
||||
}
|
||||
|
||||
scalarmult_basepoint :: proc (dst, scalar: []byte) {
|
||||
// TODO/perf: Switch to using a precomputed table.
|
||||
scalarmult(dst, scalar, _BASE_POINT[:])
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user