mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 11:11:37 -07:00
Compare commits
2280 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e5342f41f | |||
| 18607e53cb | |||
| ed933b3f21 | |||
| 49fecbdc5e | |||
| f971126183 | |||
| d4ccb69ccc | |||
| a70dde34da | |||
| f76f70c7cf | |||
| dd9843aa21 | |||
| 3c72cb67d3 | |||
| 7681c43b14 | |||
| 7e43cd7d97 | |||
| c223fc1766 | |||
| 04297bb680 | |||
| 553292ffd0 | |||
| 57862846a2 | |||
| 8e270d3a99 | |||
| ae5cb09041 | |||
| 093b2288c3 | |||
| ffe17a471d | |||
| e9f901b82d | |||
| ed3004f8a0 | |||
| d97df080f9 | |||
| 0e5c7e08fc | |||
| 83523badb7 | |||
| 0a90994403 | |||
| 376906e0ae | |||
| 47c79a2f25 | |||
| 00c138ce9f | |||
| 5676c9e7eb | |||
| 3a469dc13e | |||
| d3c70f2206 | |||
| 14f1793b3e | |||
| 8cecb6b9f5 | |||
| 4a66c3c420 | |||
| c3c88633a5 | |||
| aeaf1199ec | |||
| d4f62f52db | |||
| 384fb76a1b | |||
| dd0d61e97c | |||
| e81ed9a960 | |||
| 83f7a887b7 | |||
| ad2f1ac24e | |||
| 62d232d798 | |||
| 8906a0120c | |||
| cad753e398 | |||
| fd627dc13b | |||
| 70e8d97ee1 | |||
| cdecb0ccc3 | |||
| 460b5149af | |||
| b6dc253d8b | |||
| e7be9493ba | |||
| ea34f321ed | |||
| 2b5bc1d558 | |||
| e6a7b85da4 | |||
| 6145185478 | |||
| 2abba6e057 | |||
| db5a1b0c78 | |||
| 14cb19c2df | |||
| 46bcd18946 | |||
| ba61d911da | |||
| 3b69c6b204 | |||
| d7eabf571c | |||
| ddf9c4a65b | |||
| b3d797598e | |||
| 31c7945444 | |||
| 276e014d18 | |||
| 27f206784c | |||
| 54a6637d38 | |||
| 23be56af59 | |||
| 71df46456a | |||
| cd89d8a3c4 | |||
| 4c62a32b04 | |||
| 5f8137025d | |||
| 1843d52217 | |||
| 454c92dc64 | |||
| 7e33a86d54 | |||
| 197b832992 | |||
| 8f13724a4b | |||
| 746d5fc322 | |||
| ffc45e8cc2 | |||
| f138f71fa6 | |||
| 89b7a3f7ac | |||
| 75e15b05b0 | |||
| 78eb388110 | |||
| 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 | |||
| 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 | |||
| 92e70b9a58 | |||
| 822da9d12d | |||
| b0817d136b | |||
| 53e30e4621 | |||
| dbf42d2469 | |||
| 36c61aeacf | |||
| 289b0422bd | |||
| 78359f0c16 | |||
| 3f8c6a6745 | |||
| 5d653a9d8e | |||
| 7f61a90ea1 | |||
| 982ec1e58b | |||
| 86f831ddd1 | |||
| 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 | |||
| a7138b22a5 | |||
| 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 | |||
| c20230509f | |||
| f03e0bee73 | |||
| c5cd97dd89 | |||
| c09d3e7bf4 | |||
| 46a394815c | |||
| a4b68b93f2 | |||
| e2e9b5d3b0 | |||
| b01e0fbbc2 | |||
| 4f0a3eec24 | |||
| 2a0311797c | |||
| 46e2b4e936 | |||
| 96ba8f35d4 | |||
| 9a5ddc5218 | |||
| 0b4cbcf409 | |||
| d0cd3a7415 | |||
| 3a167e3a75 | |||
| c9e21907ea | |||
| a422d0455e | |||
| 0bc3652fc7 | |||
| 672fc9fc4d | |||
| 796a0c3968 | |||
| ff36bd3d85 | |||
| 141299eb02 | |||
| efe05b3e13 | |||
| b1de429d2c | |||
| 5f51337a01 | |||
| fca7142a3c | |||
| bfa33bf5d3 | |||
| 235dae552a | |||
| a36c1ad406 | |||
| 32506a34ff | |||
| 3224d04df8 | |||
| c13c30b466 | |||
| 3de1719c17 | |||
| 0c1675c8b0 | |||
| 3d89c6b35d | |||
| 22982586f1 | |||
| 0d2c8dfeec | |||
| 305e965bcb | |||
| 906c7ef0fc | |||
| 2a7937e2ba | |||
| 2a5b8f53fe | |||
| c6e08b059b | |||
| 9a5216921c | |||
| 8a2078aa90 | |||
| 841a96691b | |||
| 8ef6f9dd7b | |||
| 5bc8a491a7 | |||
| 87952fdb8e | |||
| c474e137ac | |||
| 7b8b2f042c | |||
| 729e721b40 | |||
| e4ce017183 | |||
| bbccf9ddbf | |||
| a8425cfb47 | |||
| ef29ffeb21 | |||
| 3794d2417d | |||
| 70793236ab | |||
| 0a1ef1e59d | |||
| 44a43eae10 | |||
| 5b7f273165 | |||
| fa07e45eda | |||
| 90d587df13 | |||
| 793a6479ef | |||
| 47bef7a557 | |||
| 208ba2c116 | |||
| 42a8ac7096 | |||
| ccab715bbc | |||
| ee724f183a | |||
| c4d2aae0ed | |||
| c722665c32 | |||
| 8868bcf57a | |||
| 9e754cb0f1 | |||
| 802df73183 | |||
| 9331ebebcc | |||
| c64674c39e | |||
| 549a383cf0 | |||
| ce8ddd0c3f | |||
| 98490454b6 | |||
| bb334007cf | |||
| 9522c42460 | |||
| d165de0d4d | |||
| 5fb70c4c94 | |||
| 72cc92dc5c | |||
| de0fbb0445 | |||
| 12ebd422c6 | |||
| 7d715fe113 | |||
| 7ac156755b | |||
| 973ca6824c | |||
| 9c7956be9e | |||
| 4475454632 | |||
| f15825d2c6 | |||
| a440d8d812 | |||
| aaaddd03a6 | |||
| d62c701a43 | |||
| 79ad6f4564 | |||
| c60c7a7621 | |||
| 306bdf8869 | |||
| 37d3a8a861 | |||
| 7a29f6cff0 | |||
| c9e6862332 | |||
| b77e2f59ed | |||
| cac8582de1 | |||
| b4df51e483 | |||
| 06f4762144 | |||
| 357d085ffb | |||
| 2a50f9c8d4 | |||
| d54b708ba8 | |||
| fba20c9bd3 | |||
| 18ed444a63 | |||
| 2d6c43c260 | |||
| 71932da7e3 | |||
| a074c367ff | |||
| e6fb4ee265 | |||
| 7c336a6a82 | |||
| 71ae424aa8 | |||
| 88d6407092 | |||
| e15f245339 | |||
| eebf49ba72 | |||
| faa9df8735 | |||
| 8113182d5c | |||
| 3323c2730f | |||
| 6399f2b014 | |||
| 7218a68e89 | |||
| 7375074d2d | |||
| c9effb9b9f | |||
| ef73a284e3 | |||
| 28af376d10 | |||
| 48de1a01a9 | |||
| 209684d5a4 | |||
| 1405420935 | |||
| 48d277a3c4 | |||
| e0b9475378 | |||
| c561de33ee | |||
| d67d7168e2 | |||
| 3b3e7550f6 | |||
| 9b7a25d4a9 | |||
| bb0855b35a | |||
| e6f725dc2c | |||
| 3e4c2e4932 | |||
| 2f4f6894bd | |||
| 30c141ceb9 | |||
| d3abc1a2b4 | |||
| 465c87bd5a | |||
| cdf881a378 | |||
| e8c602b98f | |||
| d0d9a3a4f4 | |||
| 9e43072113 | |||
| 1bfbed0e02 | |||
| 0fd525d778 | |||
| 07bf64ae52 | |||
| cee45c1b15 | |||
| 68afbb37f4 | |||
| 7faca7066c | |||
| 3eaac057da | |||
| 3f1930b76e | |||
| afac1a2cc1 | |||
| 445696d660 | |||
| f454ac3150 | |||
| 82b6772ea4 | |||
| 662cbaf425 | |||
| 1556fad65a | |||
| 243e2e2b8a | |||
| 35111b39b8 | |||
| ba331024af | |||
| 4c655865e5 | |||
| 7aac8df2f2 | |||
| e98e62ec13 | |||
| eb3c1506f0 | |||
| 5bd4536372 | |||
| 7fa05238b8 | |||
| dd7449b8b5 | |||
| b7a0627d09 | |||
| c632125d82 | |||
| a509e112ea | |||
| 7c26b0abdb | |||
| f48ee00c2f | |||
| ced0ea515c | |||
| 8876328e4d | |||
| f4ff11a7ff | |||
| 77be7144c3 | |||
| 3337d38651 | |||
| fc3b0dcc80 | |||
| fc33cf6846 | |||
| 2c469a39c7 | |||
| d0001dc11d | |||
| 68719779d7 | |||
| 2ad6aa7886 | |||
| 75e3df6da2 | |||
| 753acc6971 | |||
| 0d258e8b55 | |||
| 3a60bee804 | |||
| b67e0002c1 | |||
| 0070119392 | |||
| 310fd1936b | |||
| 077bf28d26 | |||
| d97c6a7657 | |||
| 46b33854c9 | |||
| 5ee3686569 | |||
| d23144fd27 | |||
| e64eb631df | |||
| 129a62d4f1 | |||
| 8c9299c139 | |||
| 3e9b7120be | |||
| 675ba44e67 | |||
| 240b6aab13 | |||
| a1e8769cff | |||
| 73cba2cf13 | |||
| 49c761dc6d | |||
| 2a8d4c5bf5 | |||
| 2b8807eb73 | |||
| 2ef0e6b8f6 | |||
| d386563344 | |||
| 9ecc2ab15b | |||
| cb9101e0a0 | |||
| fd9adaf1de | |||
| af7008aa44 | |||
| d3a18fbe9a | |||
| b4b53aeb71 | |||
| c4b4a841d6 | |||
| 263d63aa56 | |||
| 8fcd1794a6 | |||
| 9b5ae95677 | |||
| 21c6d691d8 | |||
| 2e9eec156c | |||
| c9468adcfd | |||
| b345176bde | |||
| 879a4d49ae | |||
| 15594706c9 | |||
| 58e3f779f2 | |||
| 455d64fbd4 | |||
| 16ca677c1f | |||
| 1f1434b384 | |||
| f83370235f | |||
| 4961aff51b | |||
| 0ed3143006 | |||
| bc2b8b597c | |||
| ccc4c641c4 | |||
| 0ca8a4ad3b | |||
| 1931e3147d | |||
| 9d9ec192f1 | |||
| 586a000152 | |||
| 728f143e33 | |||
| f2e78dcc0b | |||
| a463609e4c | |||
| e05a305764 | |||
| a437c95fed | |||
| 8fe70978ff | |||
| 647fee31f8 | |||
| 8af6b6fa18 | |||
| f48f06e7b7 | |||
| 51b5a973e2 | |||
| 2bdae52fed | |||
| b3a66b3950 | |||
| a971fb6e94 | |||
| 036fa5cb24 | |||
| 81fe50d623 | |||
| b9b9bd8612 | |||
| 171a5d6476 | |||
| 00671a59a0 | |||
| ebca0398a7 | |||
| 58a405cc9f | |||
| 444fedd8d4 | |||
| f7137bf367 | |||
| 92a0ce991e | |||
| 1e7c60e171 | |||
| 2d2e3ed6a1 | |||
| 21a06df5e1 | |||
| c04efe8762 | |||
| 41071ee351 | |||
| 594049027b | |||
| b1ea291942 | |||
| 254c504465 | |||
| d32d1b7e07 | |||
| cf94fd7b82 | |||
| 6ca793f0e9 | |||
| 9ceb6b79fd | |||
| bd1e1c66f3 | |||
| 08ac2fd1b6 | |||
| b2a36f204e | |||
| efdd5cf499 | |||
| 805e48ae1e | |||
| c6ff88e85a | |||
| f99bea12c7 | |||
| 19ac822e4a | |||
| 9d797ea225 | |||
| aac290e366 | |||
| 1d6e398f20 | |||
| 9fae575531 | |||
| e928793e79 | |||
| e99061500d | |||
| 8e2a0be804 | |||
| 94a27224b2 | |||
| 4acb4c4ee2 | |||
| b2164b5da6 | |||
| 057310472e | |||
| c22f2866c7 | |||
| 07ba3e6304 | |||
| 9bc12e3f38 | |||
| b3f2263442 | |||
| 6884841ece | |||
| 6f04c61bdf | |||
| d452758afc | |||
| c933054872 | |||
| 6f872e04c8 | |||
| ea0bf05727 | |||
| bcb07b6b9e | |||
| edbf2bf56f | |||
| 93f97defb9 | |||
| 22827da4a2 | |||
| aa382959a7 | |||
| 6fab93c4b3 | |||
| 5e30b3eeef | |||
| 14a3ddb9af | |||
| f7bf55af4a | |||
| 6a271355a6 | |||
| 931fdd2dca | |||
| e6d8fbf194 | |||
| 6fa3c992b5 | |||
| 2c055b0972 | |||
| 868c923770 | |||
| fa0d58f96e | |||
| ebc09d5e4e | |||
| bfc92d0aaf | |||
| 8c4197af38 | |||
| 374f98356c | |||
| 20529c6512 | |||
| ea555c0ccd | |||
| 0d20190b0f | |||
| 3ac13d2028 | |||
| 868d9ba534 | |||
| c0437330e9 | |||
| 6a86b3c82b | |||
| a7f59ab388 | |||
| 5586c6b9c5 | |||
| 964a09ef7a | |||
| 360f75a65c | |||
| 0201fa3fbb | |||
| 31daf80aea | |||
| 48f496f3b3 | |||
| 76fa037ca7 | |||
| 6e22a6dfa5 | |||
| 181eabcffc | |||
| b600ffba3b | |||
| b3dc3f5908 | |||
| 0a11a6e1f6 | |||
| 46d99395ce | |||
| 50ee65ea9e | |||
| 5840bfba62 | |||
| a2a7647c90 | |||
| 70dff11b29 | |||
| eb9665f836 | |||
| bf15e63130 | |||
| efa513262e | |||
| 64ce55944a | |||
| daccfca11d | |||
| ee876ad66b | |||
| ceebd7b23c | |||
| e760535dba | |||
| 2b77f5b72f | |||
| 72fe1e88a3 | |||
| 505113ee2d | |||
| 71f2289c20 | |||
| 21b6ef8757 | |||
| b427bd8105 | |||
| 6855538729 | |||
| 05ac2002e0 | |||
| 73427d3ab3 | |||
| 3713f11461 | |||
| 08ae186d8e | |||
| 54b7ed5b52 | |||
| 64f5ba6ba1 | |||
| f38b7ebf42 | |||
| 4cb7f05644 | |||
| 91d089ffe2 | |||
| d2aa6af882 | |||
| 0d12432d3f | |||
| 6d7df6f335 | |||
| b6d0a8fe0c | |||
| 662d27b796 | |||
| de00c7c9a8 | |||
| 736a763859 | |||
| 4eda1b0598 | |||
| 2e21312059 | |||
| 22218fff67 | |||
| e7ea09a93b | |||
| 6f182ae5ae | |||
| 43ebee4e91 | |||
| cb2437959c | |||
| 61efbf8684 | |||
| da14292369 | |||
| b0d5dde3d7 | |||
| 7189625cf5 | |||
| 1012055ba9 | |||
| 54e16bed0a | |||
| 9772e3a40b | |||
| 442c25bf82 | |||
| c2707618d9 | |||
| a641ef95c0 | |||
| 4e4ac460e5 | |||
| 526a42c6ca | |||
| 8de728e3dc | |||
| e9b9d15de7 | |||
| a9f4c90c79 | |||
| 6ca58dd64e | |||
| 01aa0c4151 | |||
| be68bf9f26 | |||
| 15c309b0b8 | |||
| 042dbda47f | |||
| 2d7aea79b9 | |||
| 31ed4f15a8 | |||
| 71bffd46dc | |||
| f5bc95eb34 | |||
| fb8fa5217d | |||
| 6585601765 | |||
| 2a1193ee54 | |||
| 519308a16a | |||
| 3195fac92b | |||
| 15921d032c | |||
| 813d3dc319 | |||
| ada3be05fb | |||
| 36a2cf0369 | |||
| 827363708c | |||
| 00c1d34108 | |||
| 87f1815486 | |||
| f978e74773 | |||
| 9e018b642e | |||
| 7260d3cecb | |||
| c51c002ffa | |||
| a5b4b73544 | |||
| e3359a2639 | |||
| 344abf2cb2 | |||
| 93593f4721 | |||
| ab85571ae6 | |||
| 6769a00929 | |||
| 5f920414d7 | |||
| 72782d9035 | |||
| eaefbc43cb | |||
| 1f1d8ef884 | |||
| 6899c7b7dd | |||
| b712c84afb | |||
| 9980f81062 | |||
| 297cdde144 | |||
| 461ca40848 | |||
| 12af657369 | |||
| c519ab9401 | |||
| 99df0f1b12 | |||
| 6daa8c8c59 | |||
| 87408aedb5 | |||
| 809b3a87af | |||
| 43c578d8b7 | |||
| 31a5b2bbbd | |||
| e5652da6da | |||
| 05e303e9a0 | |||
| fc66ce9dd6 | |||
| f04614b1f1 | |||
| 637685316d | |||
| f16e98b074 | |||
| f5d5417af7 | |||
| e65e0b5db2 | |||
| 56bd1e2d70 | |||
| a70ecdba6e | |||
| 187e6d3ab7 | |||
| 2786c30736 | |||
| 18ca975d33 | |||
| eb76b00a5e | |||
| 66ead6cf09 | |||
| e6905f8657 | |||
| 027d69678a | |||
| 0262539f68 | |||
| bff66ff600 | |||
| d36329b8ec | |||
| 7e859c3e75 | |||
| 5bcb47989d | |||
| e5fbf45c42 | |||
| 123db3a23d | |||
| d29a0c6680 | |||
| ff40e47042 | |||
| 76419383a8 | |||
| dc14b66c98 | |||
| e831e919a6 | |||
| 3aa2924a06 | |||
| 36151d227a | |||
| e0ee59084e | |||
| 8ff898079f | |||
| 5006674a9b | |||
| ca33cb990b | |||
| d4f5ef046d | |||
| 30554f4758 | |||
| 83bd7c73c4 | |||
| 3754af62d6 | |||
| 12296cec2c | |||
| a9f4273514 | |||
| 72508dbce3 | |||
| 3e459ba16b | |||
| 98d25cdf4d | |||
| ce2f926ff7 | |||
| c0c7b4cbda | |||
| fcb23abce4 | |||
| f7601a759b | |||
| 49011f5198 | |||
| f6d496c81d | |||
| 1dffd4ea3d | |||
| 78e923b6b0 | |||
| 1012f20d3b | |||
| 1ab6a765da | |||
| 6954076f15 | |||
| 56de14fd46 | |||
| 74a883de04 | |||
| c0b07c71f0 | |||
| de91433cb6 | |||
| ed0c3eb0a8 | |||
| 1d57a91395 | |||
| cce76ed614 | |||
| 5a77a10f0a | |||
| 97a46f664d | |||
| aa5b1c9c97 | |||
| fbebf4bc4e | |||
| abb15ddb38 | |||
| 4db5683011 | |||
| c4ec459d28 | |||
| 86cfb2ea82 | |||
| 0605a9f239 | |||
| 11c298775e | |||
| b8de25c864 | |||
| 008048e199 | |||
| fd256002b3 | |||
| 48bfce2efc | |||
| 5619b349be | |||
| b0edac58b9 | |||
| 2c13315e72 | |||
| 0434281f73 | |||
| 3bf005bfc5 | |||
| adcb39ef29 | |||
| 3ebfaf5f4c | |||
| 97a1147537 | |||
| b63d49aafa | |||
| db8f0874bd | |||
| 3ac12a4edf | |||
| f5615b204c | |||
| 2800d4b8d0 | |||
| bc15ce302c | |||
| 31f779f1a4 | |||
| a3a891a7f4 | |||
| b31a24e5e8 | |||
| de5d897b5c | |||
| 7db0c50e63 | |||
| 3faac14d62 | |||
| b2fa4ec675 | |||
| f33d0725db | |||
| b45842c33f | |||
| 1f5ce91ae2 | |||
| d7627744da | |||
| f2c5c26f2c | |||
| 6d07bd3299 | |||
| 52da5b8724 | |||
| b1ed7fc6b9 | |||
| e3809f5c1b | |||
| 4b7dcc1513 | |||
| c9f4fdc856 | |||
| 8178a2e7ce | |||
| 70e12f7a1c | |||
| 11ae87cc2f | |||
| b0f1b1ca16 | |||
| 7ed4f01d02 | |||
| 07f7d14d2c | |||
| eecc786bd2 | |||
| 7fa04fa018 | |||
| 31918d3b8f | |||
| e639c61499 | |||
| e2f035d6ee | |||
| ae354731ed | |||
| 671b413b15 | |||
| 335d361fc6 | |||
| df29d10210 | |||
| fd83cbf40b | |||
| 7d7ed6b95f | |||
| a056e19434 | |||
| 7d0dedf951 | |||
| ee04fb1ce1 | |||
| 2110778040 | |||
| 65a15e9c06 | |||
| c3a70ac277 | |||
| 5e520f4e08 | |||
| b2cf0755f2 | |||
| 720884e0f1 | |||
| 773a766b83 | |||
| 169e717021 | |||
| 72a741554c | |||
| cd09068e33 | |||
| cd4687cb13 | |||
| b810781368 | |||
| 82f58aa3de | |||
| f57201bbd1 | |||
| 2db6fea665 | |||
| b450a853d5 | |||
| 251da264ed | |||
| b176af2742 | |||
| 1fff96e088 | |||
| d399d2256b | |||
| 27fd702692 | |||
| 5f29288254 | |||
| 7c108dbf48 | |||
| 5c7cb393dc | |||
| 54b37573c9 | |||
| c3a64c2a59 | |||
| a5c31bbee0 | |||
| d6bd56da2c | |||
| 17d31bfad6 | |||
| 89ffd40d70 | |||
| 737b4fde1c | |||
| 2cfd6b7024 | |||
| 713cd728ba | |||
| f9bea5b791 | |||
| 586641d77f | |||
| 8ca4286624 | |||
| 165118c641 | |||
| 852643e6ba | |||
| 102d080a31 | |||
| ec4cae4f04 | |||
| 4153898c55 | |||
| 33df335ec9 | |||
| 893cc013b5 | |||
| b88e945268 | |||
| bf56e3ea8d | |||
| 284acc37f9 | |||
| 582559f7ac | |||
| 53556d9bd2 | |||
| da79124e5d | |||
| 2f34f1283a | |||
| f973d271cf | |||
| 4625b25287 | |||
| 8d8b3fd071 | |||
| a852c17614 | |||
| b33bf3f704 | |||
| 726788a483 | |||
| cdd3560702 | |||
| 6d49df1d87 | |||
| ac191bd31f | |||
| ad3a3547d6 | |||
| aba14c43ac | |||
| 25c3fd48f0 | |||
| e45aa68c14 | |||
| 6dfab34aca | |||
| d3d805ffb3 | |||
| 05b9724c85 | |||
| 5053f0179c | |||
| 3e4d615983 | |||
| aa8777ee47 | |||
| 98dd59e412 | |||
| 7b2f6aaa1c | |||
| ad943f0189 | |||
| 224496dca7 | |||
| 1ef59417ef | |||
| 0d5a160409 | |||
| 82facb387c | |||
| da7a0df7a1 | |||
| fcbd94b924 | |||
| 5bb3912001 | |||
| f4248b159d | |||
| 2e4edcc7e9 | |||
| 0d3272d914 | |||
| 50f3e77b43 | |||
| 964d91b855 | |||
| bdac3ee120 | |||
| b81c670597 | |||
| 4489df2871 | |||
| 766c17a6a3 | |||
| 018f8a82d6 | |||
| d5a0c004b6 | |||
| 0b6e45c9a2 | |||
| 5c41f64829 | |||
| ad56cf0038 | |||
| 58b5e92c2f | |||
| 0a0752db7c | |||
| 49fbdd6188 | |||
| 1048553e78 | |||
| 63282290db | |||
| 7a1498e7dc | |||
| 0d3cbb8883 | |||
| ce7698c20e | |||
| 382ca20916 | |||
| fe2ad54f60 | |||
| b014879159 | |||
| 81623861c0 | |||
| bd86993035 | |||
| 4ccf135892 | |||
| cba0bd30f5 | |||
| 276d4b8f0d | |||
| 7bdbaca938 | |||
| 7f34080b69 | |||
| 9397555c91 | |||
| 7a00ef1879 | |||
| 35204e3cc5 | |||
| daced956e3 | |||
| 5a2d582a09 | |||
| b878be6f79 | |||
| 1f25f60a68 | |||
| 2e921c88fb | |||
| abaf8c127d | |||
| 0c5fa2cdd5 | |||
| 36cb1f868b | |||
| 037cc679c4 | |||
| db6fad7396 | |||
| 07bfb55658 | |||
| 56078ee099 | |||
| ae4a378294 | |||
| 791d7f764b | |||
| b39a4f3e3b | |||
| 389b50f735 | |||
| 2f6e566a32 | |||
| d5bad374d9 | |||
| 445ed9be2b | |||
| 8694a0f68a | |||
| d3fee9d761 | |||
| 36a6805b7c | |||
| 19bf12aa09 | |||
| 0decdaed1a | |||
| 93b5befe45 | |||
| d72f4a8a79 | |||
| 91247a8fe1 | |||
| 18a0fa02c5 | |||
| 932f330a51 | |||
| 6a77fc4cdd | |||
| 01a888fced | |||
| f0437a4242 | |||
| 32bdad322a | |||
| bf130087e7 | |||
| 38e038a1ab | |||
| 6504607adf | |||
| c8378fce95 | |||
| 1aeaec8d5c | |||
| 2aaf927beb | |||
| bb86b0f526 | |||
| 2f5edebefa | |||
| b5cdb331b0 | |||
| fa4f3aa7ad | |||
| a90fe7211c | |||
| ac6cc5191a | |||
| 38841dd46e | |||
| e722af7f61 | |||
| df372dbd5b | |||
| 5c4d95d539 | |||
| 9ae4de2ab8 | |||
| 7845769d4b | |||
| 33239324b8 | |||
| 82a74ebfa9 | |||
| 35026000bb | |||
| 55cf3d26bf | |||
| 23d29be4d8 | |||
| 1ad0743a52 | |||
| 54af47a138 | |||
| b84ee3ab8f | |||
| e023b96737 | |||
| 3fde4616e0 | |||
| 4812006eb8 | |||
| 740995df3d | |||
| aa5c3da414 | |||
| d419d81841 | |||
| 08942714a2 | |||
| 3c443babb2 | |||
| 326e5cd046 | |||
| 79e98b71d3 | |||
| 96605f700b | |||
| 38b5e01343 | |||
| 06cde91ba3 | |||
| 3891d6a483 | |||
| 5fd7a5f32a | |||
| a01c946c20 | |||
| 84b0da44db | |||
| cdb3a5205c | |||
| eae98feb7a | |||
| 3af078e941 | |||
| 48c1f0ab59 | |||
| 19386814b3 | |||
| 706e58c1c7 | |||
| 9ab94650c8 | |||
| fce86ff3d5 | |||
| 0051cd12e2 | |||
| df159dbae7 | |||
| 94d298755a | |||
| 8b49bbb0fc | |||
| 5f072591ba | |||
| e3fef2dade | |||
| 4c306a6f99 | |||
| 0996cc82a7 | |||
| f293d7c997 | |||
| b2097604d5 | |||
| 4e1c9b71f4 | |||
| 3e2788afdc | |||
| 1a7f508dd9 | |||
| dc8cfcf92a | |||
| ac08d37ca0 | |||
| 4035fec784 | |||
| b071a07c86 | |||
| c27b8a71fd | |||
| b8661e0ae0 | |||
| 21cbac755e | |||
| a3a20f09e2 | |||
| d62f189d72 | |||
| 84713b58e0 | |||
| 294c8426e6 | |||
| 7bbc9a4634 | |||
| 1cd3b693ae | |||
| d1b9b06614 | |||
| 19aefa6a40 | |||
| effecf8595 | |||
| d5e3f72a0b | |||
| 9fb486b2ad | |||
| d70fa4329c | |||
| 3f29a0d6dd | |||
| 0db86a0638 | |||
| dc02566a84 | |||
| 0e84e06756 | |||
| e6b2df4b2b | |||
| 37be8d4091 | |||
| a3930cb470 | |||
| ad402726f1 | |||
| 8ff9f2e44f | |||
| 799a56bbcb | |||
| fbbd43a6d8 | |||
| 367bf0c7ae | |||
| f72a0de074 | |||
| 07baae04c9 | |||
| eb22a49b02 | |||
| ee24f2dd37 | |||
| 12f9b6db63 | |||
| 851780b8f4 | |||
| 5f34ff9f9f | |||
| 2b274fefbb | |||
| 6c681b258c | |||
| 1f91a2fe65 | |||
| 19ff27788c | |||
| 1ebb0bd9d6 | |||
| d505a05d36 | |||
| 07dca737f0 | |||
| 6d34a8344a | |||
| 40b7b9ecdf | |||
| 53bf66ce1e | |||
| fd95f50c56 | |||
| 777e17d80f | |||
| d4a03acbc3 | |||
| c3db24f834 | |||
| 62dcccd7ef | |||
| e288a563e1 | |||
| 6298226238 | |||
| 9321616c80 | |||
| 9890e7cfeb | |||
| 4be48973ad | |||
| f8442e0524 | |||
| 9858989b1c | |||
| 511057ca36 | |||
| 35d8976de4 | |||
| 463003e86a | |||
| 85a2a8815e | |||
| 47397a6a48 | |||
| 2323ca1622 | |||
| fc0a92f8ac | |||
| 97d80d03f9 | |||
| 627872db97 | |||
| a27612ec6a | |||
| 491e4ecc74 | |||
| cd0ce7b76e | |||
| 320387c4ee | |||
| 6424a5a8dd | |||
| 06f5a6c785 | |||
| 8b1d8c8453 | |||
| 0028cb0258 | |||
| b15ee059ad | |||
| 50feeaa285 | |||
| e80ac18324 | |||
| db0196abc7 | |||
| 149c7b88df | |||
| 7afd1b15a8 | |||
| f12672727d | |||
| 2179cc2bc7 | |||
| 961adfedd9 | |||
| 385b9c9922 | |||
| 922df6a438 | |||
| c1a001c331 | |||
| 13fab36639 | |||
| 708389a7ee | |||
| fb6c9af1ae | |||
| 85aa4dd670 | |||
| 74258a170a | |||
| 2fbff25a18 | |||
| 9c150381bf | |||
| 531c4936dd | |||
| 2aae1016ab | |||
| 9c2468ecf7 | |||
| 0a431eef19 | |||
| 9646d1f2b8 | |||
| 5f7aeb3045 | |||
| 1ebaa9fb3b | |||
| c2255c6c19 | |||
| 2884fa5506 | |||
| 31c94bd7f8 | |||
| 5f63e3952e | |||
| d953e40fb3 | |||
| c3a4d7dda2 | |||
| b4a29844e9 | |||
| 0254057f1b | |||
| f34ba44bf8 | |||
| d4d863c4db | |||
| 78c0877994 | |||
| 1d0b37c1d8 | |||
| 7648f2e655 | |||
| d9efa6c8b5 | |||
| 2e372b33a3 | |||
| 687c211a58 | |||
| 4eadd0867d | |||
| 9dba17cf87 | |||
| baef0c291d | |||
| cccd290834 | |||
| 5af85aed3d | |||
| e600e5947b | |||
| d7ae611f76 | |||
| 04a83eb9f7 | |||
| 341e8a3c99 | |||
| e3d8ac559d | |||
| 767948ab46 | |||
| dbcd8da733 | |||
| 905d5459a9 | |||
| dfd5a993a2 | |||
| daceaa65f5 | |||
| c2c07f07db | |||
| c5cbd3260a | |||
| d57e1be89f | |||
| 18dda6ff9d | |||
| 7afc367275 | |||
| c465171b45 | |||
| 02f22a0b3f | |||
| 193fd0eecb | |||
| 01f431b01f | |||
| aebfa4b28e | |||
| a3abe991a4 | |||
| e793f92e67 | |||
| 042f376626 | |||
| d99ed692ba | |||
| 4d00c2b800 | |||
| a5605e94b1 | |||
| 9cfe20cfb4 | |||
| db3501f61b | |||
| 48538aa792 | |||
| 5fd64f48ee | |||
| a3b7126875 | |||
| 5756c8a7c6 | |||
| cdd0061869 | |||
| e6adfd8054 | |||
| 6d59223efd | |||
| ebd034fff9 | |||
| 000bda8419 | |||
| 423b842347 | |||
| 16eeae36d7 | |||
| 5453e92bcb | |||
| c16c9535d9 | |||
| 662c7b1e71 | |||
| 571170fd30 | |||
| 911c428dac | |||
| 40822be595 | |||
| f5e51a29b5 | |||
| beaad719ad | |||
| 0d257c61cd | |||
| dd8fa1d690 | |||
| af6df7d7c9 | |||
| 57a17a708b | |||
| 1f79082921 | |||
| afff9478c8 | |||
| b352b42afc | |||
| c3e4509d17 | |||
| 664be28941 | |||
| d7e970ac32 | |||
| 810dcfc602 | |||
| 14645b147f | |||
| b036cc9013 | |||
| 3a8ac92995 | |||
| 67bedcba4b | |||
| 4987ef89f1 | |||
| 545b345eea | |||
| 3e961af5f1 | |||
| 9e6e769141 | |||
| c0f746a251 | |||
| ccbdf086ff | |||
| d260ca6738 | |||
| b0e64ca7e8 | |||
| 7f3d4cb504 | |||
| 97be36d18a | |||
| be76da2c90 | |||
| b1a8357f50 | |||
| 0dc900ba34 | |||
| 700624119b | |||
| af32aba7fc | |||
| 541c79c01a | |||
| 358226468d | |||
| d1e5f34f76 | |||
| 416dd93bf7 | |||
| 7c80577160 | |||
| a5d6fda433 | |||
| 4bc3796f9b | |||
| 5e12f5a746 | |||
| 4080ba4026 | |||
| e17593be94 | |||
| 44aa69748c | |||
| 9cd5ea59dd | |||
| 116e98b378 | |||
| ae25787f48 | |||
| 08dc829b70 | |||
| 3ac674cf02 | |||
| 6cd06ab95f | |||
| 99080d41f3 | |||
| 92f3567ee6 | |||
| 481fc8a5b6 | |||
| 5dfff51a40 | |||
| 2938ec028f | |||
| 981b9fb7a8 | |||
| 10f4d8df32 | |||
| e15858e2be | |||
| 6d8302825c | |||
| fae8bf96dd | |||
| bc59dc6389 | |||
| bd8e2f82be | |||
| 69027b6840 | |||
| 7a9b7af078 | |||
| 31c7afce1b | |||
| bab1873416 | |||
| 5e2950e9fb | |||
| 74c019f271 | |||
| a745bb8f42 | |||
| da9870c77d | |||
| 698eeaf7c3 | |||
| bd954d9990 | |||
| 4ded42a33b | |||
| 1877965ac3 | |||
| cec2309504 | |||
| ed5a4afc8c | |||
| f29b51efdd | |||
| 3930a32b0c | |||
| 76707e1d2f | |||
| ff2e5c3efe | |||
| 3600b2e209 | |||
| eb36a0f3b1 | |||
| b397254696 | |||
| 9a37d3b6e5 | |||
| 51c4a19234 | |||
| a1a1668dcf | |||
| e308098f18 | |||
| 63b572a0ab | |||
| e90e7d4af9 | |||
| 460e14e586 | |||
| ebcabb8a27 | |||
| 257b749e9d | |||
| d9e6ade030 | |||
| 690374d4de | |||
| adb25d9d19 | |||
| 2949e4b0c7 | |||
| 6de0181c75 | |||
| 8a6b743d2a | |||
| ed8a6f872d | |||
| 0a61d4bf2b | |||
| 332461c0d2 | |||
| d8abe7fc4d | |||
| ec9667ef5a | |||
| 9f7154a039 | |||
| 4a932616fc | |||
| 73fe36f19c | |||
| 4167ae95ae | |||
| 13c3c5be95 | |||
| 3afec0bcbe | |||
| 8b1bfc80fb | |||
| 3662275119 | |||
| 141573c18c | |||
| e692efbe09 | |||
| f6c1a5bf6e | |||
| 6afc28f827 | |||
| df6681ad4e | |||
| 114efbc57c | |||
| 2c71494ad1 | |||
| 35230b1a11 | |||
| 7acbf8b7b9 | |||
| f7413ca974 | |||
| 9b3a0251ca | |||
| 3b9ca8535f | |||
| a98eee145d | |||
| c6b9b3b9a4 | |||
| a4be1a5e4c | |||
| ee908c00de | |||
| 46264032aa | |||
| 4b831dbddd | |||
| a01d6dcea7 | |||
| 01a15f78e6 | |||
| 5f71c41582 | |||
| e8f2c5a48a | |||
| 1c76577918 | |||
| 4285b58aaa | |||
| 9cc366de97 | |||
| 212d294b84 | |||
| d6125f05d4 | |||
| ad22eda87c | |||
| 8d31ba492d | |||
| 8f611b3399 | |||
| 618f858930 | |||
| 185277a2b6 | |||
| 8e5c3141f6 | |||
| 87aaa9c3f0 | |||
| 095605b7db | |||
| 6836b501af | |||
| d949d5a046 | |||
| 064516bf0b | |||
| eaf88bcc4d | |||
| 76d3bab955 | |||
| 02f9668185 | |||
| abda75feee | |||
| a779cb2798 | |||
| 5e42675b42 | |||
| 11c565e199 | |||
| 4689a6b341 | |||
| 30a5808460 | |||
| 8ba1c9a6cd | |||
| 65b78b1aa9 | |||
| c369719362 | |||
| 40a12cca53 | |||
| d8940f5fd7 | |||
| 74dee82dbf | |||
| f1cf724bd4 | |||
| fc809d3fad | |||
| 42d135aade | |||
| ab12ca69af | |||
| 17748f18b9 | |||
| 3803bdff5f | |||
| 0a94a67190 | |||
| 8dcb14fbc2 | |||
| f62f40e508 | |||
| 980aa37bee | |||
| 1e9cc058a0 | |||
| 824efc82b9 | |||
| 1cfe226686 | |||
| ea0ce7bd2c | |||
| 342adb627d | |||
| a70635d2f6 | |||
| 5cb16c4cd1 | |||
| 538004ba5f | |||
| 8663c64e47 | |||
| bb3ffdbdfb | |||
| fcf7cf973b | |||
| fb2e1c32bd | |||
| ae0b8fce44 | |||
| d2e55f9ffa | |||
| d5e2b387fa | |||
| 922b511a24 | |||
| 9de9111082 | |||
| 5a7fe2e3d9 | |||
| 1e8c12c2a3 | |||
| b92c70e55c | |||
| 352494cbb4 | |||
| 797c41950a | |||
| e036a321a0 | |||
| 18471f358e | |||
| 55d09251d8 | |||
| d66fd71d21 | |||
| f10fc2a494 | |||
| 055d8c5370 | |||
| 955472bd21 | |||
| a2d5f660ed | |||
| 8a4b9ddaa3 | |||
| 54a2b6f00e | |||
| abe728dbbb | |||
| 574ceb37a9 | |||
| dbdc4471c2 | |||
| af95381bf8 | |||
| 41f2539484 | |||
| 8f57bb0799 | |||
| 84a4188c72 | |||
| 31f1e0aeae | |||
| 4b8cbb5a3b | |||
| 3e7aabe6d8 | |||
| d4df325e0a | |||
| 9f8a63cb43 | |||
| 6f745677b4 | |||
| 86649e6b44 | |||
| 3ca887a60a | |||
| 312a1e8a94 | |||
| 9a311ab9e7 | |||
| 7d92eaaeb2 | |||
| 582f423b67 | |||
| c2524464f9 | |||
| 55e472cdb6 | |||
| e6ad773a88 | |||
| 82eae32bca | |||
| b0e21bd616 | |||
| 7b88bed098 | |||
| 28abf5d33b | |||
| fb8ad338d0 | |||
| ee60be0137 | |||
| 9efd4c5097 | |||
| f30e6f50bd | |||
| 8ec2ca9dcd | |||
| f19bb0f4d4 | |||
| 76bb82a726 | |||
| 8e62f9c83c | |||
| 696f758435 | |||
| 6421152104 | |||
| 1e989f5c10 | |||
| 3eb42ecb55 | |||
| 286cb60c45 | |||
| 28e9a4f79c | |||
| e79fb68291 | |||
| 16eaa17ed9 | |||
| 9491c13a5c | |||
| 8d8adac1b4 | |||
| ba6c63e366 | |||
| 963b1a12d7 | |||
| 89890d7900 | |||
| 661fcad895 | |||
| cef16feb0b | |||
| 84b851f578 | |||
| 785c27daa7 | |||
| 795a5910cf | |||
| 4c21f9495d | |||
| f119fd1ee1 | |||
| 46ab822316 | |||
| 104aea9f42 | |||
| a2f2041aa6 | |||
| 599d18f26f | |||
| 61084d832d | |||
| b957996577 | |||
| f41150f8e9 | |||
| 21adad4e09 | |||
| 47f9e8f850 | |||
| ba3f2a6a0c | |||
| b9888f8f68 | |||
| 32cda5d56a | |||
| a4d9847f45 | |||
| 8aa6d70dec | |||
| ea6b222430 | |||
| 91b4bf3daa | |||
| 8c943eb054 | |||
| 446703ba75 | |||
| 266b5d7d85 | |||
| d90adb7a8e | |||
| 2573da12fc | |||
| bbc9c6a93c | |||
| 673134185a | |||
| 3bf00e6125 | |||
| 8fd4fe25d6 | |||
| ea1dc5373d | |||
| b8d6dd4eb7 | |||
| 4d80f8598d | |||
| bc4591fc1e | |||
| 6465fb8ec7 | |||
| 46204ed7f0 | |||
| 0f91ffe28f | |||
| 4b46d691f8 | |||
| 599d0cf6ac | |||
| d7dba495fd | |||
| c05f6b4a31 | |||
| a0a578c72a | |||
| 55fc2c00c0 | |||
| 6944e2fc04 | |||
| c8b353b6d8 | |||
| 6a8a31824d | |||
| 275b8d2e8a | |||
| 8cfdd9805d | |||
| 7d304f4e8b | |||
| b65e5d5e03 | |||
| b110153b51 | |||
| b261937233 | |||
| 4455ba5b65 | |||
| e8aa767c8d | |||
| bb7bd94b0a | |||
| 4a886a1bc5 | |||
| c21c754b6f | |||
| e948fcd2f1 | |||
| 0c46d06e63 | |||
| 44b6e7c45d | |||
| 284a2631fd | |||
| 3f156bcb4b | |||
| d35a9e65b6 | |||
| c440296ae8 | |||
| 79f115d6a7 | |||
| 39eccdf6b9 | |||
| 71cfa0c9fe | |||
| e82f8214e8 | |||
| b8f8d4c3a1 | |||
| 9e2eb717fe | |||
| f0c9f82e1b | |||
| cc1d3a7b19 | |||
| 159daba759 | |||
| d7e85725e1 | |||
| 247f4f3293 | |||
| 8758afdf4e | |||
| 362f07d7c5 | |||
| fe74b479c6 | |||
| 44ee0f2cdc | |||
| 50035f257e | |||
| 92abddddc5 | |||
| 4d580ed693 | |||
| 9c54ed5792 | |||
| 5108ebf015 | |||
| 86dbcb1b20 | |||
| 3ac934dd15 | |||
| 26ce40c188 | |||
| b34e4a9fd1 | |||
| 28561ef5f5 | |||
| 10b798456c | |||
| a580cdbe7b | |||
| e82e4398b6 | |||
| e0225c3579 | |||
| 8d044fd442 | |||
| 6ef96d3300 | |||
| 2e633f57a0 | |||
| 50369cf19c | |||
| 385385364b | |||
| 9ccdc40f65 | |||
| df3512b112 | |||
| ce08e832f7 | |||
| 24c89b3eee | |||
| 85e5be03d1 | |||
| b1cfeb6c95 | |||
| fffb83282b | |||
| 1cf6b6679d | |||
| 7886798156 | |||
| 0ad599675e | |||
| 5d03bc61b8 | |||
| 5e31c04a01 | |||
| 7b7081d607 | |||
| 5ae564cc8c | |||
| 0507b9ebb7 | |||
| f7b1290fe9 | |||
| b01c2e1017 | |||
| 63b54ce7c6 | |||
| b8a35c658c | |||
| 465b6139d5 | |||
| b37d344eb2 | |||
| d4ee1a9e19 | |||
| be12f12c3c | |||
| 2e5f57d8a1 | |||
| d5c3f99655 | |||
| 083e9e2053 | |||
| c6c5af527b | |||
| 5420cc083d | |||
| c81f7b31c6 | |||
| fdd0c726bc | |||
| da9cabc334 | |||
| d962cfdc6b | |||
| dfb8143149 | |||
| d2fcbf0e1d | |||
| e08f39ec28 | |||
| d353f97f91 | |||
| 8144e82c6c | |||
| d0f7cf74e9 | |||
| e1c2528d87 | |||
| eac61fb536 | |||
| 20f7e61363 | |||
| 8bb6651dda | |||
| 8ff80dec58 | |||
| f31b09212a | |||
| 073bd3f6c9 | |||
| 9a4d942b0b | |||
| ff6fdc7812 | |||
| c85c5ec38c | |||
| d70b5475eb | |||
| 42138d1ad5 | |||
| 47f97b8f89 | |||
| 902be0d09c | |||
| 502ad0c10b | |||
| 27f5aadd5a | |||
| da7a9a3584 | |||
| 03862d1f48 | |||
| 15ce8b0454 | |||
| 4f51d74fc2 | |||
| b6a1ec0229 | |||
| 60685369b9 | |||
| 0f2a9e6143 | |||
| 278de3a92f | |||
| abdf54800e | |||
| 579b317be8 | |||
| 08360e2337 | |||
| f11f84964d | |||
| bb9c1d04db | |||
| f3c4d97250 | |||
| afb6ebd21e | |||
| 6fa5eb9e1f | |||
| 94570a24c1 | |||
| 17001bf38c | |||
| b83e67f45f | |||
| a5eea97edb | |||
| 866d5302fe | |||
| 5d70289b69 | |||
| d76ba7895b | |||
| dfe1dedeb1 | |||
| d027a5f1a4 | |||
| 746e880eb5 | |||
| e4286d0ff9 | |||
| 3a556eb304 | |||
| b44a56118e | |||
| 0d044eabac | |||
| 3cf26af600 | |||
| 9a39ce6b75 | |||
| 3a5245dcce | |||
| ca0f36be42 | |||
| 59b3c472ca | |||
| 357f66fcee | |||
| 050f128554 | |||
| 1a3784c4df | |||
| 518ecaf9c9 | |||
| 77e2e1e1d0 | |||
| 448f834b28 | |||
| 9212e3176a | |||
| 3160a6a12c | |||
| ef7b72d14c | |||
| 7d534769d6 | |||
| 0a81fcc2af | |||
| 2451014b6e | |||
| 348d25c43a | |||
| 9854dbe889 | |||
| 038337fd07 | |||
| cf0bf1a7cb | |||
| 364e6c9573 | |||
| 52d38ae42b | |||
| 2dbdff07c5 | |||
| 2ad8f99790 | |||
| 87a1833862 | |||
| 0a0ba95e85 | |||
| 433d742183 | |||
| b845db1618 | |||
| ced7700cdb | |||
| 406d2ab6ba | |||
| 327116b84b | |||
| fd56b48825 | |||
| 1ef9b094b6 | |||
| 0659a11a1a | |||
| 956b59d48c | |||
| 97b537f800 | |||
| ae907bf724 | |||
| db1ef078ff | |||
| c2603297ee | |||
| a02bcd3bfd | |||
| 0e972296a4 | |||
| 5ec82623ab | |||
| 47ff50a92d | |||
| a1558b3398 | |||
| 5f617c56e1 | |||
| 7ef30355cb | |||
| 06f1eaa153 | |||
| 58e023e0cf | |||
| 222bab501c | |||
| 9c6ab05981 | |||
| bee637aef1 | |||
| d5844dfd2a | |||
| 3751322521 | |||
| 71ac145f49 | |||
| e50ef33c2a | |||
| 3061dd2497 | |||
| 088f4b5039 | |||
| 002184cd49 | |||
| 02da4d61ae | |||
| 102c29575b | |||
| 24fce21d90 | |||
| afe185ee22 | |||
| ffffb04d85 | |||
| 17390cd317 | |||
| 7ac80544a1 | |||
| 96b60d8779 | |||
| 24f2d97c0e | |||
| f6d98d2a16 | |||
| ab5460e2e2 | |||
| cd1658e56c | |||
| e7e1866e50 | |||
| 51b198aa56 | |||
| 94fd59e6f0 | |||
| c07ab5f9ad | |||
| 06e0da97b7 | |||
| 04535b2913 | |||
| 6667b78c12 | |||
| 7e0c78eae7 | |||
| 6d1eb473cf | |||
| 898245431f | |||
| 43942a6199 | |||
| 53c7e65c57 | |||
| a38586420c | |||
| 1aa9c49172 | |||
| 5a88fef483 | |||
| 43b55223af | |||
| 1d628a5e3d | |||
| af3784ebb6 | |||
| ff933ca37f | |||
| bbf79fc1d4 | |||
| 2af8e956a6 | |||
| 9ed826f6fb | |||
| 07f9e551c1 | |||
| df4404e093 | |||
| 583fd89fcf | |||
| 2691c394e0 | |||
| 4662bad59c | |||
| 7086b49ae6 | |||
| 72aa0e6e38 | |||
| cb2e6ea31d | |||
| 74c683e908 | |||
| 6383714bff | |||
| a25e796b00 | |||
| 833784e196 | |||
| 66a70b9d27 | |||
| cd125c0f41 | |||
| 87bfd31664 | |||
| 5fba548aa0 | |||
| aafbf5bac7 | |||
| 951e940470 | |||
| 3b5b845ea6 | |||
| 3f9ad6ba09 | |||
| f10f7ebbf1 | |||
| 40ed7e48d0 | |||
| ab53900c95 | |||
| 43589a56b7 | |||
| de1838c0cb | |||
| 9b8563dfc0 | |||
| b18e8898d8 | |||
| ca112c0b6d | |||
| 6eb64f2bdc | |||
| 25c3b6dc95 | |||
| 3464784e5e | |||
| f1dc7c0b27 | |||
| c708f649ec | |||
| f7b8b3a340 | |||
| 2cbb3443d3 | |||
| 11bd518f36 | |||
| a721802337 | |||
| 5a8c7b4f90 | |||
| f17fc05ff2 | |||
| a12db382e0 | |||
| 22daa50374 | |||
| a09300fb0e | |||
| bab4e5531a | |||
| 1de928df78 | |||
| aded272b33 | |||
| b0721f1e0c | |||
| 7e90ece84a | |||
| 1f563f2810 | |||
| c46317c00b | |||
| cb4b7efd3e | |||
| 411beaa3bf | |||
| 1cb3a31f32 | |||
| c99afd04ad | |||
| 3157467e4b | |||
| 2001384ae6 | |||
| b09e53d7fe | |||
| d046c9c072 | |||
| 2e8da35851 | |||
| 1d3458cadb | |||
| 9139ca4673 | |||
| e0e6bba865 |
@@ -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
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# PLEASE POST THIS IN THE DISCUSSION TAB UNDER "PROPOSALS" OR "IDEAS/REQUESTS"
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
|
||||
+110
-31
@@ -6,59 +6,138 @@ 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 llvm libbotan-2-dev botan
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin run -llvm-api
|
||||
run: ./odin run examples/demo/demo.odin -llvm-api
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
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
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo -debug
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all
|
||||
run: ./odin check examples/all -strict-style
|
||||
timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
run: |
|
||||
cd tests/core
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Vendor library tests
|
||||
run: |
|
||||
cd tests/vendor
|
||||
make
|
||||
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
|
||||
echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH
|
||||
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
|
||||
- name: Odin run
|
||||
run: |
|
||||
./odin run examples/demo/demo.odin
|
||||
./odin run examples/demo/demo.odin -llvm-api
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
run: ./odin check examples/demo -vet
|
||||
timeout-minutes: 10
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo -debug
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all
|
||||
run: ./odin check examples/all -strict-style
|
||||
timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
run: |
|
||||
cd tests/core
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Vendor library tests
|
||||
run: |
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download and unpack LLVM bins
|
||||
shell: powershell
|
||||
run: |
|
||||
cd bin
|
||||
$ProgressPreference = "SilentlyContinue";
|
||||
Invoke-WebRequest -Uri https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip -OutFile llvm-binaries.zip
|
||||
7z x llvm-binaries.zip > $null
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
./build.bat 1
|
||||
- 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
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
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
|
||||
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 -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
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
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: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\core\math\big
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
|
||||
@@ -7,18 +7,9 @@ on:
|
||||
|
||||
jobs:
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install cURL
|
||||
run: choco install curl
|
||||
- name: Download and unpack LLVM bins
|
||||
shell: cmd
|
||||
run: |
|
||||
cd bin
|
||||
curl -sL https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip --output llvm-binaries.zip
|
||||
7z x llvm-binaries.zip > nul
|
||||
rm -f llvm-binaries.zip
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -37,6 +28,7 @@ jobs:
|
||||
cp LLVM-C.dll dist
|
||||
cp -r shared dist
|
||||
cp -r core dist
|
||||
cp -r vendor dist
|
||||
cp -r bin dist
|
||||
cp -r examples dist
|
||||
- name: Upload artifact
|
||||
@@ -52,8 +44,6 @@ jobs:
|
||||
run: sudo apt-get install llvm-11 clang-11 llvm
|
||||
- name: build odin
|
||||
run: make nightly
|
||||
- name: Odin run -llvm-api
|
||||
run: ./odin run examples/demo/demo.odin -llvm-api
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Copy artifacts
|
||||
@@ -62,6 +52,7 @@ jobs:
|
||||
cp odin dist
|
||||
cp -r shared dist
|
||||
cp -r core dist
|
||||
cp -r vendor dist
|
||||
cp -r examples dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
@@ -74,14 +65,12 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm
|
||||
echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH
|
||||
brew install llvm@11
|
||||
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 nightly
|
||||
- name: Odin run -llvm-api
|
||||
run: ./odin run examples/demo/demo.odin -llvm-api
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Copy artifacts
|
||||
@@ -90,6 +79,7 @@ jobs:
|
||||
cp odin dist
|
||||
cp -r shared dist
|
||||
cp -r core dist
|
||||
cp -r vendor dist
|
||||
cp -r examples dist
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
@@ -103,7 +93,7 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
python-version: '3.8.x'
|
||||
|
||||
- name: Install B2 CLI
|
||||
shell: bash
|
||||
@@ -137,16 +127,23 @@ jobs:
|
||||
BUCKET: ${{ secrets.B2_BUCKET }}
|
||||
DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
|
||||
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/
|
||||
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/
|
||||
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/
|
||||
|
||||
echo Deleting old artifacts in B2
|
||||
python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
|
||||
|
||||
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
|
||||
|
||||
echo Clear B2 account info
|
||||
b2 clear-account
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# For macOS
|
||||
.DS_Store
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
Copyright (c) 2016-2020 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2021 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:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
1. 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.
|
||||
2. 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.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
|
||||
BIN
Binary file not shown.
@@ -1,19 +1,66 @@
|
||||
GIT_SHA=$(shell git rev-parse --short HEAD)
|
||||
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined
|
||||
LDFLAGS=-pthread -ldl -lm -lstdc++
|
||||
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-$(shell date +"%Y-%m")\"
|
||||
CC=clang
|
||||
|
||||
OS=$(shell uname)
|
||||
|
||||
ifeq ($(OS), Darwin)
|
||||
LDFLAGS:=$(LDFLAGS) -liconv
|
||||
CFLAGS:=$(CFLAGS) $(shell llvm-config --cxxflags --ldflags) -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
|
||||
LDFLAGS:=$(LDFLAGS) -lLLVM-C
|
||||
|
||||
ARCH=$(shell uname -m)
|
||||
LLVM_CONFIG=llvm-config
|
||||
|
||||
# allow for arm only llvm's with version 13
|
||||
ifeq ($(ARCH), arm64)
|
||||
LLVM_VERSIONS = "13.%.%"
|
||||
else
|
||||
# allow for x86 / amd64 all llvm versions begining from 11
|
||||
LLVM_VERSIONS = "13.%.%" "12.0.1" "11.1.0"
|
||||
endif
|
||||
|
||||
LLVM_VERSION_PATTERN_SEPERATOR = )|(
|
||||
LLVM_VERSION_PATTERNS_ESCAPED_DOT = $(subst .,\.,$(LLVM_VERSIONS))
|
||||
LLVM_VERSION_PATTERNS_REPLACE_PERCENT = $(subst %,.*,$(LLVM_VERSION_PATTERNS_ESCAPED_DOT))
|
||||
LLVM_VERSION_PATTERN_REMOVE_ELEMENTS = $(subst " ",$(LLVM_VERSION_PATTERN_SEPERATOR),$(LLVM_VERSION_PATTERNS_REPLACE_PERCENT))
|
||||
LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR = $(subst ",,$(LLVM_VERSION_PATTERN_REMOVE_ELEMENTS))
|
||||
LLVM_VERSION_PATTERN = "^(($(LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR)))"
|
||||
|
||||
ifeq ($(shell $(LLVM_CONFIG) --version | grep -E $(LLVM_VERSION_PATTERN)),)
|
||||
ifeq ($(ARCH), arm64)
|
||||
$(error "Requirement: llvm-config must be base version 13 for arm64")
|
||||
else
|
||||
$(error "Requirement: llvm-config must be base version greater than 11 for amd64/x86")
|
||||
endif
|
||||
endif
|
||||
|
||||
LDFLAGS:=$(LDFLAGS) -liconv -ldl
|
||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||
LDFLAGS:=$(LDFLAGS) -lLLVM-C
|
||||
endif
|
||||
ifeq ($(OS), Linux)
|
||||
CFLAGS:=$(CFLAGS) $(shell llvm-config-11 --cxxflags --ldflags) -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
|
||||
LDFLAGS:=$(LDFLAGS) $(shell llvm-config-11 --libs core native --system-libs)
|
||||
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
|
||||
ifeq ($(shell $(LLVM_CONFIG) --version | grep '^11\.'),)
|
||||
$(error "Requirement: llvm-config must be version 11")
|
||||
endif
|
||||
endif
|
||||
|
||||
LDFLAGS:=$(LDFLAGS) -ldl
|
||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
|
||||
endif
|
||||
ifeq ($(OS), OpenBSD)
|
||||
LLVM_CONFIG=/usr/local/bin/llvm-config
|
||||
|
||||
LDFLAGS:=$(LDFLAGS) -liconv
|
||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
|
||||
endif
|
||||
|
||||
all: debug demo
|
||||
@@ -21,14 +68,17 @@ all: debug demo
|
||||
demo:
|
||||
./odin run examples/demo/demo.odin
|
||||
|
||||
report:
|
||||
./odin report
|
||||
|
||||
debug:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
|
||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
|
||||
|
||||
release:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
|
||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 $(LDFLAGS) -o odin
|
||||
|
||||
release_native:
|
||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
|
||||
|
||||
nightly:
|
||||
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
|
||||
|
||||
|
||||
|
||||
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img src="misc/logo-slim.png" alt="Odin logo" height="120">
|
||||
<img src="misc/logo-slim.png" alt="Odin logo" style="width:65%">
|
||||
<br/>
|
||||
A fast, concise, readable, pragmatic and open sourced programming language.
|
||||
The Data-Oriented Language for Sane Software Development.
|
||||
<br/>
|
||||
<br/>
|
||||
<a href="https://github.com/odin-lang/odin/releases/latest">
|
||||
@@ -21,11 +21,8 @@
|
||||
|
||||
# The Odin Programming Language
|
||||
|
||||
The Odin programming language is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of creating an alternative to C with the following goals:
|
||||
* simplicity
|
||||
* high performance
|
||||
* built for modern systems
|
||||
* joy of programming
|
||||
|
||||
Odin is a general-purpose programming language with distinct typing, built for high performance, modern systems, and built-in data-oriented data types. The Odin Programming Language, the C alternative for the joy of programming.
|
||||
|
||||
Website: [https://odin-lang.org/](https://odin-lang.org/)
|
||||
|
||||
@@ -35,27 +32,26 @@ package main
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
program := "+ + * 😃 - /";
|
||||
accumulator := 0;
|
||||
program := "+ + * 😃 - /"
|
||||
accumulator := 0
|
||||
|
||||
for token in program {
|
||||
switch token {
|
||||
case '+': accumulator += 1;
|
||||
case '-': accumulator -= 1;
|
||||
case '*': accumulator *= 2;
|
||||
case '/': accumulator /= 2;
|
||||
case '😃': accumulator *= accumulator;
|
||||
case '+': accumulator += 1
|
||||
case '-': accumulator -= 1
|
||||
case '*': accumulator *= 2
|
||||
case '/': accumulator /= 2
|
||||
case '😃': accumulator *= accumulator
|
||||
case: // Ignore everything else
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printf("The program \"%s\" calculates the value %d\n",
|
||||
program, accumulator);
|
||||
program, accumulator)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
#### [Getting Started](https://odin-lang.org/docs/install)
|
||||
@@ -82,92 +78,16 @@ Get live support and talk with other odiners on the Odin Discord.
|
||||
|
||||
### References
|
||||
|
||||
#### [Language Specification](https://odin-lang.org/ref/spec)
|
||||
#### [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.
|
||||
|
||||
## Setup
|
||||
|
||||
Odin only supports x86-64/amd64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
|
||||
In addition, the following platform-specific steps are necessary:
|
||||
|
||||
- Windows
|
||||
* Have Visual Studio installed (MSVC 2010 or later, for the linker)
|
||||
* Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied
|
||||
* Open a valid command prompt:
|
||||
* **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or
|
||||
* **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session
|
||||
|
||||
- MacOS
|
||||
* Have LLVM explicitly installed (`brew install llvm`)
|
||||
* Have XCode installed (version X.X or later, for linking)
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
- GNU/Linux
|
||||
* Have LLVM installed (opt/llc)
|
||||
* Have Clang installed (version X.X or later, for linking)
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
Then build the compiler by calling `build.bat` (Windows) or `make` (Linux/MacOS). This will automatically run the demo program if successful.
|
||||
|
||||
**Notes for Linux:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or add `/path/to/odin` to `$PATH`.
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin) for more information.
|
||||
|
||||
## Requirements to build and run
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin).
|
||||
|
||||
- Windows
|
||||
* x86-64/amd64
|
||||
* MSVC 2010 installed (C++11 support)
|
||||
* [LLVM binaries](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) for `opt.exe`, `llc.exe`, and `lld-link.exe`
|
||||
* Requires MSVC's link.exe as the linker
|
||||
* run `vcvarsall.bat` to setup the path
|
||||
|
||||
- MacOS
|
||||
* x86-64/amd64
|
||||
* LLVM explicitly installed (`brew install llvm`)
|
||||
* XCode installed (for the linker)
|
||||
|
||||
- GNU/Linux
|
||||
* x86-64/amd64
|
||||
* Build tools (ld)
|
||||
* LLVM installed
|
||||
* Clang installed (temporary - this is Calling the linker for now)
|
||||
|
||||
- FreeBSD
|
||||
* x86-64/amd64
|
||||
* Build tools (ld)
|
||||
* LLVM installed
|
||||
* Clang installed (temporary - this is Calling the linker for now)
|
||||
|
||||
Other platforms may be supported but are experimental for the time being.
|
||||
|
||||
## Warnings
|
||||
|
||||
* The Odin compiler is still in development.
|
||||
|
||||
## Demonstrations:
|
||||
* First Talk & Demo
|
||||
- [Talk](https://youtu.be/TMCkT-uASaE?t=338)
|
||||
- [Demo](https://youtu.be/TMCkT-uASaE?t=1800)
|
||||
- [Q&A](https://youtu.be/TMCkT-uASaE?t=5749)
|
||||
* [Composition & Refactorability](https://www.youtube.com/watch?v=n1wemZfcbXM)
|
||||
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
|
||||
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
|
||||
* [when, for & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [Context Types, Unexported Entities, Labelled Branches](https://www.youtube.com/watch?v=CkHVwT1Qk-g)
|
||||
* [Bit Fields, i128 & u128, Syntax Changes](https://www.youtube.com/watch?v=NlTutcLyF64)
|
||||
* [Default and Named Arguments; Explicit Parametric Polymorphism](https://www.youtube.com/watch?v=-XQZE6S6zUU)
|
||||
* [Loadsachanges](https://www.youtube.com/watch?v=ar0vFMoMtrI)
|
||||
* [Packages, Bit Sets, cstring](https://youtu.be/b8bJbjiXZrQ)
|
||||
- [Q&A](https://youtu.be/5jmxyIfyyTk)
|
||||
|
||||
|
||||
+3
-4
@@ -2,13 +2,12 @@
|
||||
|
||||
## Setup
|
||||
|
||||
Odin only supports x86-64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
Odin currently supports x86-64 and ARM64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
|
||||
In addition, the following platform-specific steps are necessary:
|
||||
|
||||
- Windows
|
||||
* Have Visual Studio installed (MSVC 2010 or later, for the linker)
|
||||
* Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied
|
||||
* Open a valid command prompt:
|
||||
* **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or
|
||||
* **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session
|
||||
@@ -19,12 +18,12 @@ In addition, the following platform-specific steps are necessary:
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
- GNU/Linux
|
||||
* Have LLVM installed (opt/llc)
|
||||
* Have Clang installed (version X.X or later, for linking)
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
Then build the compiler by calling `build.bat` (Windows) or `make` (Linux/MacOS). This will automatically run the demo program if successful.
|
||||
|
||||
**Notes for Linux:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or add `/path/to/odin` to `$PATH`.
|
||||
**Notes for \*Nix Systems:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable, by default. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to either simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or `set ODIN_ROOT=/path/to/odin_root`.
|
||||
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin) for more information.
|
||||
|
||||
Binary file not shown.
@@ -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.
-37
@@ -1,37 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
release_mode=$1
|
||||
|
||||
warnings_to_disable="-std=c++11 -Wno-switch"
|
||||
|
||||
libraries="-pthread -ldl -lm -lstdc++ -lz -lcurses -lxml2"
|
||||
other_args="-DLLVM_BACKEND_SUPPORT"
|
||||
compiler="clang"
|
||||
|
||||
if [ -z "$release_mode" ]; then release_mode="0"; fi
|
||||
|
||||
if [ "$release_mode" -eq "0" ]; then
|
||||
other_args="${other_args} -g"
|
||||
fi
|
||||
if [ "$release_mode" -eq "1" ]; then
|
||||
other_args="${other_args} -O3 -march=native"
|
||||
fi
|
||||
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
|
||||
# Set compiler to clang on MacOS
|
||||
# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
|
||||
compiler="clang"
|
||||
|
||||
llvm_config_flags="--cxxflags --ldflags"
|
||||
# llvm_config_flags="${llvm_config_flags} --link-static"
|
||||
llvm_config="llvm-config ${llvm_config_flags}"
|
||||
|
||||
other_args="${other_args} -liconv"
|
||||
other_args="${other_args} `${llvm_config}` -lLLVM-C"
|
||||
elif [[ "$(uname)" == "FreeBSD" ]]; then
|
||||
compiler="clang"
|
||||
fi
|
||||
|
||||
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin
|
||||
# && ./odin run examples/demo/demo.odin -llvm-api
|
||||
@@ -1,5 +1,14 @@
|
||||
@echo off
|
||||
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
||||
set CURR_DATE_TIME=%%j
|
||||
)
|
||||
|
||||
set curr_year=%CURR_DATE_TIME:~0,4%
|
||||
set curr_month=%CURR_DATE_TIME:~4,2%
|
||||
|
||||
:: Make sure this is a decent name and not generic
|
||||
set exe_name=odin.exe
|
||||
|
||||
@@ -14,13 +23,15 @@ if "%1" == "1" (
|
||||
|
||||
:: Normal = 0, CI Nightly = 1
|
||||
if "%2" == "1" (
|
||||
set nightly=1
|
||||
set nightly=1
|
||||
) else (
|
||||
set nightly=0
|
||||
set nightly=0
|
||||
)
|
||||
|
||||
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
||||
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||
set compiler_defines= -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
|
||||
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
||||
|
||||
for /f %%i in ('git rev-parse --short HEAD') do set GIT_SHA=%%i
|
||||
if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
|
||||
@@ -35,12 +46,11 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
|
||||
set compiler_warnings= ^
|
||||
-W4 -WX ^
|
||||
-wd4100 -wd4101 -wd4127 -wd4189 ^
|
||||
-wd4201 -wd4204 ^
|
||||
-wd4456 -wd4457 -wd4480 ^
|
||||
-wd4512
|
||||
-wd4100 -wd4101 -wd4127 -wd4146 ^
|
||||
-wd4456 -wd4457
|
||||
|
||||
set compiler_includes=
|
||||
set compiler_includes= ^
|
||||
/Isrc\
|
||||
set libs= ^
|
||||
kernel32.lib ^
|
||||
bin\llvm\windows\LLVM-C.lib
|
||||
@@ -59,11 +69,14 @@ set linker_settings=%libs% %linker_flags%
|
||||
del *.pdb > NUL 2> NUL
|
||||
del *.ilk > NUL 2> NUL
|
||||
|
||||
cl %compiler_settings% "src\main.cpp" /link %linker_settings% -OUT:%exe_name%
|
||||
|
||||
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
|
||||
if %errorlevel% neq 0 goto end_of_build
|
||||
if %release_mode% EQU 0 odin run examples/demo/demo.odin
|
||||
|
||||
call build_vendor.bat
|
||||
if %errorlevel% neq 0 goto end_of_build
|
||||
|
||||
if %release_mode% EQU 0 odin run examples/demo
|
||||
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
:end_of_build
|
||||
:end_of_build
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
release_mode=$1
|
||||
|
||||
warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
|
||||
libraries="-pthread -ldl -lm -lstdc++"
|
||||
other_args=""
|
||||
compiler="clang"
|
||||
|
||||
if [ -z "$release_mode" ]; then release_mode="0"; fi
|
||||
|
||||
if [ "$release_mode" -eq "0" ]; then
|
||||
other_args="${other_args} -g"
|
||||
fi
|
||||
if [ "$release_mode" -eq "1" ]; then
|
||||
other_args="${other_args} -O3 -march=native"
|
||||
fi
|
||||
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
|
||||
# Set compiler to clang on MacOS
|
||||
# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
|
||||
compiler="clang"
|
||||
|
||||
other_args="${other_args} -liconv"
|
||||
elif [[ "$(uname)" == "FreeBSD" ]]; then
|
||||
compiler="clang"
|
||||
fi
|
||||
|
||||
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin && ./odin run examples/demo/demo.odin
|
||||
@@ -0,0 +1,17 @@
|
||||
@echo off
|
||||
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
rem build the .lib files already exist
|
||||
|
||||
if not exist "vendor\stb\lib\*.lib" (
|
||||
pushd vendor\stb\src
|
||||
call build.bat
|
||||
popd
|
||||
)
|
||||
|
||||
if not exist "vendor\miniaudio\lib\*.lib" (
|
||||
pushd vendor\miniaudio\src
|
||||
call build.bat
|
||||
popd
|
||||
)
|
||||
@@ -18,7 +18,7 @@ def main():
|
||||
name = remove_prefix(data['fileName'], "nightly/")
|
||||
url = f"https://f001.backblazeb2.com/file/{bucket}/nightly/{urllib.parse.quote_plus(name)}"
|
||||
sha1 = data['contentSha1']
|
||||
size = int(data['contentLength'])
|
||||
size = int(data['size'])
|
||||
ts = int(data['fileInfo']['src_last_modified_millis'])
|
||||
date = datetime.datetime.fromtimestamp(ts/1000).strftime('%Y-%m-%d')
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package bufio
|
||||
|
||||
import "core:io"
|
||||
|
||||
// Loadahead_Reader provides io lookahead.
|
||||
// This is useful for tokenizers/parsers.
|
||||
// Loadahead_Reader is similar to bufio.Reader, but unlike bufio.Reader, Loadahead_Reader's buffer size
|
||||
// will EXACTLY match the specified size, whereas bufio.Reader's buffer size may differ from the specified size.
|
||||
// This makes sure that the buffer will not be accidentally read beyond the expected size.
|
||||
Loadahead_Reader :: struct {
|
||||
r: io.Reader,
|
||||
buf: []byte,
|
||||
n: int,
|
||||
}
|
||||
|
||||
lookahead_reader_init :: proc(lr: ^Loadahead_Reader, r: io.Reader, buf: []byte) -> ^Loadahead_Reader {
|
||||
lr.r = r
|
||||
lr.buf = buf
|
||||
lr.n = 0
|
||||
return lr
|
||||
}
|
||||
|
||||
lookahead_reader_buffer :: proc(lr: ^Loadahead_Reader) -> []byte {
|
||||
return lr.buf[:lr.n]
|
||||
}
|
||||
|
||||
|
||||
// lookahead_reader_peek returns a slice of the Lookahead_Reader which holds n bytes
|
||||
// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest.
|
||||
// NOTE: The returned buffer is not a copy of the underlying buffer
|
||||
lookahead_reader_peek :: proc(lr: ^Loadahead_Reader, n: int) -> ([]byte, io.Error) {
|
||||
switch {
|
||||
case n < 0:
|
||||
return nil, .Negative_Read
|
||||
case n > len(lr.buf):
|
||||
return nil, .Buffer_Full
|
||||
}
|
||||
|
||||
n := n
|
||||
err: io.Error
|
||||
read_count: int
|
||||
|
||||
if lr.n < n {
|
||||
read_count, err = io.read_at_least(lr.r, lr.buf[lr.n:], n-lr.n)
|
||||
if err == .Unexpected_EOF {
|
||||
err = .EOF
|
||||
}
|
||||
}
|
||||
|
||||
lr.n += read_count
|
||||
|
||||
if n > lr.n {
|
||||
n = lr.n
|
||||
}
|
||||
return lr.buf[:n], err
|
||||
}
|
||||
|
||||
// lookahead_reader_peek_all returns a slice of the Lookahead_Reader populating the full buffer
|
||||
// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest.
|
||||
// NOTE: The returned buffer is not a copy of the underlying buffer
|
||||
lookahead_reader_peek_all :: proc(lr: ^Loadahead_Reader) -> ([]byte, io.Error) {
|
||||
return lookahead_reader_peek(lr, len(lr.buf))
|
||||
}
|
||||
|
||||
|
||||
// lookahead_reader_consume drops the first n populated bytes from the Lookahead_Reader.
|
||||
lookahead_reader_consume :: proc(lr: ^Loadahead_Reader, n: int) -> io.Error {
|
||||
switch {
|
||||
case n == 0:
|
||||
return nil
|
||||
case n < 0:
|
||||
return .Negative_Read
|
||||
case lr.n < n:
|
||||
return .Short_Buffer
|
||||
}
|
||||
copy(lr.buf, lr.buf[n:lr.n])
|
||||
lr.n -= n
|
||||
return nil
|
||||
}
|
||||
|
||||
lookahead_reader_consume_all :: proc(lr: ^Loadahead_Reader) -> io.Error {
|
||||
return lookahead_reader_consume(lr, lr.n)
|
||||
}
|
||||
+27
-27
@@ -10,59 +10,59 @@ Read_Writer :: struct {
|
||||
|
||||
|
||||
read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
|
||||
rw.r, rw.w = r, w;
|
||||
rw.r, rw.w = r, w
|
||||
}
|
||||
|
||||
read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
|
||||
s.stream_data = rw;
|
||||
s.stream_vtable = _read_writer_vtable;
|
||||
return;
|
||||
s.stream_data = rw
|
||||
s.stream_vtable = _read_writer_vtable
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_read_writer_vtable := &io.Stream_VTable{
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_read(b, p);
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_read(b, p)
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_read_byte(b);
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_read_byte(b)
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_unread_byte(b);
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_unread_byte(b)
|
||||
},
|
||||
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_read_rune(b);
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_read_rune(b)
|
||||
},
|
||||
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_unread_rune(b);
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_unread_rune(b)
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).r;
|
||||
return reader_write_to(b, w);
|
||||
b := (^Read_Writer)(s.stream_data).r
|
||||
return reader_write_to(b, w)
|
||||
},
|
||||
impl_flush = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Read_Writer)(s.stream_data).w;
|
||||
return writer_flush(b);
|
||||
b := (^Read_Writer)(s.stream_data).w
|
||||
return writer_flush(b)
|
||||
},
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).w;
|
||||
return writer_write(b, p);
|
||||
b := (^Read_Writer)(s.stream_data).w
|
||||
return writer_write(b, p)
|
||||
},
|
||||
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
|
||||
b := (^Read_Writer)(s.stream_data).w;
|
||||
return writer_write_byte(b, c);
|
||||
b := (^Read_Writer)(s.stream_data).w
|
||||
return writer_write_byte(b, c)
|
||||
},
|
||||
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).w;
|
||||
return writer_write_rune(b, r);
|
||||
b := (^Read_Writer)(s.stream_data).w
|
||||
return writer_write_rune(b, r)
|
||||
},
|
||||
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
b := (^Read_Writer)(s.stream_data).w;
|
||||
return writer_read_from(b, r);
|
||||
b := (^Read_Writer)(s.stream_data).w
|
||||
return writer_read_from(b, r)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
+183
-177
@@ -17,84 +17,90 @@ Reader :: struct {
|
||||
|
||||
last_byte: int, // last byte read, invalid is -1
|
||||
last_rune_size: int, // size of last rune read, invalid is -1
|
||||
|
||||
max_consecutive_empty_reads: int,
|
||||
}
|
||||
|
||||
|
||||
DEFAULT_BUF_SIZE :: 4096;
|
||||
DEFAULT_BUF_SIZE :: 4096
|
||||
|
||||
@(private)
|
||||
MIN_READ_BUFFER_SIZE :: 16;
|
||||
MIN_READ_BUFFER_SIZE :: 16
|
||||
@(private)
|
||||
MAX_CONSECUTIVE_EMPTY_READS :: 128;
|
||||
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128
|
||||
|
||||
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
||||
size := size;
|
||||
size = max(size, MIN_READ_BUFFER_SIZE);
|
||||
reader_reset(b, rd);
|
||||
b.buf_allocator = allocator;
|
||||
b.buf = make([]byte, size, allocator);
|
||||
size := size
|
||||
size = max(size, MIN_READ_BUFFER_SIZE)
|
||||
reader_reset(b, rd)
|
||||
b.buf_allocator = allocator
|
||||
b.buf = make([]byte, size, allocator)
|
||||
}
|
||||
|
||||
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
|
||||
reader_reset(b, rd);
|
||||
b.buf_allocator = {};
|
||||
b.buf = buf;
|
||||
reader_reset(b, rd)
|
||||
b.buf_allocator = {}
|
||||
b.buf = buf
|
||||
}
|
||||
|
||||
// reader_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
|
||||
reader_destroy :: proc(b: ^Reader) {
|
||||
delete(b.buf, b.buf_allocator);
|
||||
b^ = {};
|
||||
delete(b.buf, b.buf_allocator)
|
||||
b^ = {}
|
||||
}
|
||||
|
||||
reader_size :: proc(b: ^Reader) -> int {
|
||||
return len(b.buf);
|
||||
return len(b.buf)
|
||||
}
|
||||
|
||||
reader_reset :: proc(b: ^Reader, r: io.Reader) {
|
||||
b.rd = r;
|
||||
b.r, b.w = 0, 0;
|
||||
b.err = nil;
|
||||
b.last_byte = -1;
|
||||
b.last_rune_size = -1;
|
||||
b.rd = r
|
||||
b.r, b.w = 0, 0
|
||||
b.err = nil
|
||||
b.last_byte = -1
|
||||
b.last_rune_size = -1
|
||||
}
|
||||
|
||||
@(private)
|
||||
_reader_read_new_chunk :: proc(b: ^Reader) -> io.Error {
|
||||
if b.r > 0 {
|
||||
copy(b.buf, b.buf[b.r:b.w]);
|
||||
b.w -= b.r;
|
||||
b.r = 0;
|
||||
copy(b.buf, b.buf[b.r:b.w])
|
||||
b.w -= b.r
|
||||
b.r = 0
|
||||
}
|
||||
|
||||
if b.w >= len(b.buf) {
|
||||
return .Buffer_Full;
|
||||
return .Buffer_Full
|
||||
}
|
||||
|
||||
if b.max_consecutive_empty_reads <= 0 {
|
||||
b.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS
|
||||
}
|
||||
|
||||
// read new data, and try a limited number of times
|
||||
for i := MAX_CONSECUTIVE_EMPTY_READS; i > 0; i -= 1 {
|
||||
n, err := io.read(b.rd, b.buf[b.w:]);
|
||||
for i := b.max_consecutive_empty_reads; i > 0; i -= 1 {
|
||||
n, err := io.read(b.rd, b.buf[b.w:])
|
||||
if n < 0 {
|
||||
return .Negative_Read;
|
||||
return .Negative_Read
|
||||
}
|
||||
b.w += n;
|
||||
b.w += n
|
||||
if err != nil {
|
||||
b.err = err;
|
||||
return nil;
|
||||
b.err = err
|
||||
return nil
|
||||
}
|
||||
if n > 0 {
|
||||
return nil;
|
||||
return nil
|
||||
}
|
||||
}
|
||||
b.err = .No_Progress;
|
||||
return nil;
|
||||
b.err = .No_Progress
|
||||
return nil
|
||||
}
|
||||
|
||||
@(private)
|
||||
_reader_consume_err :: proc(b: ^Reader) -> io.Error {
|
||||
err := b.err;
|
||||
b.err = nil;
|
||||
return err;
|
||||
err := b.err
|
||||
b.err = nil
|
||||
return err
|
||||
}
|
||||
|
||||
// reader_peek returns the next n bytes without advancing the reader
|
||||
@@ -103,151 +109,151 @@ _reader_consume_err :: proc(b: ^Reader) -> io.Error {
|
||||
// explaining why the read is short
|
||||
// The error will be .Buffer_Full if n is larger than the internal buffer size
|
||||
reader_peek :: proc(b: ^Reader, n: int) -> (data: []byte, err: io.Error) {
|
||||
n := n;
|
||||
n := n
|
||||
|
||||
if n < 0 {
|
||||
return nil, .Negative_Count;
|
||||
return nil, .Negative_Count
|
||||
}
|
||||
b.last_byte = -1;
|
||||
b.last_rune_size = -1;
|
||||
b.last_byte = -1
|
||||
b.last_rune_size = -1
|
||||
|
||||
for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil {
|
||||
if fill_err := _reader_read_new_chunk(b); fill_err != nil {
|
||||
return nil, fill_err;
|
||||
return nil, fill_err
|
||||
}
|
||||
}
|
||||
|
||||
if n > len(b.buf) {
|
||||
return b.buf[b.r : b.w], .Buffer_Full;
|
||||
return b.buf[b.r : b.w], .Buffer_Full
|
||||
}
|
||||
|
||||
if available := b.w - b.r; available < n {
|
||||
n = available;
|
||||
err = _reader_consume_err(b);
|
||||
n = available
|
||||
err = _reader_consume_err(b)
|
||||
if err == nil {
|
||||
err = .Buffer_Full;
|
||||
err = .Buffer_Full
|
||||
}
|
||||
}
|
||||
|
||||
return b.buf[b.r : b.r+n], err;
|
||||
return b.buf[b.r : b.r+n], err
|
||||
}
|
||||
|
||||
// reader_buffered returns the number of bytes that can be read from the current buffer
|
||||
reader_buffered :: proc(b: ^Reader) -> int {
|
||||
return b.w - b.r;
|
||||
return b.w - b.r
|
||||
}
|
||||
|
||||
// reader_discard skips the next n bytes, and returns the number of bytes that were discarded
|
||||
reader_discard :: proc(b: ^Reader, n: int) -> (discarded: int, err: io.Error) {
|
||||
if n < 0 {
|
||||
return 0, .Negative_Count;
|
||||
return 0, .Negative_Count
|
||||
}
|
||||
if n == 0 {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
remaining := n;
|
||||
remaining := n
|
||||
for {
|
||||
skip := reader_buffered(b);
|
||||
skip := reader_buffered(b)
|
||||
if skip == 0 {
|
||||
if fill_err := _reader_read_new_chunk(b); fill_err != nil {
|
||||
return 0, fill_err;
|
||||
return 0, fill_err
|
||||
}
|
||||
skip = reader_buffered(b);
|
||||
skip = reader_buffered(b)
|
||||
}
|
||||
skip = min(skip, remaining);
|
||||
b.r += skip;
|
||||
remaining -= skip;
|
||||
skip = min(skip, remaining)
|
||||
b.r += skip
|
||||
remaining -= skip
|
||||
if remaining == 0 {
|
||||
return n, nil;
|
||||
return n, nil
|
||||
}
|
||||
if b.err != nil {
|
||||
return n - remaining, _reader_consume_err(b);
|
||||
return n - remaining, _reader_consume_err(b)
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// reader_read reads data into p
|
||||
// The bytes are taken from at most one read on the underlying Reader, which means n may be less than len(p)
|
||||
reader_read :: proc(b: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
||||
n = len(p);
|
||||
n = len(p)
|
||||
if n == 0 {
|
||||
if reader_buffered(b) > 0 {
|
||||
return 0, nil;
|
||||
return 0, nil
|
||||
}
|
||||
return 0, _reader_consume_err(b);
|
||||
return 0, _reader_consume_err(b)
|
||||
}
|
||||
if b.r == b.w {
|
||||
if b.err != nil {
|
||||
return 0, _reader_consume_err(b);
|
||||
return 0, _reader_consume_err(b)
|
||||
}
|
||||
|
||||
if len(p) >= len(b.buf) {
|
||||
n, b.err = io.read(b.rd, p);
|
||||
n, b.err = io.read(b.rd, p)
|
||||
if n < 0 {
|
||||
return 0, .Negative_Read;
|
||||
return 0, .Negative_Read
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
b.last_byte = int(p[n-1]);
|
||||
b.last_rune_size = -1;
|
||||
b.last_byte = int(p[n-1])
|
||||
b.last_rune_size = -1
|
||||
}
|
||||
return n, _reader_consume_err(b);
|
||||
return n, _reader_consume_err(b)
|
||||
}
|
||||
|
||||
b.r, b.w = 0, 0;
|
||||
n, b.err = io.read(b.rd, b.buf);
|
||||
b.r, b.w = 0, 0
|
||||
n, b.err = io.read(b.rd, b.buf)
|
||||
if n < 0 {
|
||||
return 0, .Negative_Read;
|
||||
return 0, .Negative_Read
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, _reader_consume_err(b);
|
||||
return 0, _reader_consume_err(b)
|
||||
}
|
||||
b.w += n;
|
||||
b.w += n
|
||||
}
|
||||
|
||||
n = copy(p, b.buf[b.r:b.w]);
|
||||
b.r += n;
|
||||
b.last_byte = int(b.buf[b.r-1]);
|
||||
b.last_rune_size = -1;
|
||||
return n, nil;
|
||||
n = copy(p, b.buf[b.r:b.w])
|
||||
b.r += n
|
||||
b.last_byte = int(b.buf[b.r-1])
|
||||
b.last_rune_size = -1
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// reader_read_byte reads and returns a single byte
|
||||
// If no byte is available, it return an error
|
||||
reader_read_byte :: proc(b: ^Reader) -> (byte, io.Error) {
|
||||
b.last_rune_size = -1;
|
||||
b.last_rune_size = -1
|
||||
for b.r == b.w {
|
||||
if b.err != nil {
|
||||
return 0, _reader_consume_err(b);
|
||||
return 0, _reader_consume_err(b)
|
||||
}
|
||||
if err := _reader_read_new_chunk(b); err != nil {
|
||||
return 0, err;
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
c := b.buf[b.r];
|
||||
b.r += 1;
|
||||
b.last_byte = int(c);
|
||||
return c, nil;
|
||||
c := b.buf[b.r]
|
||||
b.r += 1
|
||||
b.last_byte = int(c)
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// reader_unread_byte unreads the last byte. Only the most recently read byte can be unread
|
||||
reader_unread_byte :: proc(b: ^Reader) -> io.Error {
|
||||
if b.last_byte < 0 || b.r == 0 && b.w > 0 {
|
||||
return .Invalid_Unread;
|
||||
return .Invalid_Unread
|
||||
}
|
||||
if b.r > 0 {
|
||||
b.r -= 1;
|
||||
b.r -= 1
|
||||
} else {
|
||||
// b.r == 0 && b.w == 0
|
||||
b.w = 1;
|
||||
b.w = 1
|
||||
}
|
||||
b.buf[b.r] = byte(b.last_byte);
|
||||
b.last_byte = -1;
|
||||
b.last_rune_size = -1;
|
||||
return nil;
|
||||
b.buf[b.r] = byte(b.last_byte)
|
||||
b.last_byte = -1
|
||||
b.last_rune_size = -1
|
||||
return nil
|
||||
}
|
||||
|
||||
// reader_read_rune reads a single UTF-8 encoded unicode character
|
||||
@@ -259,96 +265,96 @@ reader_read_rune :: proc(b: ^Reader) -> (r: rune, size: int, err: io.Error) {
|
||||
b.err == nil &&
|
||||
b.w-b.w < len(b.buf) {
|
||||
if err = _reader_read_new_chunk(b); err != nil {
|
||||
return;
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
b.last_rune_size = -1;
|
||||
b.last_rune_size = -1
|
||||
if b.r == b.w {
|
||||
err = _reader_consume_err(b);
|
||||
return;
|
||||
err = _reader_consume_err(b)
|
||||
return
|
||||
}
|
||||
r, size = rune(b.buf[b.r]), 1;
|
||||
r, size = rune(b.buf[b.r]), 1
|
||||
if r >= utf8.RUNE_SELF {
|
||||
r, size = utf8.decode_rune(b.buf[b.r : b.w]);
|
||||
r, size = utf8.decode_rune(b.buf[b.r : b.w])
|
||||
}
|
||||
b.r += size;
|
||||
b.last_byte = int(b.buf[b.r-1]);
|
||||
b.last_rune_size = size;
|
||||
return;
|
||||
b.r += size
|
||||
b.last_byte = int(b.buf[b.r-1])
|
||||
b.last_rune_size = size
|
||||
return
|
||||
}
|
||||
|
||||
// reader_unread_rune unreads the last rune. Only the most recently read rune can be unread
|
||||
reader_unread_rune :: proc(b: ^Reader) -> io.Error {
|
||||
if b.last_rune_size < 0 || b.r < b.last_rune_size {
|
||||
return .Invalid_Unread;
|
||||
return .Invalid_Unread
|
||||
}
|
||||
b.r -= b.last_rune_size;
|
||||
b.last_byte = -1;
|
||||
b.last_rune_size = -1;
|
||||
return nil;
|
||||
b.r -= b.last_rune_size
|
||||
b.last_byte = -1
|
||||
b.last_rune_size = -1
|
||||
return nil
|
||||
}
|
||||
|
||||
reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
write_buf :: proc(b: ^Reader, w: io.Writer) -> (i64, io.Error) {
|
||||
n, err := io.write(w, b.buf[b.r:b.w]);
|
||||
n, err := io.write(w, b.buf[b.r:b.w])
|
||||
if n < 0 {
|
||||
return 0, .Negative_Write;
|
||||
return 0, .Negative_Write
|
||||
}
|
||||
b.r += n;
|
||||
return i64(n), err;
|
||||
b.r += n
|
||||
return i64(n), err
|
||||
}
|
||||
|
||||
n, err = write_buf(b, w);
|
||||
n, err = write_buf(b, w)
|
||||
if err != nil {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
m: i64;
|
||||
m: i64
|
||||
if nr, ok := io.to_writer_to(b.rd); ok {
|
||||
m, err = io.write_to(nr, w);
|
||||
n += m;
|
||||
return n, err;
|
||||
m, err = io.write_to(nr, w)
|
||||
n += m
|
||||
return n, err
|
||||
}
|
||||
|
||||
if nw, ok := io.to_reader_from(w); ok {
|
||||
m, err = io.read_from(nw, b.rd);
|
||||
n += m;
|
||||
return n, err;
|
||||
m, err = io.read_from(nw, b.rd)
|
||||
n += m
|
||||
return n, err
|
||||
}
|
||||
|
||||
if b.w-b.r < len(b.buf) {
|
||||
if err = _reader_read_new_chunk(b); err != nil {
|
||||
return;
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for b.r < b.w {
|
||||
m, err = write_buf(b, w);
|
||||
n += m;
|
||||
m, err = write_buf(b, w)
|
||||
n += m
|
||||
if err != nil {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if err = _reader_read_new_chunk(b); err != nil {
|
||||
return;
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if b.err == .EOF {
|
||||
b.err = nil;
|
||||
b.err = nil
|
||||
}
|
||||
|
||||
err = _reader_consume_err(b);
|
||||
return;
|
||||
err = _reader_consume_err(b)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
// reader_to_stream converts a Reader into an io.Stream
|
||||
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
|
||||
s.stream_data = b;
|
||||
s.stream_vtable = _reader_vtable;
|
||||
return;
|
||||
s.stream_data = b
|
||||
s.stream_vtable = _reader_vtable
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -356,35 +362,35 @@ reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
|
||||
@(private)
|
||||
_reader_vtable := &io.Stream_VTable{
|
||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Reader)(s.stream_data);
|
||||
reader_destroy(b);
|
||||
return nil;
|
||||
b := (^Reader)(s.stream_data)
|
||||
reader_destroy(b)
|
||||
return nil
|
||||
},
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_read(b, p);
|
||||
b := (^Reader)(s.stream_data)
|
||||
return reader_read(b, p)
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_read_byte(b);
|
||||
b := (^Reader)(s.stream_data)
|
||||
return reader_read_byte(b)
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_unread_byte(b);
|
||||
b := (^Reader)(s.stream_data)
|
||||
return reader_unread_byte(b)
|
||||
},
|
||||
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_read_rune(b);
|
||||
b := (^Reader)(s.stream_data)
|
||||
return reader_read_rune(b)
|
||||
},
|
||||
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_unread_rune(b);
|
||||
b := (^Reader)(s.stream_data)
|
||||
return reader_unread_rune(b)
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
b := (^Reader)(s.stream_data);
|
||||
return reader_write_to(b, w);
|
||||
b := (^Reader)(s.stream_data)
|
||||
return reader_write_to(b, w)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -404,71 +410,71 @@ _reader_vtable := &io.Stream_VTable{
|
||||
// reader_read_slice returns err != nil if and only if line does not end in delim
|
||||
//
|
||||
reader_read_slice :: proc(b: ^Reader, delim: byte) -> (line: []byte, err: io.Error) {
|
||||
s := 0;
|
||||
s := 0
|
||||
for {
|
||||
if i := bytes.index_byte(b.buf[b.r+s : b.w], delim); i >= 0 {
|
||||
i += s;
|
||||
line = b.buf[b.r:][:i+1];
|
||||
b.r += i + 1;
|
||||
break;
|
||||
i += s
|
||||
line = b.buf[b.r:][:i+1]
|
||||
b.r += i + 1
|
||||
break
|
||||
}
|
||||
|
||||
if b.err != nil {
|
||||
line = b.buf[b.r : b.w];
|
||||
b.r = b.w;
|
||||
err = _reader_consume_err(b);
|
||||
break;
|
||||
line = b.buf[b.r : b.w]
|
||||
b.r = b.w
|
||||
err = _reader_consume_err(b)
|
||||
break
|
||||
}
|
||||
|
||||
if reader_buffered(b) >= len(b.buf) {
|
||||
b.r = b.w;
|
||||
line = b.buf;
|
||||
err = .Buffer_Full;
|
||||
break;
|
||||
b.r = b.w
|
||||
line = b.buf
|
||||
err = .Buffer_Full
|
||||
break
|
||||
}
|
||||
|
||||
s = b.w - b.r;
|
||||
s = b.w - b.r
|
||||
|
||||
if err = _reader_read_new_chunk(b); err != nil {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if i := len(line)-1; i >= 0 {
|
||||
b.last_byte = int(line[i]);
|
||||
b.last_rune_size = -1;
|
||||
b.last_byte = int(line[i])
|
||||
b.last_rune_size = -1
|
||||
}
|
||||
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// reader_read_bytes reads until the first occurrence of delim from the Reader
|
||||
// It returns an allocated slice containing the data up to and including the delimiter
|
||||
reader_read_bytes :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (buf: []byte, err: io.Error) {
|
||||
full: [dynamic]byte;
|
||||
full.allocator = allocator;
|
||||
full: [dynamic]byte
|
||||
full.allocator = allocator
|
||||
|
||||
frag: []byte;
|
||||
frag: []byte
|
||||
for {
|
||||
e: io.Error;
|
||||
frag, e = reader_read_slice(b, delim);
|
||||
e: io.Error
|
||||
frag, e = reader_read_slice(b, delim)
|
||||
if e == nil {
|
||||
break;
|
||||
break
|
||||
}
|
||||
if e != .Buffer_Full {
|
||||
err = e;
|
||||
break;
|
||||
err = e
|
||||
break
|
||||
}
|
||||
|
||||
append(&full, ..frag);
|
||||
append(&full, ..frag)
|
||||
}
|
||||
append(&full, ..frag);
|
||||
return full[:], err;
|
||||
append(&full, ..frag)
|
||||
return full[:], err
|
||||
}
|
||||
|
||||
// reader_read_string reads until the first occurrence of delim from the Reader
|
||||
// It returns an allocated string containing the data up to and including the delimiter
|
||||
reader_read_string :: proc(b: ^Reader, delim: byte, allocator := context.allocator) -> (string, io.Error) {
|
||||
buf, err := reader_read_bytes(b, delim, allocator);
|
||||
return string(buf), err;
|
||||
buf, err := reader_read_bytes(b, delim, allocator)
|
||||
return string(buf), err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
package bufio
|
||||
|
||||
import "core:bytes"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
import "core:intrinsics"
|
||||
|
||||
// Extra errors returns by scanning procedures
|
||||
Scanner_Extra_Error :: enum i32 {
|
||||
Negative_Advance,
|
||||
Advanced_Too_Far,
|
||||
Bad_Read_Count,
|
||||
Too_Long,
|
||||
Too_Short,
|
||||
}
|
||||
|
||||
Scanner_Error :: union {
|
||||
io.Error,
|
||||
Scanner_Extra_Error,
|
||||
}
|
||||
|
||||
// Split_Proc is the signature of the split procedure used to tokenize the input.
|
||||
Split_Proc :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool)
|
||||
|
||||
Scanner :: struct {
|
||||
r: io.Reader,
|
||||
split: Split_Proc,
|
||||
|
||||
buf: [dynamic]byte,
|
||||
max_token_size: int,
|
||||
start: int,
|
||||
end: int,
|
||||
token: []byte,
|
||||
|
||||
_err: Scanner_Error,
|
||||
max_consecutive_empty_reads: int,
|
||||
successive_empty_token_count: int,
|
||||
scan_called: bool,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
DEFAULT_MAX_SCAN_TOKEN_SIZE :: 1<<16
|
||||
|
||||
@(private)
|
||||
_INIT_BUF_SIZE :: 4096
|
||||
|
||||
scanner_init :: proc(s: ^Scanner, r: io.Reader, buf_allocator := context.allocator) -> ^Scanner {
|
||||
s.r = r
|
||||
s.split = scan_lines
|
||||
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE
|
||||
s.buf.allocator = buf_allocator
|
||||
return s
|
||||
}
|
||||
scanner_init_with_buffer :: proc(s: ^Scanner, r: io.Reader, buf: []byte) -> ^Scanner {
|
||||
s.r = r
|
||||
s.split = scan_lines
|
||||
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE
|
||||
s.buf = mem.buffer_from_slice(buf)
|
||||
resize(&s.buf, cap(s.buf))
|
||||
return s
|
||||
}
|
||||
scanner_destroy :: proc(s: ^Scanner) {
|
||||
delete(s.buf)
|
||||
}
|
||||
|
||||
|
||||
// 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:
|
||||
return nil
|
||||
}
|
||||
return s._err
|
||||
}
|
||||
|
||||
// Returns the most recent token created by scanner_scan.
|
||||
// The underlying array may point to data that may be overwritten
|
||||
// by another call to scanner_scan.
|
||||
// Treat the returned value as if it is immutable.
|
||||
scanner_bytes :: proc(s: ^Scanner) -> []byte {
|
||||
return s.token
|
||||
}
|
||||
|
||||
// Returns the most recent token created by scanner_scan.
|
||||
// The underlying array may point to data that may be overwritten
|
||||
// by another call to scanner_scan.
|
||||
// Treat the returned value as if it is immutable.
|
||||
scanner_text :: proc(s: ^Scanner) -> string {
|
||||
return string(s.token)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
if s.done {
|
||||
return false
|
||||
}
|
||||
s.scan_called = true
|
||||
|
||||
for {
|
||||
// Check if a token is possible with what is available
|
||||
// Allow the split procedure to recover if it fails
|
||||
if s.start < s.end || s._err != nil {
|
||||
advance, token, err, final_token := s.split(s.buf[s.start:s.end], s._err != nil)
|
||||
if final_token {
|
||||
s.token = token
|
||||
s.done = true
|
||||
return true
|
||||
}
|
||||
if err != nil {
|
||||
set_err(s, err)
|
||||
return false
|
||||
}
|
||||
|
||||
// Do advance
|
||||
if advance < 0 {
|
||||
set_err(s, .Negative_Advance)
|
||||
return false
|
||||
}
|
||||
if advance > s.end-s.start {
|
||||
set_err(s, .Advanced_Too_Far)
|
||||
return false
|
||||
}
|
||||
s.start += advance
|
||||
|
||||
s.token = token
|
||||
if s.token != nil {
|
||||
if s._err == nil || advance > 0 {
|
||||
s.successive_empty_token_count = 0
|
||||
} else {
|
||||
s.successive_empty_token_count += 1
|
||||
|
||||
if s.max_consecutive_empty_reads <= 0 {
|
||||
s.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS
|
||||
}
|
||||
if s.successive_empty_token_count > s.max_consecutive_empty_reads {
|
||||
set_err(s, .No_Progress)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// If an error is hit, no token can be created
|
||||
if s._err != nil {
|
||||
s.start = 0
|
||||
s.end = 0
|
||||
return false
|
||||
}
|
||||
|
||||
// More data must be required to be read
|
||||
if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
|
||||
copy(s.buf[:], s.buf[s.start:s.end])
|
||||
s.end -= s.start
|
||||
s.start = 0
|
||||
}
|
||||
|
||||
could_be_too_short := false
|
||||
|
||||
// Resize the buffer if full
|
||||
if s.end == len(s.buf) {
|
||||
if s.max_token_size <= 0 {
|
||||
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE
|
||||
}
|
||||
if len(s.buf) >= s.max_token_size {
|
||||
set_err(s, .Too_Long)
|
||||
return false
|
||||
}
|
||||
// overflow check
|
||||
new_size := _INIT_BUF_SIZE
|
||||
if len(s.buf) > 0 {
|
||||
overflowed: bool
|
||||
if new_size, overflowed = intrinsics.overflow_mul(len(s.buf), 2); overflowed {
|
||||
set_err(s, .Too_Long)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
old_size := len(s.buf)
|
||||
new_size = min(new_size, s.max_token_size)
|
||||
resize(&s.buf, new_size)
|
||||
s.end -= s.start
|
||||
s.start = 0
|
||||
|
||||
could_be_too_short = old_size >= len(s.buf)
|
||||
|
||||
}
|
||||
|
||||
// Read data into the buffer
|
||||
loop := 0
|
||||
for {
|
||||
n, err := io.read(s.r, s.buf[s.end:len(s.buf)])
|
||||
if n < 0 || len(s.buf)-s.end < n {
|
||||
set_err(s, .Bad_Read_Count)
|
||||
break
|
||||
}
|
||||
s.end += n
|
||||
if err != nil {
|
||||
set_err(s, err)
|
||||
break
|
||||
}
|
||||
if n > 0 {
|
||||
s.successive_empty_token_count = 0
|
||||
break
|
||||
}
|
||||
loop += 1
|
||||
|
||||
if s.max_consecutive_empty_reads <= 0 {
|
||||
s.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS
|
||||
}
|
||||
if loop > s.max_consecutive_empty_reads {
|
||||
if could_be_too_short {
|
||||
set_err(s, .Too_Short)
|
||||
} else {
|
||||
set_err(s, .No_Progress)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
if at_eof && len(data) == 0 {
|
||||
return
|
||||
}
|
||||
return 1, data[0:1], nil, false
|
||||
}
|
||||
|
||||
scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
if at_eof && len(data) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if data[0] < utf8.RUNE_SELF {
|
||||
advance = 1
|
||||
token = data[0:1]
|
||||
return
|
||||
}
|
||||
|
||||
_, width := utf8.decode_rune(data)
|
||||
if width > 1 {
|
||||
advance = width
|
||||
token = data[0:width]
|
||||
return
|
||||
}
|
||||
|
||||
if !at_eof && !utf8.full_rune(data) {
|
||||
return
|
||||
}
|
||||
|
||||
@thread_local ERROR_RUNE := []byte{0xef, 0xbf, 0xbd}
|
||||
|
||||
advance = 1
|
||||
token = ERROR_RUNE
|
||||
return
|
||||
}
|
||||
|
||||
scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
is_space :: proc "contextless" (r: rune) -> bool {
|
||||
switch r {
|
||||
// lower ones
|
||||
case ' ', '\t', '\n', '\v', '\f', '\r':
|
||||
return true
|
||||
case '\u0085', '\u00a0':
|
||||
return true
|
||||
// higher ones
|
||||
case '\u2000' ..= '\u200a':
|
||||
return true
|
||||
case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// skip spaces at the beginning
|
||||
start := 0
|
||||
for width := 0; start < len(data); start += width {
|
||||
r: rune
|
||||
r, width = utf8.decode_rune(data[start:])
|
||||
if !is_space(r) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for width, i := 0, start; i < len(data); i += width {
|
||||
r: rune
|
||||
r, width = utf8.decode_rune(data[i:])
|
||||
if is_space(r) {
|
||||
advance = i+width
|
||||
token = data[start:i]
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if at_eof && len(data) > start {
|
||||
advance = len(data)
|
||||
token = data[start:]
|
||||
return
|
||||
}
|
||||
|
||||
advance = start
|
||||
return
|
||||
}
|
||||
|
||||
scan_lines :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
trim_carriage_return :: proc "contextless" (data: []byte) -> []byte {
|
||||
if len(data) > 0 && data[len(data)-1] == '\r' {
|
||||
return data[0:len(data)-1]
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
if at_eof && len(data) == 0 {
|
||||
return
|
||||
}
|
||||
if i := bytes.index_byte(data, '\n'); i >= 0 {
|
||||
advance = i+1
|
||||
token = trim_carriage_return(data[0:i])
|
||||
return
|
||||
}
|
||||
|
||||
if at_eof {
|
||||
advance = len(data)
|
||||
token = trim_carriage_return(data)
|
||||
}
|
||||
return
|
||||
}
|
||||
+103
-99
@@ -15,153 +15,155 @@ Writer :: struct {
|
||||
|
||||
err: io.Error,
|
||||
|
||||
max_consecutive_empty_writes: int,
|
||||
|
||||
}
|
||||
|
||||
writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
||||
size := size;
|
||||
size = max(size, MIN_READ_BUFFER_SIZE);
|
||||
writer_reset(b, wr);
|
||||
b.buf_allocator = allocator;
|
||||
b.buf = make([]byte, size, allocator);
|
||||
size := size
|
||||
size = max(size, MIN_READ_BUFFER_SIZE)
|
||||
writer_reset(b, wr)
|
||||
b.buf_allocator = allocator
|
||||
b.buf = make([]byte, size, allocator)
|
||||
}
|
||||
|
||||
writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) {
|
||||
writer_reset(b, wr);
|
||||
b.buf_allocator = {};
|
||||
b.buf = buf;
|
||||
writer_reset(b, wr)
|
||||
b.buf_allocator = {}
|
||||
b.buf = buf
|
||||
}
|
||||
|
||||
// writer_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set
|
||||
writer_destroy :: proc(b: ^Writer) {
|
||||
delete(b.buf, b.buf_allocator);
|
||||
b^ = {};
|
||||
delete(b.buf, b.buf_allocator)
|
||||
b^ = {}
|
||||
}
|
||||
|
||||
// writer_size returns the size of underlying buffer in bytes
|
||||
writer_size :: proc(b: ^Writer) -> int {
|
||||
return len(b.buf);
|
||||
return len(b.buf)
|
||||
}
|
||||
|
||||
writer_reset :: proc(b: ^Writer, w: io.Writer) {
|
||||
b.wr = w;
|
||||
b.n = 0;
|
||||
b.err = nil;
|
||||
b.wr = w
|
||||
b.n = 0
|
||||
b.err = nil
|
||||
}
|
||||
|
||||
|
||||
// writer_flush writes any buffered data into the underlying io.Writer
|
||||
writer_flush :: proc(b: ^Writer) -> io.Error {
|
||||
if b.err != nil {
|
||||
return b.err;
|
||||
return b.err
|
||||
}
|
||||
if b.n == 0 {
|
||||
return nil;
|
||||
return nil
|
||||
}
|
||||
|
||||
n, err := io.write(b.wr, b.buf[0:b.n]);
|
||||
n, err := io.write(b.wr, b.buf[0:b.n])
|
||||
if n < b.n && err == nil {
|
||||
err = .Short_Write;
|
||||
err = .Short_Write
|
||||
}
|
||||
if err != nil {
|
||||
if n > 0 && n < b.n {
|
||||
copy(b.buf[:b.n-n], b.buf[n : b.n]);
|
||||
copy(b.buf[:b.n-n], b.buf[n : b.n])
|
||||
}
|
||||
b.n -= n;
|
||||
b.err = err;
|
||||
return err;
|
||||
b.n -= n
|
||||
b.err = err
|
||||
return err
|
||||
}
|
||||
b.n = 0;
|
||||
return nil;
|
||||
b.n = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// writer_available returns how many bytes are unused in the buffer
|
||||
writer_available :: proc(b: ^Writer) -> int {
|
||||
return len(b.buf) - b.n;
|
||||
return len(b.buf) - b.n
|
||||
}
|
||||
|
||||
// writer_buffered returns the number of bytes that have been writted into the current buffer
|
||||
writer_buffered :: proc(b: ^Writer) -> int {
|
||||
return b.n;
|
||||
return b.n
|
||||
}
|
||||
|
||||
// writer_write writes the contents of p into the buffer
|
||||
// It returns the number of bytes written
|
||||
// If n < len(p), it will return an error explaining why the write is short
|
||||
writer_write :: proc(b: ^Writer, p: []byte) -> (n: int, err: io.Error) {
|
||||
p := p;
|
||||
p := p
|
||||
for len(p) > writer_available(b) && b.err == nil {
|
||||
m: int;
|
||||
m: int
|
||||
if writer_buffered(b) == 0 {
|
||||
m, b.err = io.write(b.wr, p);
|
||||
m, b.err = io.write(b.wr, p)
|
||||
} else {
|
||||
m = copy(b.buf[b.n:], p);
|
||||
b.n += m;
|
||||
writer_flush(b);
|
||||
m = copy(b.buf[b.n:], p)
|
||||
b.n += m
|
||||
writer_flush(b)
|
||||
}
|
||||
n += m;
|
||||
p = p[m:];
|
||||
n += m
|
||||
p = p[m:]
|
||||
}
|
||||
if b.err != nil {
|
||||
return n, b.err;
|
||||
return n, b.err
|
||||
}
|
||||
m := copy(b.buf[b.n:], p);
|
||||
b.n += m;
|
||||
m += n;
|
||||
return m, nil;
|
||||
m := copy(b.buf[b.n:], p)
|
||||
b.n += m
|
||||
m += n
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// writer_write_byte writes a single byte
|
||||
writer_write_byte :: proc(b: ^Writer, c: byte) -> io.Error {
|
||||
if b.err != nil {
|
||||
return b.err;
|
||||
return b.err
|
||||
}
|
||||
if writer_available(b) <= 0 && writer_flush(b) != nil {
|
||||
return b.err;
|
||||
return b.err
|
||||
}
|
||||
b.buf[b.n] = c;
|
||||
b.n += 1;
|
||||
return nil;
|
||||
b.buf[b.n] = c
|
||||
b.n += 1
|
||||
return nil
|
||||
}
|
||||
|
||||
// writer_write_rune writes a single unicode code point, and returns the number of bytes written with any error
|
||||
writer_write_rune :: proc(b: ^Writer, r: rune) -> (size: int, err: io.Error) {
|
||||
if r < utf8.RUNE_SELF {
|
||||
err = writer_write_byte(b, byte(r));
|
||||
size = 0 if err != nil else 1;
|
||||
return;
|
||||
err = writer_write_byte(b, byte(r))
|
||||
size = 0 if err != nil else 1
|
||||
return
|
||||
}
|
||||
if b.err != nil {
|
||||
return 0, b.err;
|
||||
return 0, b.err
|
||||
}
|
||||
|
||||
buf: [4]u8;
|
||||
buf: [4]u8
|
||||
|
||||
n := writer_available(b);
|
||||
n := writer_available(b)
|
||||
if n < utf8.UTF_MAX {
|
||||
writer_flush(b);
|
||||
writer_flush(b)
|
||||
if b.err != nil {
|
||||
return 0, b.err;
|
||||
return 0, b.err
|
||||
}
|
||||
n = writer_available(b);
|
||||
n = writer_available(b)
|
||||
if n < utf8.UTF_MAX {
|
||||
// this only happens if the buffer is very small
|
||||
w: int;
|
||||
buf, w = utf8.encode_rune(r);
|
||||
return writer_write(b, buf[:w]);
|
||||
w: int
|
||||
buf, w = utf8.encode_rune(r)
|
||||
return writer_write(b, buf[:w])
|
||||
}
|
||||
}
|
||||
|
||||
buf, size = utf8.encode_rune(r);
|
||||
copy(b.buf[b.n:], buf[:size]);
|
||||
b.n += size;
|
||||
return;
|
||||
buf, size = utf8.encode_rune(r)
|
||||
copy(b.buf[b.n:], buf[:size])
|
||||
b.n += size
|
||||
return
|
||||
}
|
||||
|
||||
// writer_write writes a string into the buffer
|
||||
// It returns the number of bytes written
|
||||
// If n < len(p), it will return an error explaining why the write is short
|
||||
writer_write_string :: proc(b: ^Writer, s: string) -> (int, io.Error) {
|
||||
return writer_write(b, transmute([]byte)s);
|
||||
return writer_write(b, transmute([]byte)s)
|
||||
}
|
||||
|
||||
// writer_read_from is to support io.Reader_From types
|
||||
@@ -169,58 +171,60 @@ writer_write_string :: proc(b: ^Writer, s: string) -> (int, io.Error) {
|
||||
// this procedure calls the underlying read_from implementation without buffering
|
||||
writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
if b.err != nil {
|
||||
return 0, b.err;
|
||||
return 0, b.err
|
||||
}
|
||||
if writer_buffered(b) == 0 {
|
||||
if w, ok := io.to_reader_from(b.wr); !ok {
|
||||
n, err = io.read_from(w, r);
|
||||
b.err = err;
|
||||
return;
|
||||
n, err = io.read_from(w, r)
|
||||
b.err = err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
if writer_available(b) == 0 {
|
||||
if ferr := writer_flush(b); ferr != nil {
|
||||
return n, ferr;
|
||||
}
|
||||
writer_flush(b) or_return
|
||||
}
|
||||
m: int;
|
||||
nr := 0;
|
||||
for nr < MAX_CONSECUTIVE_EMPTY_READS {
|
||||
m, err = io.read(r, b.buf[b.n:]);
|
||||
if b.max_consecutive_empty_writes <= 0 {
|
||||
b.max_consecutive_empty_writes = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS
|
||||
}
|
||||
|
||||
m: int
|
||||
nr := 0
|
||||
for nr < b.max_consecutive_empty_writes {
|
||||
m, err = io.read(r, b.buf[b.n:])
|
||||
if m != 0 || err != nil {
|
||||
break;
|
||||
break
|
||||
}
|
||||
nr += 1;
|
||||
nr += 1
|
||||
}
|
||||
if nr == MAX_CONSECUTIVE_EMPTY_READS {
|
||||
return n, .No_Progress;
|
||||
if nr == b.max_consecutive_empty_writes {
|
||||
return n, .No_Progress
|
||||
}
|
||||
b.n += m;
|
||||
n += i64(m);
|
||||
b.n += m
|
||||
n += i64(m)
|
||||
if err != nil {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err == .EOF {
|
||||
if writer_available(b) == 0 {
|
||||
err = writer_flush(b);
|
||||
err = writer_flush(b)
|
||||
} else {
|
||||
err = nil;
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
// writer_to_stream converts a Writer into an io.Stream
|
||||
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
|
||||
s.stream_data = b;
|
||||
s.stream_vtable = _writer_vtable;
|
||||
return;
|
||||
s.stream_data = b
|
||||
s.stream_vtable = _writer_vtable
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -228,28 +232,28 @@ writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
|
||||
@(private)
|
||||
_writer_vtable := &io.Stream_VTable{
|
||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Writer)(s.stream_data);
|
||||
writer_destroy(b);
|
||||
return nil;
|
||||
b := (^Writer)(s.stream_data)
|
||||
writer_destroy(b)
|
||||
return nil
|
||||
},
|
||||
impl_flush = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Writer)(s.stream_data);
|
||||
return writer_flush(b);
|
||||
b := (^Writer)(s.stream_data)
|
||||
return writer_flush(b)
|
||||
},
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Writer)(s.stream_data);
|
||||
return writer_write(b, p);
|
||||
b := (^Writer)(s.stream_data)
|
||||
return writer_write(b, p)
|
||||
},
|
||||
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
|
||||
b := (^Writer)(s.stream_data);
|
||||
return writer_write_byte(b, c);
|
||||
b := (^Writer)(s.stream_data)
|
||||
return writer_write_byte(b, c)
|
||||
},
|
||||
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
|
||||
b := (^Writer)(s.stream_data);
|
||||
return writer_write_rune(b, r);
|
||||
b := (^Writer)(s.stream_data)
|
||||
return writer_write_rune(b, r)
|
||||
},
|
||||
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
b := (^Writer)(s.stream_data);
|
||||
return writer_read_from(b, r);
|
||||
b := (^Writer)(s.stream_data)
|
||||
return writer_read_from(b, r)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
package builtin
|
||||
|
||||
nil :: nil;
|
||||
false :: 0!==0;
|
||||
false :: 0!=0;
|
||||
true :: 0==0;
|
||||
|
||||
ODIN_OS :: ODIN_OS;
|
||||
|
||||
+187
-187
@@ -3,10 +3,10 @@ package bytes
|
||||
import "core:io"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
MIN_READ :: 512;
|
||||
MIN_READ :: 512
|
||||
|
||||
@(private)
|
||||
SMALL_BUFFER_SIZE :: 64;
|
||||
SMALL_BUFFER_SIZE :: 64
|
||||
|
||||
// A Buffer is a variable-sized buffer of bytes with a io.Stream interface
|
||||
// The zero value for Buffer is an empty buffer ready to use.
|
||||
@@ -28,406 +28,406 @@ Read_Op :: enum i8 {
|
||||
|
||||
|
||||
buffer_init :: proc(b: ^Buffer, buf: []byte) {
|
||||
resize(&b.buf, len(buf));
|
||||
copy(b.buf[:], buf);
|
||||
resize(&b.buf, len(buf))
|
||||
copy(b.buf[:], buf)
|
||||
}
|
||||
|
||||
buffer_init_string :: proc(b: ^Buffer, s: string) {
|
||||
resize(&b.buf, len(s));
|
||||
copy(b.buf[:], s);
|
||||
resize(&b.buf, len(s))
|
||||
copy(b.buf[:], s)
|
||||
}
|
||||
|
||||
buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) {
|
||||
b.buf.allocator = allocator;
|
||||
reserve(&b.buf, cap);
|
||||
resize(&b.buf, len);
|
||||
b.buf.allocator = allocator
|
||||
reserve(&b.buf, cap)
|
||||
resize(&b.buf, len)
|
||||
}
|
||||
|
||||
buffer_destroy :: proc(b: ^Buffer) {
|
||||
delete(b.buf);
|
||||
buffer_reset(b);
|
||||
delete(b.buf)
|
||||
buffer_reset(b)
|
||||
}
|
||||
|
||||
buffer_to_bytes :: proc(b: ^Buffer) -> []byte {
|
||||
return b.buf[b.off:];
|
||||
return b.buf[b.off:]
|
||||
}
|
||||
|
||||
buffer_to_string :: proc(b: ^Buffer) -> string {
|
||||
if b == nil {
|
||||
return "<nil>";
|
||||
return "<nil>"
|
||||
}
|
||||
return string(b.buf[b.off:]);
|
||||
return string(b.buf[b.off:])
|
||||
}
|
||||
|
||||
buffer_is_empty :: proc(b: ^Buffer) -> bool {
|
||||
return len(b.buf) <= b.off;
|
||||
return len(b.buf) <= b.off
|
||||
}
|
||||
|
||||
buffer_length :: proc(b: ^Buffer) -> int {
|
||||
return len(b.buf) - b.off;
|
||||
return len(b.buf) - b.off
|
||||
}
|
||||
|
||||
buffer_capacity :: proc(b: ^Buffer) -> int {
|
||||
return cap(b.buf);
|
||||
return cap(b.buf)
|
||||
}
|
||||
|
||||
buffer_reset :: proc(b: ^Buffer) {
|
||||
clear(&b.buf);
|
||||
b.off = 0;
|
||||
b.last_read = .Invalid;
|
||||
clear(&b.buf)
|
||||
b.off = 0
|
||||
b.last_read = .Invalid
|
||||
}
|
||||
|
||||
|
||||
buffer_truncate :: proc(b: ^Buffer, n: int) {
|
||||
if n == 0 {
|
||||
buffer_reset(b);
|
||||
return;
|
||||
buffer_reset(b)
|
||||
return
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
b.last_read = .Invalid
|
||||
if n < 0 || n > buffer_length(b) {
|
||||
panic("bytes.truncate: truncation out of range");
|
||||
panic("bytes.truncate: truncation out of range")
|
||||
}
|
||||
resize(&b.buf, b.off+n);
|
||||
resize(&b.buf, b.off+n)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) {
|
||||
if l := len(b.buf); n <= cap(b.buf)-l {
|
||||
resize(&b.buf, l+n);
|
||||
return l, true;
|
||||
resize(&b.buf, l+n)
|
||||
return l, true
|
||||
}
|
||||
return 0, false;
|
||||
return 0, false
|
||||
}
|
||||
|
||||
@(private)
|
||||
_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
|
||||
m := buffer_length(b);
|
||||
m := buffer_length(b)
|
||||
if m == 0 && b.off != 0 {
|
||||
buffer_reset(b);
|
||||
buffer_reset(b)
|
||||
}
|
||||
if i, ok := _buffer_try_grow(b, n); ok {
|
||||
return i;
|
||||
return i
|
||||
}
|
||||
if b.buf == nil && n <= SMALL_BUFFER_SIZE {
|
||||
b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE);
|
||||
return 0;
|
||||
b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE)
|
||||
return 0
|
||||
}
|
||||
|
||||
c := cap(b.buf);
|
||||
c := cap(b.buf)
|
||||
if n <= c/2 - m {
|
||||
copy(b.buf[:], b.buf[b.off:]);
|
||||
copy(b.buf[:], b.buf[b.off:])
|
||||
} else if c > max(int) - c - n {
|
||||
panic("bytes.Buffer: too large");
|
||||
panic("bytes.Buffer: too large")
|
||||
} else {
|
||||
resize(&b.buf, 2*c + n);
|
||||
copy(b.buf[:], b.buf[b.off:]);
|
||||
resize(&b.buf, 2*c + n)
|
||||
copy(b.buf[:], b.buf[b.off:])
|
||||
}
|
||||
b.off = 0;
|
||||
resize(&b.buf, m+n);
|
||||
return m;
|
||||
b.off = 0
|
||||
resize(&b.buf, m+n)
|
||||
return m
|
||||
}
|
||||
|
||||
buffer_grow :: proc(b: ^Buffer, n: int) {
|
||||
if n < 0 {
|
||||
panic("bytes.buffer_grow: negative count");
|
||||
panic("bytes.buffer_grow: negative count")
|
||||
}
|
||||
m := _buffer_grow(b, n);
|
||||
resize(&b.buf, m);
|
||||
m := _buffer_grow(b, n)
|
||||
resize(&b.buf, m)
|
||||
}
|
||||
|
||||
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
b.last_read = .Invalid
|
||||
if offset < 0 {
|
||||
err = .Invalid_Offset;
|
||||
return;
|
||||
err = .Invalid_Offset
|
||||
return
|
||||
}
|
||||
_, ok := _buffer_try_grow(b, offset+len(p));
|
||||
_, ok := _buffer_try_grow(b, offset+len(p))
|
||||
if !ok {
|
||||
_ = _buffer_grow(b, offset+len(p));
|
||||
_ = _buffer_grow(b, offset+len(p))
|
||||
}
|
||||
if len(b.buf) <= offset {
|
||||
return 0, .Short_Write;
|
||||
return 0, .Short_Write
|
||||
}
|
||||
return copy(b.buf[offset:], p), nil;
|
||||
return copy(b.buf[offset:], p), nil
|
||||
}
|
||||
|
||||
|
||||
buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, len(p));
|
||||
b.last_read = .Invalid
|
||||
m, ok := _buffer_try_grow(b, len(p))
|
||||
if !ok {
|
||||
m = _buffer_grow(b, len(p));
|
||||
m = _buffer_grow(b, len(p))
|
||||
}
|
||||
return copy(b.buf[m:], p), nil;
|
||||
return copy(b.buf[m:], p), nil
|
||||
}
|
||||
|
||||
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, len(s));
|
||||
b.last_read = .Invalid
|
||||
m, ok := _buffer_try_grow(b, len(s))
|
||||
if !ok {
|
||||
m = _buffer_grow(b, len(s));
|
||||
m = _buffer_grow(b, len(s))
|
||||
}
|
||||
return copy(b.buf[m:], s), nil;
|
||||
return copy(b.buf[m:], s), nil
|
||||
}
|
||||
|
||||
buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error {
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, 1);
|
||||
b.last_read = .Invalid
|
||||
m, ok := _buffer_try_grow(b, 1)
|
||||
if !ok {
|
||||
m = _buffer_grow(b, 1);
|
||||
m = _buffer_grow(b, 1)
|
||||
}
|
||||
b.buf[m] = c;
|
||||
return nil;
|
||||
b.buf[m] = c
|
||||
return nil
|
||||
}
|
||||
|
||||
buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) {
|
||||
if r < utf8.RUNE_SELF {
|
||||
buffer_write_byte(b, byte(r));
|
||||
return 1, nil;
|
||||
buffer_write_byte(b, byte(r))
|
||||
return 1, nil
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
m, ok := _buffer_try_grow(b, utf8.UTF_MAX);
|
||||
b.last_read = .Invalid
|
||||
m, ok := _buffer_try_grow(b, utf8.UTF_MAX)
|
||||
if !ok {
|
||||
m = _buffer_grow(b, utf8.UTF_MAX);
|
||||
m = _buffer_grow(b, utf8.UTF_MAX)
|
||||
}
|
||||
res: [4]byte;
|
||||
res, n = utf8.encode_rune(r);
|
||||
copy(b.buf[m:][:utf8.UTF_MAX], res[:n]);
|
||||
resize(&b.buf, m+n);
|
||||
return;
|
||||
res: [4]byte
|
||||
res, n = utf8.encode_rune(r)
|
||||
copy(b.buf[m:][:utf8.UTF_MAX], res[:n])
|
||||
resize(&b.buf, m+n)
|
||||
return
|
||||
}
|
||||
|
||||
buffer_next :: proc(b: ^Buffer, n: int) -> []byte {
|
||||
n := n;
|
||||
b.last_read = .Invalid;
|
||||
m := buffer_length(b);
|
||||
n := n
|
||||
b.last_read = .Invalid
|
||||
m := buffer_length(b)
|
||||
if n > m {
|
||||
n = m;
|
||||
n = m
|
||||
}
|
||||
data := b.buf[b.off : b.off + n];
|
||||
b.off += n;
|
||||
data := b.buf[b.off : b.off + n]
|
||||
b.off += n
|
||||
if n > 0 {
|
||||
b.last_read = .Read;
|
||||
b.last_read = .Read
|
||||
}
|
||||
return data;
|
||||
return data
|
||||
}
|
||||
|
||||
buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
b.last_read = .Invalid
|
||||
if buffer_is_empty(b) {
|
||||
buffer_reset(b);
|
||||
buffer_reset(b)
|
||||
if len(p) == 0 {
|
||||
return 0, nil;
|
||||
return 0, nil
|
||||
}
|
||||
return 0, .EOF;
|
||||
return 0, .EOF
|
||||
}
|
||||
n = copy(p, b.buf[b.off:]);
|
||||
b.off += n;
|
||||
n = copy(p, b.buf[b.off:])
|
||||
b.off += n
|
||||
if n > 0 {
|
||||
b.last_read = .Read;
|
||||
b.last_read = .Read
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
b.last_read = .Invalid
|
||||
|
||||
if offset < 0 || offset >= len(b.buf) {
|
||||
err = .Invalid_Offset;
|
||||
return;
|
||||
err = .Invalid_Offset
|
||||
return
|
||||
}
|
||||
|
||||
if 0 <= offset && offset < len(b.buf) {
|
||||
n = copy(p, b.buf[offset:]);
|
||||
n = copy(p, b.buf[offset:])
|
||||
}
|
||||
if n > 0 {
|
||||
b.last_read = .Read;
|
||||
b.last_read = .Read
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
buffer_read_byte :: proc(b: ^Buffer) -> (byte, io.Error) {
|
||||
if buffer_is_empty(b) {
|
||||
buffer_reset(b);
|
||||
return 0, .EOF;
|
||||
buffer_reset(b)
|
||||
return 0, .EOF
|
||||
}
|
||||
c := b.buf[b.off];
|
||||
b.off += 1;
|
||||
b.last_read = .Read;
|
||||
return c, nil;
|
||||
c := b.buf[b.off]
|
||||
b.off += 1
|
||||
b.last_read = .Read
|
||||
return c, nil
|
||||
}
|
||||
|
||||
buffer_read_rune :: proc(b: ^Buffer) -> (r: rune, size: int, err: io.Error) {
|
||||
if buffer_is_empty(b) {
|
||||
buffer_reset(b);
|
||||
return 0, 0, .EOF;
|
||||
buffer_reset(b)
|
||||
return 0, 0, .EOF
|
||||
}
|
||||
c := b.buf[b.off];
|
||||
c := b.buf[b.off]
|
||||
if c < utf8.RUNE_SELF {
|
||||
b.off += 1;
|
||||
b.last_read = .Read_Rune1;
|
||||
return rune(c), 1, nil;
|
||||
b.off += 1
|
||||
b.last_read = .Read_Rune1
|
||||
return rune(c), 1, nil
|
||||
}
|
||||
r, size = utf8.decode_rune(b.buf[b.off:]);
|
||||
b.off += size;
|
||||
b.last_read = Read_Op(i8(size));
|
||||
return;
|
||||
r, size = utf8.decode_rune(b.buf[b.off:])
|
||||
b.off += size
|
||||
b.last_read = Read_Op(i8(size))
|
||||
return
|
||||
}
|
||||
|
||||
buffer_unread_byte :: proc(b: ^Buffer) -> io.Error {
|
||||
if b.last_read == .Invalid {
|
||||
return .Invalid_Unread;
|
||||
return .Invalid_Unread
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
b.last_read = .Invalid
|
||||
if b.off > 0 {
|
||||
b.off -= 1;
|
||||
b.off -= 1
|
||||
}
|
||||
return nil;
|
||||
return nil
|
||||
}
|
||||
|
||||
buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
|
||||
if b.last_read <= .Invalid {
|
||||
return .Invalid_Unread;
|
||||
return .Invalid_Unread
|
||||
}
|
||||
if b.off >= int(b.last_read) {
|
||||
b.off -= int(i8(b.last_read));
|
||||
b.off -= int(i8(b.last_read))
|
||||
}
|
||||
b.last_read = .Invalid;
|
||||
return nil;
|
||||
b.last_read = .Invalid
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
|
||||
i := index_byte(b.buf[b.off:], delim);
|
||||
end := b.off + i + 1;
|
||||
i := index_byte(b.buf[b.off:], delim)
|
||||
end := b.off + i + 1
|
||||
if i < 0 {
|
||||
end = len(b.buf);
|
||||
err = .EOF;
|
||||
end = len(b.buf)
|
||||
err = .EOF
|
||||
}
|
||||
line = b.buf[b.off:end];
|
||||
b.off = end;
|
||||
b.last_read = .Read;
|
||||
return;
|
||||
line = b.buf[b.off:end]
|
||||
b.off = end
|
||||
b.last_read = .Read
|
||||
return
|
||||
}
|
||||
|
||||
buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Error) {
|
||||
slice: []byte;
|
||||
slice, err = buffer_read_bytes(b, delim);
|
||||
return string(slice), err;
|
||||
slice: []byte
|
||||
slice, err = buffer_read_bytes(b, delim)
|
||||
return string(slice), err
|
||||
}
|
||||
|
||||
buffer_write_to :: proc(b: ^Buffer, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
b.last_read = .Invalid;
|
||||
b.last_read = .Invalid
|
||||
if byte_count := buffer_length(b); byte_count > 0 {
|
||||
m, e := io.write(w, b.buf[b.off:]);
|
||||
m, e := io.write(w, b.buf[b.off:])
|
||||
if m > byte_count {
|
||||
panic("bytes.buffer_write_to: invalid io.write count");
|
||||
panic("bytes.buffer_write_to: invalid io.write count")
|
||||
}
|
||||
b.off += m;
|
||||
n = i64(m);
|
||||
b.off += m
|
||||
n = i64(m)
|
||||
if e != nil {
|
||||
err = e;
|
||||
return;
|
||||
err = e
|
||||
return
|
||||
}
|
||||
if m != byte_count {
|
||||
err = .Short_Write;
|
||||
return;
|
||||
err = .Short_Write
|
||||
return
|
||||
}
|
||||
}
|
||||
buffer_reset(b);
|
||||
return;
|
||||
buffer_reset(b)
|
||||
return
|
||||
}
|
||||
|
||||
buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #no_bounds_check {
|
||||
b.last_read = .Invalid;
|
||||
b.last_read = .Invalid
|
||||
for {
|
||||
i := _buffer_grow(b, MIN_READ);
|
||||
resize(&b.buf, i);
|
||||
m, e := io.read(r, b.buf[i:cap(b.buf)]);
|
||||
i := _buffer_grow(b, MIN_READ)
|
||||
resize(&b.buf, i)
|
||||
m, e := io.read(r, b.buf[i:cap(b.buf)])
|
||||
if m < 0 {
|
||||
err = .Negative_Read;
|
||||
return;
|
||||
err = .Negative_Read
|
||||
return
|
||||
}
|
||||
|
||||
resize(&b.buf, i+m);
|
||||
n += i64(m);
|
||||
resize(&b.buf, i+m)
|
||||
n += i64(m)
|
||||
if e == .EOF {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if e != nil {
|
||||
err = e;
|
||||
return;
|
||||
err = e
|
||||
return
|
||||
}
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
|
||||
s.stream_data = b;
|
||||
s.stream_vtable = _buffer_vtable;
|
||||
return;
|
||||
s.stream_data = b
|
||||
s.stream_vtable = _buffer_vtable
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_buffer_vtable := &io.Stream_VTable{
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return i64(buffer_capacity(b));
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return i64(buffer_capacity(b))
|
||||
},
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read(b, p);
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_read(b, p)
|
||||
},
|
||||
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_at(b, p, int(offset));
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_read_at(b, p, int(offset))
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_byte(b);
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_read_byte(b)
|
||||
},
|
||||
impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_rune(b);
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_read_rune(b)
|
||||
},
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write(b, p);
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_write(b, p)
|
||||
},
|
||||
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_at(b, p, int(offset));
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_write_at(b, p, int(offset))
|
||||
},
|
||||
impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_byte(b, c);
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_write_byte(b, c)
|
||||
},
|
||||
impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_rune(b, r);
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_write_rune(b, r)
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_unread_byte(b);
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_unread_byte(b)
|
||||
},
|
||||
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_unread_rune(b);
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_unread_rune(b)
|
||||
},
|
||||
impl_destroy = proc(s: io.Stream) -> io.Error {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
buffer_destroy(b);
|
||||
return nil;
|
||||
b := (^Buffer)(s.stream_data)
|
||||
buffer_destroy(b)
|
||||
return nil
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_write_to(b, w);
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_write_to(b, w)
|
||||
},
|
||||
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
b := (^Buffer)(s.stream_data);
|
||||
return buffer_read_from(b, r);
|
||||
b := (^Buffer)(s.stream_data)
|
||||
return buffer_read_from(b, r)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+421
-438
File diff suppressed because it is too large
Load Diff
+79
-79
@@ -10,168 +10,168 @@ Reader :: struct {
|
||||
}
|
||||
|
||||
reader_init :: proc(r: ^Reader, s: []byte) {
|
||||
r.s = s;
|
||||
r.i = 0;
|
||||
r.prev_rune = -1;
|
||||
r.s = s
|
||||
r.i = 0
|
||||
r.prev_rune = -1
|
||||
}
|
||||
|
||||
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
||||
s.stream_data = r;
|
||||
s.stream_vtable = _reader_vtable;
|
||||
return;
|
||||
s.stream_data = r
|
||||
s.stream_vtable = _reader_vtable
|
||||
return
|
||||
}
|
||||
|
||||
reader_length :: proc(r: ^Reader) -> int {
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
return int(i64(len(r.s)) - r.i);
|
||||
return int(i64(len(r.s)) - r.i)
|
||||
}
|
||||
|
||||
reader_size :: proc(r: ^Reader) -> i64 {
|
||||
return i64(len(r.s));
|
||||
return i64(len(r.s))
|
||||
}
|
||||
|
||||
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0, .EOF;
|
||||
return 0, .EOF
|
||||
}
|
||||
r.prev_rune = -1;
|
||||
n = copy(p, r.s[r.i:]);
|
||||
r.i += i64(n);
|
||||
return;
|
||||
r.prev_rune = -1
|
||||
n = copy(p, r.s[r.i:])
|
||||
r.i += i64(n)
|
||||
return
|
||||
}
|
||||
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
||||
if off < 0 {
|
||||
return 0, .Invalid_Offset;
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
if off >= i64(len(r.s)) {
|
||||
return 0, .EOF;
|
||||
return 0, .EOF
|
||||
}
|
||||
n = copy(p, r.s[off:]);
|
||||
n = copy(p, r.s[off:])
|
||||
if n < len(p) {
|
||||
err = .EOF;
|
||||
err = .EOF
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
|
||||
r.prev_rune = -1;
|
||||
r.prev_rune = -1
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0, .EOF;
|
||||
return 0, .EOF
|
||||
}
|
||||
b := r.s[r.i];
|
||||
r.i += 1;
|
||||
return b, nil;
|
||||
b := r.s[r.i]
|
||||
r.i += 1
|
||||
return b, nil
|
||||
}
|
||||
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
|
||||
if r.i <= 0 {
|
||||
return .Invalid_Unread;
|
||||
return .Invalid_Unread
|
||||
}
|
||||
r.prev_rune = -1;
|
||||
r.i -= 1;
|
||||
return nil;
|
||||
r.prev_rune = -1
|
||||
r.i -= 1
|
||||
return nil
|
||||
}
|
||||
reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
|
||||
if r.i >= i64(len(r.s)) {
|
||||
r.prev_rune = -1;
|
||||
return 0, 0, .EOF;
|
||||
r.prev_rune = -1
|
||||
return 0, 0, .EOF
|
||||
}
|
||||
r.prev_rune = int(r.i);
|
||||
r.prev_rune = int(r.i)
|
||||
if c := r.s[r.i]; c < utf8.RUNE_SELF {
|
||||
r.i += 1;
|
||||
return rune(c), 1, nil;
|
||||
r.i += 1
|
||||
return rune(c), 1, nil
|
||||
}
|
||||
ch, size = utf8.decode_rune(r.s[r.i:]);
|
||||
r.i += i64(size);
|
||||
return;
|
||||
ch, size = utf8.decode_rune(r.s[r.i:])
|
||||
r.i += i64(size)
|
||||
return
|
||||
}
|
||||
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
|
||||
if r.i <= 0 {
|
||||
return .Invalid_Unread;
|
||||
return .Invalid_Unread
|
||||
}
|
||||
if r.prev_rune < 0 {
|
||||
return .Invalid_Unread;
|
||||
return .Invalid_Unread
|
||||
}
|
||||
r.i = i64(r.prev_rune);
|
||||
r.prev_rune = -1;
|
||||
return nil;
|
||||
r.i = i64(r.prev_rune)
|
||||
r.prev_rune = -1
|
||||
return nil
|
||||
}
|
||||
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||
r.prev_rune = -1;
|
||||
abs: i64;
|
||||
r.prev_rune = -1
|
||||
abs: i64
|
||||
switch whence {
|
||||
case .Start:
|
||||
abs = offset;
|
||||
abs = offset
|
||||
case .Current:
|
||||
abs = r.i + offset;
|
||||
abs = r.i + offset
|
||||
case .End:
|
||||
abs = i64(len(r.s)) + offset;
|
||||
abs = i64(len(r.s)) + offset
|
||||
case:
|
||||
return 0, .Invalid_Whence;
|
||||
return 0, .Invalid_Whence
|
||||
}
|
||||
|
||||
if abs < 0 {
|
||||
return 0, .Invalid_Offset;
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
r.i = abs;
|
||||
return abs, nil;
|
||||
r.i = abs
|
||||
return abs, nil
|
||||
}
|
||||
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
r.prev_rune = -1;
|
||||
r.prev_rune = -1
|
||||
if r.i >= i64(len(r.s)) {
|
||||
return 0, nil;
|
||||
return 0, nil
|
||||
}
|
||||
s := r.s[r.i:];
|
||||
m: int;
|
||||
m, err = io.write(w, s);
|
||||
s := r.s[r.i:]
|
||||
m: int
|
||||
m, err = io.write(w, s)
|
||||
if m > len(s) {
|
||||
panic("bytes.Reader.write_to: invalid io.write_string count");
|
||||
panic("bytes.Reader.write_to: invalid io.write_string count")
|
||||
}
|
||||
r.i += i64(m);
|
||||
n = i64(m);
|
||||
r.i += i64(m)
|
||||
n = i64(m)
|
||||
if m != len(s) && err == nil {
|
||||
err = .Short_Write;
|
||||
err = .Short_Write
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_reader_vtable := &io.Stream_VTable{
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_size(r);
|
||||
r := (^Reader)(s.stream_data)
|
||||
return reader_size(r)
|
||||
},
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read(r, p);
|
||||
r := (^Reader)(s.stream_data)
|
||||
return reader_read(r, p)
|
||||
},
|
||||
impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read_at(r, p, off);
|
||||
r := (^Reader)(s.stream_data)
|
||||
return reader_read_at(r, p, off)
|
||||
},
|
||||
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read_byte(r);
|
||||
r := (^Reader)(s.stream_data)
|
||||
return reader_read_byte(r)
|
||||
},
|
||||
impl_unread_byte = proc(s: io.Stream) -> io.Error {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_unread_byte(r);
|
||||
r := (^Reader)(s.stream_data)
|
||||
return reader_unread_byte(r)
|
||||
},
|
||||
impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_read_rune(r);
|
||||
r := (^Reader)(s.stream_data)
|
||||
return reader_read_rune(r)
|
||||
},
|
||||
impl_unread_rune = proc(s: io.Stream) -> io.Error {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_unread_rune(r);
|
||||
r := (^Reader)(s.stream_data)
|
||||
return reader_unread_rune(r)
|
||||
},
|
||||
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_seek(r, offset, whence);
|
||||
r := (^Reader)(s.stream_data)
|
||||
return reader_seek(r, offset, whence)
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
r := (^Reader)(s.stream_data);
|
||||
return reader_write_to(r, w);
|
||||
r := (^Reader)(s.stream_data)
|
||||
return reader_write_to(r, w)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
+97
-26
@@ -1,35 +1,106 @@
|
||||
package c
|
||||
|
||||
import b "core:builtin"
|
||||
import builtin "core:builtin"
|
||||
|
||||
CHAR_BIT :: 8;
|
||||
char :: builtin.u8 // assuming -funsigned-char
|
||||
|
||||
bool :: b.bool;
|
||||
char :: b.u8;
|
||||
byte :: b.byte;
|
||||
schar :: b.i8;
|
||||
uchar :: b.u8;
|
||||
short :: b.i16;
|
||||
ushort :: b.u16;
|
||||
int :: b.i32;
|
||||
uint :: b.u32;
|
||||
schar :: builtin.i8
|
||||
short :: builtin.i16
|
||||
int :: builtin.i32
|
||||
long :: builtin.i32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.i64
|
||||
longlong :: builtin.i64
|
||||
|
||||
long :: b.i32 when (ODIN_OS == "windows" || size_of(b.rawptr) == 4) else b.i64;
|
||||
ulong :: b.u32 when (ODIN_OS == "windows" || size_of(b.rawptr) == 4) else b.u64;
|
||||
uchar :: builtin.u8
|
||||
ushort :: builtin.u16
|
||||
uint :: builtin.u32
|
||||
ulong :: builtin.u32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.u64
|
||||
ulonglong :: builtin.u64
|
||||
|
||||
longlong :: b.i64;
|
||||
ulonglong :: b.u64;
|
||||
float :: b.f32;
|
||||
double :: b.f64;
|
||||
complex_float :: b.complex64;
|
||||
complex_double :: b.complex128;
|
||||
bool :: builtin.bool
|
||||
|
||||
#assert(size_of(b.uintptr) == size_of(b.int));
|
||||
size_t :: builtin.uint
|
||||
ssize_t :: builtin.int
|
||||
wchar_t :: builtin.u16 when (ODIN_OS == .Windows) else builtin.u32
|
||||
|
||||
size_t :: b.uint;
|
||||
ssize_t :: b.int;
|
||||
ptrdiff_t :: b.int;
|
||||
uintptr_t :: b.uintptr;
|
||||
intptr_t :: b.int;
|
||||
float :: builtin.f32
|
||||
double :: builtin.f64
|
||||
complex_float :: builtin.complex64
|
||||
complex_double :: builtin.complex128
|
||||
|
||||
wchar_t :: b.u16 when (ODIN_OS == "windows") else b.u32;
|
||||
// 7.20.1 Integer types
|
||||
int8_t :: builtin.i8
|
||||
uint8_t :: builtin.u8
|
||||
int16_t :: builtin.i16
|
||||
uint16_t :: builtin.u16
|
||||
int32_t :: builtin.i32
|
||||
uint32_t :: builtin.u32
|
||||
int64_t :: builtin.i64
|
||||
uint64_t :: builtin.u64
|
||||
|
||||
// These are all the same in multiple libc's for multiple architectures.
|
||||
int_least8_t :: builtin.i8
|
||||
uint_least8_t :: builtin.u8
|
||||
int_least16_t :: builtin.i16
|
||||
uint_least16_t :: builtin.u16
|
||||
int_least32_t :: builtin.i32
|
||||
uint_least32_t :: builtin.u32
|
||||
int_least64_t :: builtin.i64
|
||||
uint_least64_t :: builtin.u64
|
||||
|
||||
// Same on Windows, Linux, and FreeBSD
|
||||
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||
int_fast8_t :: builtin.i8
|
||||
uint_fast8_t :: builtin.u8
|
||||
int_fast16_t :: builtin.i32
|
||||
uint_fast16_t :: builtin.u32
|
||||
int_fast32_t :: builtin.i32
|
||||
uint_fast32_t :: builtin.u32
|
||||
int_fast64_t :: builtin.i64
|
||||
uint_fast64_t :: builtin.u64
|
||||
} else {
|
||||
int_fast8_t :: builtin.i8
|
||||
uint_fast8_t :: builtin.u8
|
||||
int_fast16_t :: builtin.i16
|
||||
uint_fast16_t :: builtin.u16
|
||||
int_fast32_t :: builtin.i32
|
||||
uint_fast32_t :: builtin.u32
|
||||
int_fast64_t :: builtin.i64
|
||||
uint_fast64_t :: builtin.u64
|
||||
}
|
||||
|
||||
intptr_t :: builtin.int
|
||||
uintptr_t :: builtin.uintptr
|
||||
ptrdiff_t :: distinct intptr_t
|
||||
|
||||
intmax_t :: builtin.i64
|
||||
uintmax_t :: builtin.u64
|
||||
|
||||
// Copy C's rules for type promotion here by forcing the type on the literals.
|
||||
INT8_MAX :: int(0x7f)
|
||||
INT16_MAX :: int(0x7fff)
|
||||
INT32_MAX :: int(0x7fffffff)
|
||||
INT64_MAX :: longlong(0x7fffffffffffffff)
|
||||
|
||||
UINT8_MAX :: int(0xff)
|
||||
UINT16_MAX :: int(0xffff)
|
||||
UINT32_MAX :: uint(0xffffffff)
|
||||
UINT64_MAX :: ulonglong(0xffffffffffffffff)
|
||||
|
||||
INT8_MIN :: ~INT8_MAX
|
||||
INT16_MIN :: ~INT16_MAX
|
||||
INT32_MIN :: ~INT32_MAX
|
||||
INT64_MIN :: ~INT64_MAX
|
||||
|
||||
SIZE_MAX :: max(size_t)
|
||||
|
||||
PTRDIFF_MIN :: min(ptrdiff_t)
|
||||
PTRDIFF_MAX :: max(ptrdiff_t)
|
||||
|
||||
WCHAR_MIN :: min(wchar_t)
|
||||
WCHAR_MAX :: max(wchar_t)
|
||||
|
||||
NULL :: rawptr(uintptr(0))
|
||||
|
||||
NDEBUG :: !ODIN_DEBUG
|
||||
|
||||
CHAR_BIT :: 8
|
||||
|
||||
@@ -6,20 +6,20 @@ const_expr :: proc(rest: ^^Token, tok: ^Token) -> i64 {
|
||||
// TODO(bill): Handle const_expr correctly
|
||||
// This is effectively a mini-parser
|
||||
|
||||
assert(rest != nil);
|
||||
assert(tok != nil);
|
||||
rest^ = tokenizer.new_eof(tok);
|
||||
assert(rest != nil)
|
||||
assert(tok != nil)
|
||||
rest^ = tokenizer.new_eof(tok)
|
||||
switch v in tok.val {
|
||||
case i64:
|
||||
return v;
|
||||
return v
|
||||
case f64:
|
||||
return i64(v);
|
||||
return i64(v)
|
||||
case string:
|
||||
return 0;
|
||||
return 0
|
||||
case []u16:
|
||||
// TODO
|
||||
case []u32:
|
||||
// TODO
|
||||
}
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,150 +5,150 @@ import "core:unicode/utf8"
|
||||
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
||||
hex_to_int :: proc(c: byte) -> int {
|
||||
switch c {
|
||||
case '0'..'9': return int(c-'0');
|
||||
case 'a'..'f': return int(c-'a')+10;
|
||||
case 'A'..'F': return int(c-'A')+10;
|
||||
case '0'..='9': return int(c-'0')
|
||||
case 'a'..='f': return int(c-'a')+10
|
||||
case 'A'..='F': return int(c-'A')+10
|
||||
}
|
||||
return -1;
|
||||
return -1
|
||||
}
|
||||
w: int;
|
||||
w: int
|
||||
|
||||
if str[0] == quote && quote == '"' {
|
||||
return;
|
||||
return
|
||||
} else if str[0] >= 0x80 {
|
||||
r, w = utf8.decode_rune_in_string(str);
|
||||
return r, true, str[w:], true;
|
||||
r, w = utf8.decode_rune_in_string(str)
|
||||
return r, true, str[w:], true
|
||||
} else if str[0] != '\\' {
|
||||
return rune(str[0]), false, str[1:], true;
|
||||
return rune(str[0]), false, str[1:], true
|
||||
}
|
||||
|
||||
if len(str) <= 1 {
|
||||
return;
|
||||
return
|
||||
}
|
||||
s := str;
|
||||
c := s[1];
|
||||
s = s[2:];
|
||||
s := str
|
||||
c := s[1]
|
||||
s = s[2:]
|
||||
|
||||
switch c {
|
||||
case: r = rune(c);
|
||||
case: r = rune(c)
|
||||
|
||||
case 'a': r = '\a';
|
||||
case 'b': r = '\b';
|
||||
case 'e': r = '\e';
|
||||
case 'f': r = '\f';
|
||||
case 'n': r = '\n';
|
||||
case 'r': r = '\r';
|
||||
case 't': r = '\t';
|
||||
case 'v': r = '\v';
|
||||
case '\\': r = '\\';
|
||||
case 'a': r = '\a'
|
||||
case 'b': r = '\b'
|
||||
case 'e': r = '\e'
|
||||
case 'f': r = '\f'
|
||||
case 'n': r = '\n'
|
||||
case 'r': r = '\r'
|
||||
case 't': r = '\t'
|
||||
case 'v': r = '\v'
|
||||
case '\\': r = '\\'
|
||||
|
||||
case '"': r = '"';
|
||||
case '\'': r = '\'';
|
||||
case '"': r = '"'
|
||||
case '\'': r = '\''
|
||||
|
||||
case '0'..'7':
|
||||
v := int(c-'0');
|
||||
case '0'..='7':
|
||||
v := int(c-'0')
|
||||
if len(s) < 2 {
|
||||
return;
|
||||
return
|
||||
}
|
||||
for i in 0..<len(s) {
|
||||
d := int(s[i]-'0');
|
||||
d := int(s[i]-'0')
|
||||
if d < 0 || d > 7 {
|
||||
return;
|
||||
return
|
||||
}
|
||||
v = (v<<3) | d;
|
||||
v = (v<<3) | d
|
||||
}
|
||||
s = s[2:];
|
||||
s = s[2:]
|
||||
if v > 0xff {
|
||||
return;
|
||||
return
|
||||
}
|
||||
r = rune(v);
|
||||
r = rune(v)
|
||||
|
||||
case 'x', 'u', 'U':
|
||||
count: int;
|
||||
count: int
|
||||
switch c {
|
||||
case 'x': count = 2;
|
||||
case 'u': count = 4;
|
||||
case 'U': count = 8;
|
||||
case 'x': count = 2
|
||||
case 'u': count = 4
|
||||
case 'U': count = 8
|
||||
}
|
||||
|
||||
if len(s) < count {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
for i in 0..<count {
|
||||
d := hex_to_int(s[i]);
|
||||
d := hex_to_int(s[i])
|
||||
if d < 0 {
|
||||
return;
|
||||
return
|
||||
}
|
||||
r = (r<<4) | rune(d);
|
||||
r = (r<<4) | rune(d)
|
||||
}
|
||||
s = s[count:];
|
||||
s = s[count:]
|
||||
if c == 'x' {
|
||||
break;
|
||||
break
|
||||
}
|
||||
if r > utf8.MAX_RUNE {
|
||||
return;
|
||||
return
|
||||
}
|
||||
multiple_bytes = true;
|
||||
multiple_bytes = true
|
||||
}
|
||||
|
||||
success = true;
|
||||
tail_string = s;
|
||||
return;
|
||||
success = true
|
||||
tail_string = s
|
||||
return
|
||||
}
|
||||
|
||||
unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: string, allocated, success: bool) {
|
||||
contains_rune :: proc(s: string, r: rune) -> int {
|
||||
for c, offset in s {
|
||||
if c == r {
|
||||
return offset;
|
||||
return offset
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
return -1
|
||||
}
|
||||
|
||||
assert(len(lit) >= 2);
|
||||
assert(len(lit) >= 2)
|
||||
|
||||
s := lit;
|
||||
quote := '"';
|
||||
s := lit
|
||||
quote := '"'
|
||||
|
||||
if s == `""` {
|
||||
return "", false, true;
|
||||
return "", false, true
|
||||
}
|
||||
|
||||
if contains_rune(s, '\n') >= 0 {
|
||||
return s, false, false;
|
||||
return s, false, false
|
||||
}
|
||||
|
||||
if contains_rune(s, '\\') < 0 && contains_rune(s, quote) < 0 {
|
||||
if quote == '"' {
|
||||
return s, false, true;
|
||||
return s, false, true
|
||||
}
|
||||
}
|
||||
s = s[1:len(s)-1];
|
||||
s = s[1:len(s)-1]
|
||||
|
||||
|
||||
buf_len := 3*len(s) / 2;
|
||||
buf := make([]byte, buf_len, allocator);
|
||||
offset := 0;
|
||||
buf_len := 3*len(s) / 2
|
||||
buf := make([]byte, buf_len, allocator)
|
||||
offset := 0
|
||||
for len(s) > 0 {
|
||||
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote));
|
||||
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote))
|
||||
if !ok {
|
||||
delete(buf);
|
||||
return s, false, false;
|
||||
delete(buf)
|
||||
return s, false, false
|
||||
}
|
||||
s = tail_string;
|
||||
s = tail_string
|
||||
if r < 0x80 || !multiple_bytes {
|
||||
buf[offset] = byte(r);
|
||||
offset += 1;
|
||||
buf[offset] = byte(r)
|
||||
offset += 1
|
||||
} else {
|
||||
b, w := utf8.encode_rune(r);
|
||||
copy(buf[offset:], b[:w]);
|
||||
offset += w;
|
||||
b, w := utf8.encode_rune(r)
|
||||
copy(buf[offset:], b[:w])
|
||||
offset += w
|
||||
}
|
||||
}
|
||||
|
||||
new_string := string(buf[:offset]);
|
||||
new_string := string(buf[:offset])
|
||||
|
||||
return new_string, true, true;
|
||||
return new_string, true, true
|
||||
}
|
||||
|
||||
@@ -11,58 +11,58 @@ Hide_Set :: struct {
|
||||
|
||||
|
||||
new_hide_set :: proc(name: string) -> ^Hide_Set {
|
||||
hs := new(Hide_Set);
|
||||
hs.name = name;
|
||||
return hs;
|
||||
hs := new(Hide_Set)
|
||||
hs.name = name
|
||||
return hs
|
||||
}
|
||||
|
||||
hide_set_contains :: proc(hs: ^Hide_Set, name: string) -> bool {
|
||||
for h := hs; h != nil; h = h.next {
|
||||
if h.name == name {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
hide_set_union :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
|
||||
head: Hide_Set;
|
||||
curr := &head;
|
||||
head: Hide_Set
|
||||
curr := &head
|
||||
|
||||
for h := a; h != nil; h = h.next {
|
||||
curr.next = new_hide_set(h.name);
|
||||
curr = curr.next;
|
||||
curr.next = new_hide_set(h.name)
|
||||
curr = curr.next
|
||||
}
|
||||
curr.next = b;
|
||||
return head.next;
|
||||
curr.next = b
|
||||
return head.next
|
||||
}
|
||||
|
||||
|
||||
hide_set_intersection :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
|
||||
head: Hide_Set;
|
||||
curr := &head;
|
||||
head: Hide_Set
|
||||
curr := &head
|
||||
|
||||
for h := a; h != nil; h = h.next {
|
||||
if hide_set_contains(b, h.name) {
|
||||
curr.next = new_hide_set(h.name);
|
||||
curr = curr.next;
|
||||
curr.next = new_hide_set(h.name)
|
||||
curr = curr.next
|
||||
}
|
||||
}
|
||||
return head.next;
|
||||
return head.next
|
||||
}
|
||||
|
||||
|
||||
add_hide_set :: proc(tok: ^Token, hs: ^Hide_Set) -> ^Token {
|
||||
head: Token;
|
||||
curr := &head;
|
||||
head: Token
|
||||
curr := &head
|
||||
|
||||
tok := tok;
|
||||
tok := tok
|
||||
for ; tok != nil; tok = tok.next {
|
||||
t := copy_token(tok);
|
||||
t.hide_set = hide_set_union(t.hide_set, hs);
|
||||
curr.next = t;
|
||||
curr = curr.next;
|
||||
t := copy_token(tok)
|
||||
t.hide_set = hide_set_union(t.hide_set, hs)
|
||||
curr.next = t
|
||||
curr = curr.next
|
||||
}
|
||||
return head.next;
|
||||
return head.next
|
||||
}
|
||||
|
||||
@@ -80,29 +80,29 @@ Token :: struct {
|
||||
origin: ^Token,
|
||||
}
|
||||
|
||||
Is_Keyword_Proc :: #type proc(tok: ^Token) -> bool;
|
||||
Is_Keyword_Proc :: #type proc(tok: ^Token) -> bool
|
||||
|
||||
copy_token :: proc(tok: ^Token) -> ^Token {
|
||||
t := new_clone(tok^);
|
||||
t.next = nil;
|
||||
return t;
|
||||
t, _ := new_clone(tok^)
|
||||
t.next = nil
|
||||
return t
|
||||
}
|
||||
|
||||
new_eof :: proc(tok: ^Token) -> ^Token {
|
||||
t := new_clone(tok^);
|
||||
t.kind = .EOF;
|
||||
t.lit = "";
|
||||
return t;
|
||||
t, _ := new_clone(tok^)
|
||||
t.kind = .EOF
|
||||
t.lit = ""
|
||||
return t
|
||||
}
|
||||
|
||||
default_is_keyword :: proc(tok: ^Token) -> bool {
|
||||
if tok.kind == .Keyword {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
if len(tok.lit) > 0 {
|
||||
return default_keyword_set[tok.lit];
|
||||
return default_keyword_set[tok.lit]
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ token_name := [Token_Kind]string {
|
||||
.PP_Number = "preprocessor number",
|
||||
.Comment = "comment",
|
||||
.EOF = "eof",
|
||||
};
|
||||
}
|
||||
|
||||
default_keyword_set := map[string]bool{
|
||||
"auto" = true,
|
||||
@@ -166,4 +166,4 @@ default_keyword_set := map[string]bool{
|
||||
"__restrict__" = true,
|
||||
"__thread" = true,
|
||||
"__attribute__" = true,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
|
||||
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
|
||||
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any)
|
||||
|
||||
|
||||
Tokenizer :: struct {
|
||||
@@ -34,415 +34,415 @@ Tokenizer :: struct {
|
||||
}
|
||||
|
||||
init_defaults :: proc(t: ^Tokenizer, err: Error_Handler = default_error_handler, warn: Error_Handler = default_warn_handler) {
|
||||
t.err = err;
|
||||
t.warn = warn;
|
||||
t.err = err
|
||||
t.warn = warn
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> (pos: Pos) {
|
||||
pos.file = t.path;
|
||||
pos.offset = offset;
|
||||
pos.line = t.line_count;
|
||||
pos.column = offset - t.line_offset + 1;
|
||||
return;
|
||||
pos.file = t.path
|
||||
pos.offset = offset
|
||||
pos.line = t.line_count
|
||||
pos.column = offset - t.line_offset + 1
|
||||
return
|
||||
}
|
||||
|
||||
default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
||||
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column);
|
||||
fmt.eprintf(msg, ..args);
|
||||
fmt.eprintf("\n");
|
||||
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column)
|
||||
fmt.eprintf(msg, ..args)
|
||||
fmt.eprintf("\n")
|
||||
}
|
||||
|
||||
default_warn_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
||||
fmt.eprintf("%s(%d:%d) warning: ", pos.file, pos.line, pos.column);
|
||||
fmt.eprintf(msg, ..args);
|
||||
fmt.eprintf("\n");
|
||||
fmt.eprintf("%s(%d:%d) warning: ", pos.file, pos.line, pos.column)
|
||||
fmt.eprintf(msg, ..args)
|
||||
fmt.eprintf("\n")
|
||||
}
|
||||
|
||||
error_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
||||
pos := offset_to_pos(t, offset);
|
||||
pos := offset_to_pos(t, offset)
|
||||
if t.err != nil {
|
||||
t.err(pos, msg, ..args);
|
||||
t.err(pos, msg, ..args)
|
||||
}
|
||||
t.error_count += 1;
|
||||
t.error_count += 1
|
||||
}
|
||||
|
||||
warn_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
||||
pos := offset_to_pos(t, offset);
|
||||
pos := offset_to_pos(t, offset)
|
||||
if t.warn != nil {
|
||||
t.warn(pos, msg, ..args);
|
||||
t.warn(pos, msg, ..args)
|
||||
}
|
||||
t.warning_count += 1;
|
||||
t.warning_count += 1
|
||||
}
|
||||
|
||||
error :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
|
||||
pos := tok.pos;
|
||||
pos := tok.pos
|
||||
if t.err != nil {
|
||||
t.err(pos, msg, ..args);
|
||||
t.err(pos, msg, ..args)
|
||||
}
|
||||
t.error_count += 1;
|
||||
t.error_count += 1
|
||||
}
|
||||
|
||||
warn :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
|
||||
pos := tok.pos;
|
||||
pos := tok.pos
|
||||
if t.warn != nil {
|
||||
t.warn(pos, msg, ..args);
|
||||
t.warn(pos, msg, ..args)
|
||||
}
|
||||
t.warning_count += 1;
|
||||
t.warning_count += 1
|
||||
}
|
||||
|
||||
|
||||
advance_rune :: proc(t: ^Tokenizer) {
|
||||
if t.read_offset < len(t.src) {
|
||||
t.offset = t.read_offset;
|
||||
t.offset = t.read_offset
|
||||
if t.ch == '\n' {
|
||||
t.at_bol = true;
|
||||
t.line_offset = t.offset;
|
||||
t.line_count += 1;
|
||||
t.at_bol = true
|
||||
t.line_offset = t.offset
|
||||
t.line_count += 1
|
||||
}
|
||||
r, w := rune(t.src[t.read_offset]), 1;
|
||||
r, w := rune(t.src[t.read_offset]), 1
|
||||
switch {
|
||||
case r == 0:
|
||||
error_offset(t, t.offset, "illegal character NUL");
|
||||
error_offset(t, t.offset, "illegal character NUL")
|
||||
case r >= utf8.RUNE_SELF:
|
||||
r, w = utf8.decode_rune(t.src[t.read_offset:]);
|
||||
r, w = utf8.decode_rune(t.src[t.read_offset:])
|
||||
if r == utf8.RUNE_ERROR && w == 1 {
|
||||
error_offset(t, t.offset, "illegal UTF-8 encoding");
|
||||
error_offset(t, t.offset, "illegal UTF-8 encoding")
|
||||
} else if r == utf8.RUNE_BOM && t.offset > 0 {
|
||||
error_offset(t, t.offset, "illegal byte order mark");
|
||||
error_offset(t, t.offset, "illegal byte order mark")
|
||||
}
|
||||
}
|
||||
t.read_offset += w;
|
||||
t.ch = r;
|
||||
t.read_offset += w
|
||||
t.ch = r
|
||||
} else {
|
||||
t.offset = len(t.src);
|
||||
t.offset = len(t.src)
|
||||
if t.ch == '\n' {
|
||||
t.at_bol = true;
|
||||
t.line_offset = t.offset;
|
||||
t.line_count += 1;
|
||||
t.at_bol = true
|
||||
t.line_offset = t.offset
|
||||
t.line_count += 1
|
||||
}
|
||||
t.ch = -1;
|
||||
t.ch = -1
|
||||
}
|
||||
}
|
||||
|
||||
advance_rune_n :: proc(t: ^Tokenizer, n: int) {
|
||||
for in 0..<n {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
}
|
||||
|
||||
is_digit :: proc(r: rune) -> bool {
|
||||
return '0' <= r && r <= '9';
|
||||
return '0' <= r && r <= '9'
|
||||
}
|
||||
|
||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||
for {
|
||||
switch t.ch {
|
||||
case ' ', '\t', '\r', '\v', '\f', '\n':
|
||||
t.has_space = true;
|
||||
advance_rune(t);
|
||||
t.has_space = true
|
||||
advance_rune(t)
|
||||
case:
|
||||
return;
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan_comment :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1;
|
||||
next := -1;
|
||||
offset := t.offset-1
|
||||
next := -1
|
||||
general: {
|
||||
if t.ch == '/'{ // line comments
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
for t.ch != '\n' && t.ch >= 0 {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
|
||||
next = t.offset;
|
||||
next = t.offset
|
||||
if t.ch == '\n' {
|
||||
next += 1;
|
||||
next += 1
|
||||
}
|
||||
break general;
|
||||
break general
|
||||
}
|
||||
|
||||
/* style comment */
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
for t.ch >= 0 {
|
||||
ch := t.ch;
|
||||
advance_rune(t);
|
||||
ch := t.ch
|
||||
advance_rune(t)
|
||||
if ch == '*' && t.ch == '/' {
|
||||
advance_rune(t);
|
||||
next = t.offset;
|
||||
break general;
|
||||
advance_rune(t)
|
||||
next = t.offset
|
||||
break general
|
||||
}
|
||||
}
|
||||
|
||||
error_offset(t, offset, "comment not terminated");
|
||||
error_offset(t, offset, "comment not terminated")
|
||||
}
|
||||
|
||||
lit := t.src[offset : t.offset];
|
||||
lit := t.src[offset : t.offset]
|
||||
|
||||
// NOTE(bill): Strip CR for line comments
|
||||
for len(lit) > 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
|
||||
lit = lit[:len(lit)-1];
|
||||
lit = lit[:len(lit)-1]
|
||||
}
|
||||
|
||||
|
||||
return string(lit);
|
||||
return string(lit)
|
||||
}
|
||||
|
||||
scan_identifier :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset;
|
||||
offset := t.offset
|
||||
|
||||
for is_ident1(t.ch) {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset]);
|
||||
return string(t.src[offset : t.offset])
|
||||
}
|
||||
|
||||
scan_string :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1;
|
||||
offset := t.offset-1
|
||||
|
||||
for {
|
||||
ch := t.ch;
|
||||
ch := t.ch
|
||||
if ch == '\n' || ch < 0 {
|
||||
error_offset(t, offset, "string literal was not terminated");
|
||||
break;
|
||||
error_offset(t, offset, "string literal was not terminated")
|
||||
break
|
||||
}
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
if ch == '"' {
|
||||
break;
|
||||
break
|
||||
}
|
||||
if ch == '\\' {
|
||||
scan_escape(t);
|
||||
scan_escape(t)
|
||||
}
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset]);
|
||||
return string(t.src[offset : t.offset])
|
||||
}
|
||||
|
||||
digit_val :: proc(r: rune) -> int {
|
||||
switch r {
|
||||
case '0'..'9':
|
||||
return int(r-'0');
|
||||
case 'A'..'F':
|
||||
return int(r-'A' + 10);
|
||||
case 'a'..'f':
|
||||
return int(r-'a' + 10);
|
||||
case '0'..='9':
|
||||
return int(r-'0')
|
||||
case 'A'..='F':
|
||||
return int(r-'A' + 10)
|
||||
case 'a'..='f':
|
||||
return int(r-'a' + 10)
|
||||
}
|
||||
return 16;
|
||||
return 16
|
||||
}
|
||||
|
||||
scan_escape :: proc(t: ^Tokenizer) -> bool {
|
||||
offset := t.offset;
|
||||
offset := t.offset
|
||||
|
||||
esc := t.ch;
|
||||
n: int;
|
||||
base, max: u32;
|
||||
esc := t.ch
|
||||
n: int
|
||||
base, max: u32
|
||||
switch esc {
|
||||
case 'a', 'b', 'e', 'f', 'n', 't', 'v', 'r', '\\', '\'', '"':
|
||||
advance_rune(t);
|
||||
return true;
|
||||
advance_rune(t)
|
||||
return true
|
||||
|
||||
case '0'..'7':
|
||||
case '0'..='7':
|
||||
for digit_val(t.ch) < 8 {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
return true;
|
||||
return true
|
||||
case 'x':
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
for digit_val(t.ch) < 16 {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
return true;
|
||||
return true
|
||||
case 'u':
|
||||
advance_rune(t);
|
||||
n, base, max = 4, 16, utf8.MAX_RUNE;
|
||||
advance_rune(t)
|
||||
n, base, max = 4, 16, utf8.MAX_RUNE
|
||||
case 'U':
|
||||
advance_rune(t);
|
||||
n, base, max = 8, 16, utf8.MAX_RUNE;
|
||||
advance_rune(t)
|
||||
n, base, max = 8, 16, utf8.MAX_RUNE
|
||||
case:
|
||||
if t.ch < 0 {
|
||||
error_offset(t, offset, "escape sequence was not terminated");
|
||||
error_offset(t, offset, "escape sequence was not terminated")
|
||||
} else {
|
||||
break;
|
||||
break
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
x: u32;
|
||||
x: u32
|
||||
main_loop: for n > 0 {
|
||||
d := u32(digit_val(t.ch));
|
||||
d := u32(digit_val(t.ch))
|
||||
if d >= base {
|
||||
if t.ch == '"' || t.ch == '\'' {
|
||||
break main_loop;
|
||||
break main_loop
|
||||
}
|
||||
if t.ch < 0 {
|
||||
error_offset(t, t.offset, "escape sequence was not terminated");
|
||||
error_offset(t, t.offset, "escape sequence was not terminated")
|
||||
} else {
|
||||
error_offset(t, t.offset, "illegal character '%r' : %d in escape sequence", t.ch, t.ch);
|
||||
error_offset(t, t.offset, "illegal character '%r' : %d in escape sequence", t.ch, t.ch)
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
x = x*base + d;
|
||||
advance_rune(t);
|
||||
n -= 1;
|
||||
x = x*base + d
|
||||
advance_rune(t)
|
||||
n -= 1
|
||||
}
|
||||
|
||||
if x > max || 0xd800 <= x && x <= 0xe000 {
|
||||
error_offset(t, offset, "escape sequence is an invalid Unicode code point");
|
||||
return false;
|
||||
error_offset(t, offset, "escape sequence is an invalid Unicode code point")
|
||||
return false
|
||||
}
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
scan_rune :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1;
|
||||
valid := true;
|
||||
n := 0;
|
||||
offset := t.offset-1
|
||||
valid := true
|
||||
n := 0
|
||||
for {
|
||||
ch := t.ch;
|
||||
ch := t.ch
|
||||
if ch == '\n' || ch < 0 {
|
||||
if valid {
|
||||
error_offset(t, offset, "rune literal not terminated");
|
||||
valid = false;
|
||||
error_offset(t, offset, "rune literal not terminated")
|
||||
valid = false
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
if ch == '\'' {
|
||||
break;
|
||||
break
|
||||
}
|
||||
n += 1;
|
||||
n += 1
|
||||
if ch == '\\' {
|
||||
if !scan_escape(t) {
|
||||
valid = false;
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if valid && n != 1 {
|
||||
error_offset(t, offset, "illegal rune literal");
|
||||
error_offset(t, offset, "illegal rune literal")
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset]);
|
||||
return string(t.src[offset : t.offset])
|
||||
}
|
||||
|
||||
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) {
|
||||
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
|
||||
for digit_val(t.ch) < base {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
}
|
||||
scan_exponent :: proc(t: ^Tokenizer) {
|
||||
if t.ch == 'e' || t.ch == 'E' || t.ch == 'p' || t.ch == 'P' {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
if t.ch == '-' || t.ch == '+' {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
if digit_val(t.ch) < 10 {
|
||||
scan_mantissa(t, 10);
|
||||
scan_mantissa(t, 10)
|
||||
} else {
|
||||
error_offset(t, t.offset, "illegal floating-point exponent");
|
||||
error_offset(t, t.offset, "illegal floating-point exponent")
|
||||
}
|
||||
}
|
||||
}
|
||||
scan_fraction :: proc(t: ^Tokenizer) -> (early_exit: bool) {
|
||||
if t.ch == '.' && peek(t) == '.' {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
if t.ch == '.' {
|
||||
advance_rune(t);
|
||||
scan_mantissa(t, 10);
|
||||
advance_rune(t)
|
||||
scan_mantissa(t, 10)
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
check_end := true;
|
||||
check_end := true
|
||||
|
||||
|
||||
offset := t.offset;
|
||||
seen_point := seen_decimal_point;
|
||||
offset := t.offset
|
||||
seen_point := seen_decimal_point
|
||||
|
||||
if seen_point {
|
||||
offset -= 1;
|
||||
scan_mantissa(t, 10);
|
||||
scan_exponent(t);
|
||||
offset -= 1
|
||||
scan_mantissa(t, 10)
|
||||
scan_exponent(t)
|
||||
} else {
|
||||
if t.ch == '0' {
|
||||
int_base :: proc(t: ^Tokenizer, base: int, msg: string) {
|
||||
prev := t.offset;
|
||||
advance_rune(t);
|
||||
scan_mantissa(t, base);
|
||||
prev := t.offset
|
||||
advance_rune(t)
|
||||
scan_mantissa(t, base)
|
||||
if t.offset - prev <= 1 {
|
||||
error_offset(t, t.offset, msg);
|
||||
error_offset(t, t.offset, msg)
|
||||
}
|
||||
}
|
||||
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
switch t.ch {
|
||||
case 'b', 'B':
|
||||
int_base(t, 2, "illegal binary integer");
|
||||
int_base(t, 2, "illegal binary integer")
|
||||
case 'x', 'X':
|
||||
int_base(t, 16, "illegal hexadecimal integer");
|
||||
int_base(t, 16, "illegal hexadecimal integer")
|
||||
case:
|
||||
seen_point = false;
|
||||
scan_mantissa(t, 10);
|
||||
seen_point = false
|
||||
scan_mantissa(t, 10)
|
||||
if t.ch == '.' {
|
||||
seen_point = true;
|
||||
seen_point = true
|
||||
if scan_fraction(t) {
|
||||
check_end = false;
|
||||
check_end = false
|
||||
}
|
||||
}
|
||||
if check_end {
|
||||
scan_exponent(t);
|
||||
check_end = false;
|
||||
scan_exponent(t)
|
||||
check_end = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if check_end {
|
||||
scan_mantissa(t, 10);
|
||||
scan_mantissa(t, 10)
|
||||
|
||||
if !scan_fraction(t) {
|
||||
scan_exponent(t);
|
||||
scan_exponent(t)
|
||||
}
|
||||
}
|
||||
|
||||
return .Number, string(t.src[offset : t.offset]);
|
||||
return .Number, string(t.src[offset : t.offset])
|
||||
}
|
||||
|
||||
scan_punct :: proc(t: ^Tokenizer, ch: rune) -> (kind: Token_Kind) {
|
||||
kind = .Punct;
|
||||
kind = .Punct
|
||||
switch ch {
|
||||
case:
|
||||
kind = .Invalid;
|
||||
kind = .Invalid
|
||||
|
||||
case '<', '>':
|
||||
if t.ch == ch {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
case '!', '+', '-', '*', '/', '%', '^', '=':
|
||||
if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
case '#':
|
||||
if t.ch == '#' {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
case '&':
|
||||
if t.ch == '=' || t.ch == '&' {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
case '|':
|
||||
if t.ch == '=' || t.ch == '|' {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
case '(', ')', '[', ']', '{', '}':
|
||||
// okay
|
||||
@@ -452,216 +452,216 @@ scan_punct :: proc(t: ^Tokenizer, ch: rune) -> (kind: Token_Kind) {
|
||||
// okay
|
||||
case '.':
|
||||
if t.ch == '.' && peek(t) == '.' {
|
||||
advance_rune(t);
|
||||
advance_rune(t); // consume last '.'
|
||||
advance_rune(t)
|
||||
advance_rune(t) // consume last '.'
|
||||
}
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
peek :: proc(t: ^Tokenizer) -> byte {
|
||||
if t.read_offset < len(t.src) {
|
||||
return t.src[t.read_offset];
|
||||
return t.src[t.read_offset]
|
||||
}
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
peek_str :: proc(t: ^Tokenizer, str: string) -> bool {
|
||||
if t.read_offset < len(t.src) {
|
||||
return strings.has_prefix(string(t.src[t.offset:]), str);
|
||||
return strings.has_prefix(string(t.src[t.offset:]), str)
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
scan_literal_prefix :: proc(t: ^Tokenizer, str: string, prefix: ^string) -> bool {
|
||||
if peek_str(t, str) {
|
||||
offset := t.offset;
|
||||
offset := t.offset
|
||||
for _ in str {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
prefix^ = string(t.src[offset:][:len(str)-1]);
|
||||
return true;
|
||||
prefix^ = string(t.src[offset:][:len(str)-1])
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
allow_next_to_be_newline :: proc(t: ^Tokenizer) -> bool {
|
||||
if t.ch == '\n' {
|
||||
advance_rune(t);
|
||||
return true;
|
||||
advance_rune(t)
|
||||
return true
|
||||
} else if t.ch == '\r' && peek(t) == '\n' { // allow for MS-DOS style line endings
|
||||
advance_rune(t); // \r
|
||||
advance_rune(t); // \n
|
||||
return true;
|
||||
advance_rune(t) // \r
|
||||
advance_rune(t) // \n
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
scan :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
|
||||
skip_whitespace(t);
|
||||
skip_whitespace(t)
|
||||
|
||||
offset := t.offset;
|
||||
offset := t.offset
|
||||
|
||||
kind: Token_Kind;
|
||||
lit: string;
|
||||
prefix: string;
|
||||
kind: Token_Kind
|
||||
lit: string
|
||||
prefix: string
|
||||
|
||||
switch ch := t.ch; {
|
||||
case scan_literal_prefix(t, `u8"`, &prefix):
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
kind = .String
|
||||
lit = scan_string(t)
|
||||
case scan_literal_prefix(t, `u"`, &prefix):
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
kind = .String
|
||||
lit = scan_string(t)
|
||||
case scan_literal_prefix(t, `L"`, &prefix):
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
kind = .String
|
||||
lit = scan_string(t)
|
||||
case scan_literal_prefix(t, `U"`, &prefix):
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
kind = .String
|
||||
lit = scan_string(t)
|
||||
case scan_literal_prefix(t, `u'`, &prefix):
|
||||
kind = .Char;
|
||||
lit = scan_rune(t);
|
||||
kind = .Char
|
||||
lit = scan_rune(t)
|
||||
case scan_literal_prefix(t, `L'`, &prefix):
|
||||
kind = .Char;
|
||||
lit = scan_rune(t);
|
||||
kind = .Char
|
||||
lit = scan_rune(t)
|
||||
case scan_literal_prefix(t, `U'`, &prefix):
|
||||
kind = .Char;
|
||||
lit = scan_rune(t);
|
||||
kind = .Char
|
||||
lit = scan_rune(t)
|
||||
|
||||
case is_ident0(ch):
|
||||
lit = scan_identifier(t);
|
||||
kind = .Ident;
|
||||
lit = scan_identifier(t)
|
||||
kind = .Ident
|
||||
case '0' <= ch && ch <= '9':
|
||||
kind, lit = scan_number(t, false);
|
||||
kind, lit = scan_number(t, false)
|
||||
case:
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
switch ch {
|
||||
case -1:
|
||||
kind = .EOF;
|
||||
kind = .EOF
|
||||
case '\\':
|
||||
kind = .Punct;
|
||||
kind = .Punct
|
||||
if allow_next_to_be_newline(t) {
|
||||
t.at_bol = true;
|
||||
t.has_space = false;
|
||||
return scan(t, f);
|
||||
t.at_bol = true
|
||||
t.has_space = false
|
||||
return scan(t, f)
|
||||
}
|
||||
|
||||
case '.':
|
||||
if is_digit(t.ch) {
|
||||
kind, lit = scan_number(t, true);
|
||||
kind, lit = scan_number(t, true)
|
||||
} else {
|
||||
kind = scan_punct(t, ch);
|
||||
kind = scan_punct(t, ch)
|
||||
}
|
||||
case '"':
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
kind = .String
|
||||
lit = scan_string(t)
|
||||
case '\'':
|
||||
kind = .Char;
|
||||
lit = scan_rune(t);
|
||||
kind = .Char
|
||||
lit = scan_rune(t)
|
||||
case '/':
|
||||
if t.ch == '/' || t.ch == '*' {
|
||||
kind = .Comment;
|
||||
lit = scan_comment(t);
|
||||
t.has_space = true;
|
||||
break;
|
||||
kind = .Comment
|
||||
lit = scan_comment(t)
|
||||
t.has_space = true
|
||||
break
|
||||
}
|
||||
fallthrough;
|
||||
fallthrough
|
||||
case:
|
||||
kind = scan_punct(t, ch);
|
||||
kind = scan_punct(t, ch)
|
||||
if kind == .Invalid && ch != utf8.RUNE_BOM {
|
||||
error_offset(t, t.offset, "illegal character '%r': %d", ch, ch);
|
||||
error_offset(t, t.offset, "illegal character '%r': %d", ch, ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if lit == "" {
|
||||
lit = string(t.src[offset : t.offset]);
|
||||
lit = string(t.src[offset : t.offset])
|
||||
}
|
||||
|
||||
if kind == .Comment {
|
||||
return scan(t, f);
|
||||
return scan(t, f)
|
||||
}
|
||||
|
||||
tok := new(Token);
|
||||
tok.kind = kind;
|
||||
tok.lit = lit;
|
||||
tok.pos = offset_to_pos(t, offset);
|
||||
tok.file = f;
|
||||
tok.prefix = prefix;
|
||||
tok.at_bol = t.at_bol;
|
||||
tok.has_space = t.has_space;
|
||||
tok := new(Token)
|
||||
tok.kind = kind
|
||||
tok.lit = lit
|
||||
tok.pos = offset_to_pos(t, offset)
|
||||
tok.file = f
|
||||
tok.prefix = prefix
|
||||
tok.at_bol = t.at_bol
|
||||
tok.has_space = t.has_space
|
||||
|
||||
t.at_bol, t.has_space = false, false;
|
||||
t.at_bol, t.has_space = false, false
|
||||
|
||||
return tok;
|
||||
return tok
|
||||
}
|
||||
|
||||
tokenize :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
|
||||
setup_tokenizer: {
|
||||
t.src = f.src;
|
||||
t.ch = ' ';
|
||||
t.offset = 0;
|
||||
t.read_offset = 0;
|
||||
t.line_offset = 0;
|
||||
t.line_count = len(t.src) > 0 ? 1 : 0;
|
||||
t.error_count = 0;
|
||||
t.path = f.name;
|
||||
t.src = f.src
|
||||
t.ch = ' '
|
||||
t.offset = 0
|
||||
t.read_offset = 0
|
||||
t.line_offset = 0
|
||||
t.line_count = len(t.src) > 0 ? 1 : 0
|
||||
t.error_count = 0
|
||||
t.path = f.name
|
||||
|
||||
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
if t.ch == utf8.RUNE_BOM {
|
||||
advance_rune(t);
|
||||
advance_rune(t)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
t.at_bol = true;
|
||||
t.has_space = false;
|
||||
t.at_bol = true
|
||||
t.has_space = false
|
||||
|
||||
head: Token;
|
||||
curr := &head;
|
||||
head: Token
|
||||
curr := &head
|
||||
for {
|
||||
tok := scan(t, f);
|
||||
tok := scan(t, f)
|
||||
if tok == nil {
|
||||
break;
|
||||
break
|
||||
}
|
||||
curr.next = tok;
|
||||
curr = curr.next;
|
||||
curr.next = tok
|
||||
curr = curr.next
|
||||
if tok.kind == .EOF {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return head.next;
|
||||
return head.next
|
||||
}
|
||||
|
||||
add_new_file :: proc(t: ^Tokenizer, name: string, src: []byte, id: int) -> ^File {
|
||||
file := new(File);
|
||||
file.id = id;
|
||||
file.src = src;
|
||||
file.name = name;
|
||||
file.display_name = name;
|
||||
return file;
|
||||
file := new(File)
|
||||
file.id = id
|
||||
file.src = src
|
||||
file.name = name
|
||||
file.display_name = name
|
||||
return file
|
||||
}
|
||||
|
||||
tokenize_file :: proc(t: ^Tokenizer, path: string, id: int, loc := #caller_location) -> ^Token {
|
||||
src, ok := os.read_entire_file(path);
|
||||
src, ok := os.read_entire_file(path)
|
||||
if !ok {
|
||||
return nil;
|
||||
return nil
|
||||
}
|
||||
return tokenize(t, add_new_file(t, path, src, id));
|
||||
return tokenize(t, add_new_file(t, path, src, id))
|
||||
}
|
||||
|
||||
|
||||
inline_tokenize :: proc(t: ^Tokenizer, tok: ^Token, src: []byte) -> ^Token {
|
||||
file := new(File);
|
||||
file.src = src;
|
||||
file := new(File)
|
||||
file.src = src
|
||||
if tok.file != nil {
|
||||
file.id = tok.file.id;
|
||||
file.name = tok.file.name;
|
||||
file.display_name = tok.file.name;
|
||||
file.id = tok.file.id
|
||||
file.name = tok.file.name
|
||||
file.display_name = tok.file.name
|
||||
}
|
||||
|
||||
return tokenize(t, file);
|
||||
return tokenize(t, file)
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ package c_frontend_tokenizer
|
||||
in_range :: proc(range: []rune, c: rune) -> bool #no_bounds_check {
|
||||
for i := 0; range[i] != -1; i += 2 {
|
||||
if range[i] <= c && c <= range[i+1] {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,11 @@ in_range :: proc(range: []rune, c: rune) -> bool #no_bounds_check {
|
||||
//
|
||||
// is_ident0 returns true if a given character is acceptable as the first character of an identifier.
|
||||
is_ident0 :: proc(c: rune) -> bool {
|
||||
return in_range(_range_ident0, c);
|
||||
return in_range(_range_ident0, c)
|
||||
}
|
||||
// is_ident0 returns true if a given character is acceptable as a non-first character of an identifier.
|
||||
is_ident1 :: proc(c: rune) -> bool {
|
||||
return is_ident0(c) || in_range(_range_ident1, c);
|
||||
return is_ident0(c) || in_range(_range_ident1, c)
|
||||
}
|
||||
|
||||
// Returns the number of columns needed to display a given character in a fixed-width font.
|
||||
@@ -27,18 +27,18 @@ is_ident1 :: proc(c: rune) -> bool {
|
||||
char_width :: proc(c: rune) -> int {
|
||||
switch {
|
||||
case in_range(_range_width0, c):
|
||||
return 0;
|
||||
return 0
|
||||
case in_range(_range_width2, c):
|
||||
return 2;
|
||||
return 2
|
||||
}
|
||||
return 1;
|
||||
return 1
|
||||
}
|
||||
|
||||
display_width :: proc(str: string) -> (w: int) {
|
||||
for c in str {
|
||||
w += char_width(c);
|
||||
w += char_width(c)
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -59,12 +59,12 @@ _range_ident0 := []rune{
|
||||
0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD,
|
||||
0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD,
|
||||
-1,
|
||||
};
|
||||
}
|
||||
|
||||
_range_ident1 := []rune{
|
||||
'0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F,
|
||||
-1,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
_range_width0 := []rune{
|
||||
@@ -105,7 +105,7 @@ _range_width0 := []rune{
|
||||
0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD,
|
||||
0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF,
|
||||
-1,
|
||||
};
|
||||
}
|
||||
|
||||
_range_width2 := []rune{
|
||||
0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E,
|
||||
@@ -113,4 +113,4 @@ _range_width2 := []rune{
|
||||
0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644,
|
||||
0x20000, 0x2FFFD, 0x30000, 0x3FFFD,
|
||||
-1,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# C support
|
||||
|
||||
The following is a mostly-complete projection of the C11 standard library as defined by the C11 specification: N1570, or ISO/IEC 9899:2011. Only the macros, types, and functions as required by the standard are projected. Extensions to C, such as POSIX are not handled by these bindings, this is otherwise portable to any implementation which can support a hosted C runtime.
|
||||
|
||||
## Support matrix
|
||||
| Header | Status |
|
||||
|:------------------|:---------------------------------------------------|
|
||||
| `<assert.h>` | Not applicable, use Odin's `#assert` |
|
||||
| `<complex.h>` | Mostly projected, see [limitations](#Limitations) |
|
||||
| `<ctype.h>` | Fully projected |
|
||||
| `<errno.h>` | Fully projected |
|
||||
| `<fenv.h>` | Not projected |
|
||||
| `<float.h>` | Not projected |
|
||||
| `<inttypes.h>` | Fully projected |
|
||||
| `<iso646.h>` | Not applicable, use Odin's operators |
|
||||
| `<limits.h>` | Not projected |
|
||||
| `<locale.h>` | Not projected |
|
||||
| `<math.h>` | Mostly projected, see [limitations](#Limitations) |
|
||||
| `<setjmp.h>` | Fully projected |
|
||||
| `<signal.h>` | Fully projected |
|
||||
| `<stdalign.h>` | Not applicable, use Odin's `#align` |
|
||||
| `<stdarg.h>` | Mostly projected, see [limitations](#Limitations) |
|
||||
| `<stdatomic.h>` | Fully projected |
|
||||
| `<stdbool.h>` | Not applicable, use Odin's `b32` |
|
||||
| `<stddef.h>` | Mostly projected, see [limitations](#Limitations) |
|
||||
| `<stdint.h>` | Fully projected |
|
||||
| `<stdio.h>` | Fully projected |
|
||||
| `<stdlib.h>` | Fully projected |
|
||||
| `<stdnoreturn.h>` | Not applicable, use Odin's divergent return `!` |
|
||||
| `<string.h>` | Fully projected |
|
||||
| `<tgmath.h>` | Mostly projected, see [limitations](#Limitations) |
|
||||
| `<threads.h>` | Fully projected |
|
||||
| `<time.h>` | Fully projected |
|
||||
| `<uchar.h>` | Fully projected |
|
||||
| `<wchar.h>` | Fully projected |
|
||||
| `<wctype.h>` | Fully projected |
|
||||
|
||||
## Limitations
|
||||
Not all C standard library functionality can be fully projected due to language differences. These limitations are listed here.
|
||||
|
||||
### `long double`
|
||||
As Odin lacks a means to interact with `long double` in it's foreign interface, this projection effort does not bind or define anything requiring `long double` which is permitted by the C standard.
|
||||
|
||||
### `<complex.h>`
|
||||
The special values `_Complex_I`, `_Imaginary_I` and the appropriate definition of `I` cannot be realized with the same type in Odin as it would be in C. The literal `1i` is tempting to use for these definitions but the semantics differ from C and would be confusing to use.
|
||||
|
||||
### `<math.h>`
|
||||
The classification functions, e.g: `fpclassify` are required by C to be implemented as macros, meaning no implementation would expose functions in their library we could bind. Instead, we provide native Odin implementations with functionally equivalent semantics and behavior as the C ones. Unfortunately, since classification returns unspecified constant values this may be an ABI break where the value of those constants enter and exit native C code.
|
||||
|
||||
### `<stdarg.h>`
|
||||
While Odin can interact with variable argument C functions through the use of the `#c_vararg` attribute within a foreign block, it's not actually possible to create procedures in Odin with bodies that have the same ABI as that of variable argument C functions, as a result `va_arg` is not projected.
|
||||
|
||||
### `<stddef.h>`
|
||||
`offsetof` is not realizable in Odin, however you can use `offset_of` instead.
|
||||
|
||||
### `<tgmath.h>`
|
||||
C has some strange promotion and type-coercion behavior for `<tgmath.h>` which isn't correctly handled by this projection, specifically involving the use of complex arithmetic and kernels. We do mostly support type-generic math through the use of Odin's explicit procedure overloading, however the semantic behavior of that doesn't match C and so literal expressions of complex type in C may not call the same underlying math kernel functions as they do in Odin through this projection.
|
||||
|
||||
## Caveats
|
||||
|
||||
In addition to limitations, there are some minor caveats you should be aware when using this projection.
|
||||
|
||||
* `errno()` is a function which returns `^int` rather than a macro.
|
||||
* `MB_CUR_MAX()` is a function which return `size_t` rather than a macro.
|
||||
* Currently only works on Windows (MSVCRT) and Linux (GLIBC or MUSL)
|
||||
|
||||
## License
|
||||
Every file within this directory is made available under Odin's BSD-2 license
|
||||
with the following copyright.
|
||||
|
||||
```
|
||||
Copyright 2021 Dale Weiler <weilercdale@gmail.com>.
|
||||
```
|
||||
@@ -0,0 +1,84 @@
|
||||
package libc
|
||||
|
||||
// 7.3 Complex arithmetic
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.3.5 Trigonometric functions
|
||||
cacos :: proc(z: complex_double) -> complex_double ---
|
||||
cacosf :: proc(z: complex_float) -> complex_float ---
|
||||
casin :: proc(z: complex_double) -> complex_double ---
|
||||
casinf :: proc(z: complex_float) -> complex_float ---
|
||||
catan :: proc(z: complex_double) -> complex_double ---
|
||||
catanf :: proc(z: complex_float) -> complex_float ---
|
||||
ccos :: proc(z: complex_double) -> complex_double ---
|
||||
ccosf :: proc(z: complex_float) -> complex_float ---
|
||||
csin :: proc(z: complex_double) -> complex_double ---
|
||||
csinf :: proc(z: complex_float) -> complex_float ---
|
||||
ctan :: proc(z: complex_double) -> complex_double ---
|
||||
ctanf :: proc(z: complex_float) -> complex_float ---
|
||||
|
||||
// 7.3.6 Hyperbolic functions
|
||||
cacosh :: proc(z: complex_double) -> complex_double ---
|
||||
cacoshf :: proc(z: complex_float) -> complex_float ---
|
||||
casinh :: proc(z: complex_double) -> complex_double ---
|
||||
casinhf :: proc(z: complex_float) -> complex_float ---
|
||||
catanh :: proc(z: complex_double) -> complex_double ---
|
||||
catanhf :: proc(z: complex_float) -> complex_float ---
|
||||
ccosh :: proc(z: complex_double) -> complex_double ---
|
||||
ccoshf :: proc(z: complex_float) -> complex_float ---
|
||||
csinh :: proc(z: complex_double) -> complex_double ---
|
||||
csinhf :: proc(z: complex_float) -> complex_float ---
|
||||
ctanh :: proc(z: complex_double) -> complex_double ---
|
||||
ctanhf :: proc(z: complex_float) -> complex_float ---
|
||||
|
||||
// 7.3.7 Exponential and logarithmic functions
|
||||
cexp :: proc(z: complex_double) -> complex_double ---
|
||||
cexpf :: proc(z: complex_float) -> complex_float ---
|
||||
clog :: proc(z: complex_double) -> complex_double ---
|
||||
clogf :: proc(z: complex_float) -> complex_float ---
|
||||
|
||||
// 7.3.8 Power and absolute-value functions
|
||||
cabs :: proc(z: complex_double) -> complex_double ---
|
||||
cabsf :: proc(z: complex_float) -> complex_float ---
|
||||
cpow :: proc(z: complex_double) -> complex_double ---
|
||||
cpowf :: proc(z: complex_float) -> complex_float ---
|
||||
csqrt :: proc(z: complex_double) -> complex_double ---
|
||||
csqrtf :: proc(z: complex_float) -> complex_float ---
|
||||
|
||||
// 7.3.9 Manipulation functions
|
||||
carg :: proc(z: complex_double) -> double ---
|
||||
cargf :: proc(z: complex_float) -> float ---
|
||||
cimag :: proc(z: complex_double) -> double ---
|
||||
cimagf :: proc(z: complex_float) -> float ---
|
||||
conj :: proc(z: complex_double) -> complex_double ---
|
||||
conjf :: proc(z: complex_float) -> complex_float ---
|
||||
cproj :: proc(z: complex_double) -> complex_double ---
|
||||
cprojf :: proc(z: complex_float) -> complex_float ---
|
||||
creal :: proc(z: complex_double) -> double ---
|
||||
crealf :: proc(z: complex_float) -> float ---
|
||||
}
|
||||
|
||||
import builtin "core:builtin"
|
||||
|
||||
complex_float :: distinct builtin.complex64
|
||||
complex_double :: distinct builtin.complex128
|
||||
|
||||
// Cannot implement _Complex_I or _Imaginary_I in Odin, thus
|
||||
// complex and imaginary cannot be implement either.
|
||||
|
||||
CMPLX :: #force_inline proc(x, y: double) -> complex_double {
|
||||
return builtin.complex(x, y)
|
||||
}
|
||||
|
||||
CMPLXF :: #force_inline proc(x, y: float) -> complex_float {
|
||||
return builtin.complex(x, y)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package libc
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
// 7.4 Character handling
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.4.1 Character classification functions
|
||||
isalnum :: proc(c: int) -> int ---
|
||||
isalpha :: proc(c: int) -> int ---
|
||||
isblank :: proc(c: int) -> int ---
|
||||
iscntrl :: proc(c: int) -> int ---
|
||||
isdigit :: proc(c: int) -> int ---
|
||||
isgraph :: proc(c: int) -> int ---
|
||||
islower :: proc(c: int) -> int ---
|
||||
isprint :: proc(c: int) -> int ---
|
||||
ispunct :: proc(c: int) -> int ---
|
||||
isspace :: proc(c: int) -> int ---
|
||||
isupper :: proc(c: int) -> int ---
|
||||
isxdigit :: proc(c: int) -> int ---
|
||||
|
||||
// 7.4.2 Character case mapping functions
|
||||
tolower :: proc(c: int) -> int ---
|
||||
toupper :: proc(c: int) -> int ---
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package libc
|
||||
|
||||
// 7.5 Errors
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
// C11 standard only requires the definition of:
|
||||
// EDOM,
|
||||
// EILSEQ
|
||||
// ERANGE
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__libc_errno_location")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
EDOM :: 33
|
||||
EILSEQ :: 84
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
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 {
|
||||
@(link_name="_errno")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
EDOM :: 33
|
||||
EILSEQ :: 42
|
||||
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
|
||||
// it actually is.
|
||||
errno :: #force_inline proc() -> ^int {
|
||||
return _get_errno()
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
package libc
|
||||
|
||||
// 7.12 Mathematics
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
// To support C's tgmath behavior we use Odin's explicit procedure overloading,
|
||||
// but we cannot use the same names as exported by libc so use @(link_name)
|
||||
// and keep them as private symbols of name "libc_"
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.12.4 Trigonometric functions
|
||||
@(link_name="acos") libc_acos :: proc(x: double) -> double ---
|
||||
@(link_name="acosf") libc_acosf :: proc(x: float) -> float ---
|
||||
@(link_name="asin") libc_asin :: proc(x: double) -> double ---
|
||||
@(link_name="asinf") libc_asinf :: proc(x: float) -> float ---
|
||||
@(link_name="atan") libc_atan :: proc(x: double) -> double ---
|
||||
@(link_name="atanf") libc_atanf :: proc(x: float) -> float ---
|
||||
@(link_name="atan2") libc_atan2 :: proc(y: double, x: double) -> double ---
|
||||
@(link_name="atan2f") libc_atan2f :: proc(y: float, x: float) -> float ---
|
||||
@(link_name="cos") libc_cos :: proc(x: double) -> double ---
|
||||
@(link_name="cosf") libc_cosf :: proc(x: float) -> float ---
|
||||
@(link_name="sin") libc_sin :: proc(x: double) -> double ---
|
||||
@(link_name="sinf") libc_sinf :: proc(x: float) -> float ---
|
||||
@(link_name="tan") libc_tan :: proc(x: double) -> double ---
|
||||
@(link_name="tanf") libc_tanf :: proc(x: float) -> float ---
|
||||
|
||||
// 7.12.5 Hyperbolic functions
|
||||
@(link_name="acosh") libc_acosh :: proc(x: double) -> double ---
|
||||
@(link_name="acoshf") libc_acoshf :: proc(x: float) -> float ---
|
||||
@(link_name="asinh") libc_asinh :: proc(x: double) -> double ---
|
||||
@(link_name="asinhf") libc_asinhf :: proc(x: float) -> float ---
|
||||
@(link_name="atanh") libc_atanh :: proc(x: double) -> double ---
|
||||
@(link_name="atanhf") libc_atanhf :: proc(x: float) -> float ---
|
||||
@(link_name="cosh") libc_cosh :: proc(x: double) -> double ---
|
||||
@(link_name="coshf") libc_coshf :: proc(x: float) -> float ---
|
||||
@(link_name="sinh") libc_sinh :: proc(x: double) -> double ---
|
||||
@(link_name="sinhf") libc_sinhf :: proc(x: float) -> float ---
|
||||
@(link_name="tanh") libc_tanh :: proc(x: double) -> double ---
|
||||
@(link_name="tanhf") libc_tanhf :: proc(x: float) -> float ---
|
||||
|
||||
// 7.12.6 Exponential and logarithmic functions
|
||||
@(link_name="exp") libc_exp :: proc(x: double) -> double ---
|
||||
@(link_name="expf") libc_expf :: proc(x: float) -> float ---
|
||||
@(link_name="exp2") libc_exp2 :: proc(x: double) -> double ---
|
||||
@(link_name="exp2f") libc_exp2f :: proc(x: float) -> float ---
|
||||
@(link_name="expm1") libc_expm1 :: proc(x: double) -> double ---
|
||||
@(link_name="expm1f") libc_expm1f :: proc(x: float) -> float ---
|
||||
@(link_name="frexp") libc_frexp :: proc(value: double, exp: ^int) -> double ---
|
||||
@(link_name="frexpf") libc_frexpf :: proc(value: float, exp: ^int) -> float ---
|
||||
@(link_name="ilogb") libc_ilogb :: proc(x: double) -> int ---
|
||||
@(link_name="ilogbf") libc_ilogbf :: proc(x: float) -> int ---
|
||||
@(link_name="ldexp") libc_ldexp :: proc(x: double, exp: int) -> double ---
|
||||
@(link_name="ldexpf") libc_ldexpf :: proc(x: float, exp: int) -> float ---
|
||||
@(link_name="log") libc_log :: proc(x: double) -> double ---
|
||||
@(link_name="logf") libc_logf :: proc(x: float) -> float ---
|
||||
@(link_name="log10") libc_log10 :: proc(x: double) -> double ---
|
||||
@(link_name="log10f") libc_log10f :: proc(x: float) -> float ---
|
||||
@(link_name="log1p") libc_log1p :: proc(x: double) -> double ---
|
||||
@(link_name="log1pf") libc_log1pf :: proc(x: float) -> float ---
|
||||
@(link_name="log2") libc_log2 :: proc(x: double) -> double ---
|
||||
@(link_name="log2f") libc_log2f :: proc(x: float) -> float ---
|
||||
@(link_name="logb") libc_logb :: proc(x: double) -> double ---
|
||||
@(link_name="logbf") libc_logbf :: proc(x: float) -> float ---
|
||||
@(link_name="modf") libc_modf :: proc(value: double, iptr: ^double) -> double ---
|
||||
@(link_name="modff") libc_modff :: proc(value: float, iptr: ^float) -> float ---
|
||||
@(link_name="scalbn") libc_scalbn :: proc(x: double, n: int) -> double ---
|
||||
@(link_name="scalbnf") libc_scalbnf :: proc(x: float, n: int) -> float ---
|
||||
@(link_name="scalbln") libc_scalbln :: proc(x: double, n: long) -> double ---
|
||||
@(link_name="scalblnf") libc_scalblnf :: proc(x: float, n: long) -> float ---
|
||||
|
||||
// 7.12.7 Power and absolute-value functions
|
||||
@(link_name="cbrt") libc_cbrt :: proc(x: double) -> double ---
|
||||
@(link_name="cbrtf") libc_cbrtf :: proc(x: float) -> float ---
|
||||
@(link_name="fabs") libc_fabs :: proc(x: double) -> double ---
|
||||
@(link_name="fabsf") libc_fabsf :: proc(x: float) -> float ---
|
||||
@(link_name="hypot") libc_hypot :: proc(x: double, y: double) -> double ---
|
||||
@(link_name="hypotf") libc_hypotf :: proc(x: float, y: float) -> float ---
|
||||
@(link_name="pow") libc_pow :: proc(x: double, y: double) -> double ---
|
||||
@(link_name="powf") libc_powf :: proc(x: float, y: float) -> float ---
|
||||
@(link_name="sqrt") libc_sqrt :: proc(x: double) -> double ---
|
||||
@(link_name="sqrtf") libc_sqrtf :: proc(x: float) -> float ---
|
||||
|
||||
// 7.12.8 Error and gamma functions
|
||||
@(link_name="erf") libc_erf :: proc(x: double) -> double ---
|
||||
@(link_name="erff") libc_erff :: proc(x: float) -> float ---
|
||||
@(link_name="erfc") libc_erfc :: proc(x: double) -> double ---
|
||||
@(link_name="erfcf") libc_erfcf :: proc(x: float) -> float ---
|
||||
@(link_name="lgamma") libc_lgamma :: proc(x: double) -> double ---
|
||||
@(link_name="lgammaf") libc_lgammaf :: proc(x: float) -> float ---
|
||||
@(link_name="tgamma") libc_tgamma :: proc(x: double) -> double ---
|
||||
@(link_name="tgammaf") libc_tgammaf :: proc(x: float) -> float ---
|
||||
|
||||
// 7.12.9 Nearest integer functions
|
||||
@(link_name="ceil") libc_ceil :: proc(x: double) -> double ---
|
||||
@(link_name="ceilf") libc_ceilf :: proc(x: float) -> float ---
|
||||
@(link_name="floor") libc_floor :: proc(x: double) -> double ---
|
||||
@(link_name="floorf") libc_floorf :: proc(x: float) -> float ---
|
||||
@(link_name="nearbyint") libc_nearbyint :: proc(x: double) -> double ---
|
||||
@(link_name="nearbyintf") libc_nearbyintf :: proc(x: float) -> float ---
|
||||
@(link_name="rint") libc_rint :: proc(x: double) -> double ---
|
||||
@(link_name="rintf") libc_rintf :: proc(x: float) -> float ---
|
||||
@(link_name="lrint") libc_lrint :: proc(x: double) -> long ---
|
||||
@(link_name="lrintf") libc_lrintf :: proc(x: float) -> long ---
|
||||
@(link_name="llrint") libc_llrint :: proc(x: double) -> longlong ---
|
||||
@(link_name="llrintf") libc_llrintf :: proc(x: float) -> longlong ---
|
||||
@(link_name="round") libc_round :: proc(x: double) -> double ---
|
||||
@(link_name="roundf") libc_roundf :: proc(x: float) -> float ---
|
||||
@(link_name="lround") libc_lround :: proc(x: double) -> long ---
|
||||
@(link_name="lroundf") libc_lroundf :: proc(x: float) -> long ---
|
||||
@(link_name="llround") libc_llround :: proc(x: double) -> longlong ---
|
||||
@(link_name="llroundf") libc_llroundf :: proc(x: float) -> longlong ---
|
||||
@(link_name="trunc") libc_trunc :: proc(x: double) -> double ---
|
||||
@(link_name="truncf") libc_truncf :: proc(x: float) -> float ---
|
||||
|
||||
// 7.12.10 Remainder functions
|
||||
@(link_name="fmod") libc_fmod :: proc(x: double, y: double) -> double ---
|
||||
@(link_name="fmodf") libc_fmodf :: proc(x: float, y: float) -> float ---
|
||||
@(link_name="remainder") libc_remainder :: proc(x: double, y: double) -> double ---
|
||||
@(link_name="remainderf") libc_remainderf :: proc(x: float, y: float) -> float ---
|
||||
@(link_name="remquo") libc_remquo :: proc(x: double, y: double, quo: ^int) -> double ---
|
||||
@(link_name="remquof") libc_remquof :: proc(x: float, y: float, quo: ^int) -> float ---
|
||||
|
||||
// 7.12.11 Manipulation functions
|
||||
@(link_name="copysign") libc_copysign :: proc(x: double, y: double) -> double ---
|
||||
@(link_name="copysignf") libc_copysignf :: proc(x: float, y: float) -> float ---
|
||||
@(link_name="nan") libc_nan :: proc(tagp: cstring) -> double ---
|
||||
@(link_name="nanf") libc_nanf :: proc(tagp: cstring) -> float ---
|
||||
@(link_name="nextafter") libc_nextafter :: proc(x: double, y: double) -> double ---
|
||||
@(link_name="nextafterf") libc_nextafterf :: proc(x: float, y: float) -> float ---
|
||||
|
||||
// 7.12.12 Maximum, minimum, and positive difference functions
|
||||
@(link_name="fdim") libc_fdim :: proc(x: double, y: double) -> double ---
|
||||
@(link_name="fdimf") libc_fdimf :: proc(x: float, y: float) -> float ---
|
||||
@(link_name="fmax") libc_fmax :: proc(x: double, y: double) -> double ---
|
||||
@(link_name="fmaxf") libc_fmaxf :: proc(x: float, y: float) -> float ---
|
||||
@(link_name="fmin") libc_fmin :: proc(x: double, y: double) -> double ---
|
||||
@(link_name="fminf") libc_fminf :: proc(x: float, y: float) -> float ---
|
||||
@(link_name="fma") libc_fma :: proc(x, y, z: double) -> double ---
|
||||
@(link_name="fmaf") libc_fmaf :: proc(x, y, z: float) -> float ---
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
_nan_bit_pattern := ~u64(0)
|
||||
|
||||
// On amd64 Windows and Linux, float_t and double_t are respectively both
|
||||
// their usual types. On x86 it's not possible to define these types correctly
|
||||
// since they would be long double which Odin does have support for.
|
||||
float_t :: float
|
||||
double_t :: double
|
||||
|
||||
NAN := transmute(double)(_nan_bit_pattern)
|
||||
INFINITY :: 1e5000
|
||||
|
||||
HUGE_VALF :: INFINITY
|
||||
HUGE_VAL :: double(INFINITY)
|
||||
|
||||
MATH_ERRNO :: 1
|
||||
MATH_ERREXCEPT :: 2
|
||||
|
||||
math_errhandling :: 2 // Windows, Linux, macOS all use this mode.
|
||||
|
||||
FP_ILOGBNAN :: -1 - int((~uint(0)) >> 1)
|
||||
FP_ILOGB0 :: FP_ILOGBNAN
|
||||
|
||||
// Number classification constants. These do not have to match libc since we
|
||||
// implement our own classification functions as libc requires they be macros,
|
||||
// which means libc does not export standard functions for them.
|
||||
FP_NAN :: 0
|
||||
FP_INFINITE :: 1
|
||||
FP_ZERO :: 2
|
||||
FP_NORMAL :: 3
|
||||
FP_SUBNORMAL :: 4
|
||||
|
||||
@(private)
|
||||
_fpclassify :: #force_inline proc(x: double) -> int {
|
||||
u := transmute(uint64_t)x
|
||||
switch e := u >> 52 & 0x7ff; e {
|
||||
case 0: return FP_SUBNORMAL if (u << 1) != 0 else FP_ZERO
|
||||
case 0x7ff: return FP_NAN if (u << 12) != 0 else FP_INFINITE
|
||||
}
|
||||
return FP_NORMAL
|
||||
}
|
||||
|
||||
@(private)
|
||||
_fpclassifyf :: #force_inline proc(x: float) -> int {
|
||||
u := transmute(uint32_t)x
|
||||
switch e := u >> 23 & 0xff; e {
|
||||
case 0: return FP_SUBNORMAL if (u << 1) != 0 else FP_ZERO
|
||||
case 0xff: return FP_NAN if (u << 9) != 0 else FP_INFINITE
|
||||
}
|
||||
return FP_NORMAL
|
||||
}
|
||||
|
||||
@(private)
|
||||
_signbit :: #force_inline proc(x: double) -> int {
|
||||
return int(transmute(uint64_t)x >> 63)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_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) {
|
||||
return fpclassify(x) == FP_INFINITE
|
||||
}
|
||||
|
||||
isinf :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) > FP_INFINITE
|
||||
}
|
||||
|
||||
isnan :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) == FP_NAN
|
||||
}
|
||||
|
||||
isnormal :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) == FP_NORMAL
|
||||
}
|
||||
|
||||
// These are special in that they avoid float exceptions. They cannot just be
|
||||
// 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) {
|
||||
return !isunordered(x, y) && x > y
|
||||
}
|
||||
|
||||
isgreaterequal :: #force_inline proc(x, y: $T) 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) {
|
||||
return !isunordered(x, y) && x < y
|
||||
}
|
||||
|
||||
islessequal :: #force_inline proc(x, y: $T) 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) {
|
||||
return !isunordered(x, y) && x <= y
|
||||
}
|
||||
|
||||
isunordered :: #force_inline proc(x, y: $T) 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.
|
||||
sink: T
|
||||
intrinsics.volatile_store(&sink, intrinsics.volatile_load(&y))
|
||||
return true
|
||||
}
|
||||
return isnan(y)
|
||||
}
|
||||
|
||||
fpclassify :: proc{_fpclassify, _fpclassifyf}
|
||||
signbit :: proc{_signbit, _signbitf}
|
||||
|
||||
// Emulate tgmath.h behavior with explicit procedure overloading here.
|
||||
acos :: proc{libc_acos, libc_acosf, cacos, cacosf}
|
||||
asin :: proc{libc_asin, libc_asinf, casin, casinf}
|
||||
atan :: proc{libc_atan, libc_atanf, catan, catanf}
|
||||
atan2 :: proc{libc_atan2, libc_atan2f}
|
||||
cos :: proc{libc_cos, libc_cosf, ccos, ccosf}
|
||||
sin :: proc{libc_sin, libc_sinf, csin, csinf}
|
||||
tan :: proc{libc_tan, libc_tanf, ctan, ctanf}
|
||||
|
||||
acosh :: proc{libc_acosh, libc_acoshf, cacosh, cacoshf}
|
||||
asinh :: proc{libc_asinh, libc_asinhf, casinh, casinhf}
|
||||
atanh :: proc{libc_atanh, libc_atanhf, catanh, catanhf}
|
||||
cosh :: proc{libc_cosh, libc_coshf, ccosh, ccoshf}
|
||||
sinh :: proc{libc_sinh, libc_sinhf, csinh, csinhf}
|
||||
tanh :: proc{libc_tanh, libc_tanhf, ctanh, ctanhf}
|
||||
|
||||
exp :: proc{libc_exp, libc_expf, cexp, cexpf}
|
||||
exp2 :: proc{libc_exp2, libc_exp2f}
|
||||
expm1 :: proc{libc_expm1, libc_expm1f}
|
||||
frexp :: proc{libc_frexp, libc_frexpf}
|
||||
ilogb :: proc{libc_ilogb, libc_ilogbf}
|
||||
ldexp :: proc{libc_ldexp, libc_ldexpf}
|
||||
log :: proc{libc_log, libc_logf, clog, clogf}
|
||||
log10 :: proc{libc_log10, libc_log10f}
|
||||
log1p :: proc{libc_log1p, libc_log1pf}
|
||||
log2 :: proc{libc_log2, libc_log2f}
|
||||
logb :: proc{libc_logb, libc_logbf}
|
||||
modf :: proc{libc_modf, libc_modff}
|
||||
scalbn :: proc{libc_scalbn, libc_scalbnf}
|
||||
scalbln :: proc{libc_scalbln, libc_scalblnf}
|
||||
|
||||
cbrt :: proc{libc_cbrt, libc_cbrtf}
|
||||
fabs :: proc{libc_fabs, libc_fabsf, cabs, cabsf}
|
||||
hypot :: proc{libc_hypot, libc_hypotf}
|
||||
pow :: proc{libc_pow, libc_powf, cpow, cpowf}
|
||||
sqrt :: proc{libc_sqrt, libc_sqrtf, csqrt, csqrtf}
|
||||
|
||||
erf :: proc{libc_erf, libc_erff}
|
||||
erfc :: proc{libc_erfc, libc_erfcf}
|
||||
lgamma :: proc{libc_lgamma, libc_lgammaf}
|
||||
tgamma :: proc{libc_tgamma, libc_tgammaf}
|
||||
|
||||
ceil :: proc{libc_ceil, libc_ceilf}
|
||||
floor :: proc{libc_floor, libc_floorf}
|
||||
nearbyint :: proc{libc_nearbyint, libc_nearbyintf}
|
||||
rint :: proc{libc_rint, libc_rintf}
|
||||
lrint :: proc{libc_lrint, libc_lrintf}
|
||||
llrint :: proc{libc_llrint, libc_llrintf}
|
||||
round :: proc{libc_round, libc_roundf}
|
||||
lround :: proc{libc_lround, libc_lroundf}
|
||||
llround :: proc{libc_llround, libc_llroundf}
|
||||
trunc :: proc{libc_trunc, libc_truncf}
|
||||
|
||||
fmod :: proc{libc_fmod, libc_fmodf}
|
||||
remainder :: proc{libc_remainder, libc_remainderf}
|
||||
remquo :: proc{libc_remquo, libc_remquof}
|
||||
|
||||
copysign :: proc{libc_copysign, libc_copysignf}
|
||||
nextafter :: proc{libc_nextafter, libc_nextafterf}
|
||||
|
||||
fdim :: proc{libc_fdim, libc_fdimf}
|
||||
fmax :: proc{libc_fmax, libc_fmaxf}
|
||||
fmin :: proc{libc_fmin, libc_fminf}
|
||||
fma :: proc{libc_fma, libc_fmaf}
|
||||
|
||||
// But retain the 'f' suffix-variant functions as well so they can be used,
|
||||
// a trick is used here where we use explicit procedrual overloading of one
|
||||
// procedure. This is done because the foreign block is marked @(private) and
|
||||
// aliasing functions does not remove privateness from the entity.
|
||||
acosf :: proc{libc_acosf}
|
||||
asinf :: proc{libc_asinf}
|
||||
atanf :: proc{libc_atanf}
|
||||
atan2f :: proc{libc_atan2f}
|
||||
cosf :: proc{libc_cosf}
|
||||
sinf :: proc{libc_sinf}
|
||||
tanf :: proc{libc_tanf}
|
||||
|
||||
acoshf :: proc{libc_acoshf}
|
||||
asinhf :: proc{libc_asinhf}
|
||||
atanhf :: proc{libc_atanhf}
|
||||
coshf :: proc{libc_coshf}
|
||||
sinhf :: proc{libc_sinhf}
|
||||
tanhf :: proc{libc_tanhf}
|
||||
|
||||
expf :: proc{libc_expf}
|
||||
exp2f :: proc{libc_exp2f}
|
||||
expm1f :: proc{libc_expm1f}
|
||||
frexpf :: proc{libc_frexpf}
|
||||
ilogbf :: proc{libc_ilogbf}
|
||||
ldexpf :: proc{libc_ldexpf}
|
||||
logf :: proc{libc_logf}
|
||||
log10f :: proc{libc_log10f}
|
||||
log1pf :: proc{libc_log1pf}
|
||||
log2f :: proc{libc_log2f}
|
||||
logbf :: proc{libc_logbf}
|
||||
modff :: proc{libc_modff}
|
||||
scalbnf :: proc{libc_scalbnf}
|
||||
scalblnf :: proc{libc_scalblnf}
|
||||
|
||||
cbrtf :: proc{libc_cbrtf}
|
||||
fabsf :: proc{libc_fabsf}
|
||||
hypotf :: proc{libc_hypotf}
|
||||
powf :: proc{libc_powf}
|
||||
sqrtf :: proc{libc_sqrtf}
|
||||
|
||||
erff :: proc{libc_erff}
|
||||
erfcf :: proc{libc_erfcf}
|
||||
lgammaf :: proc{libc_lgammaf}
|
||||
tgammaf :: proc{libc_tgammaf}
|
||||
|
||||
ceilf :: proc{libc_ceilf}
|
||||
floorf :: proc{libc_floorf}
|
||||
nearbyintf :: proc{libc_nearbyintf}
|
||||
rintf :: proc{libc_rintf}
|
||||
lrintf :: proc{libc_lrintf}
|
||||
llrintf :: proc{libc_llrintf}
|
||||
roundf :: proc{libc_roundf}
|
||||
lroundf :: proc{libc_lroundf}
|
||||
llroundf :: proc{libc_llroundf}
|
||||
truncf :: proc{libc_truncf}
|
||||
|
||||
fmodf :: proc{libc_fmodf}
|
||||
remainderf :: proc{libc_remainderf}
|
||||
remquof :: proc{libc_remquof}
|
||||
|
||||
copysignf :: proc{libc_copysignf}
|
||||
nextafterf :: proc{libc_nextafterf}
|
||||
|
||||
fdimf :: proc{libc_fdimf}
|
||||
fmaxf :: proc{libc_fmaxf}
|
||||
fminf :: proc{libc_fminf}
|
||||
fmaf :: proc{libc_fmaf}
|
||||
|
||||
// These two functions are special and not made type generic in tgmath.h since
|
||||
// they only differ by their return type.
|
||||
nan :: proc{libc_nan}
|
||||
nanf :: proc{libc_nanf}
|
||||
@@ -0,0 +1,66 @@
|
||||
package libc
|
||||
|
||||
// 7.13 Nonlocal jumps
|
||||
|
||||
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 {
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.13.1 Save calling environment
|
||||
//
|
||||
// NOTE(dweiler): C11 requires setjmp be a macro, which means it won't
|
||||
// necessarily export a symbol named setjmp but rather _setjmp in the case
|
||||
// of musl, glibc, BSD libc, and msvcrt.
|
||||
//
|
||||
/// NOTE(dweiler): UCRT has two implementations of longjmp. One that performs
|
||||
// stack unwinding and one that doesn't. The choice of which to use depends on a
|
||||
// flag which is set inside the jmp_buf structure given to setjmp. The default
|
||||
// behavior is to unwind the stack. Within Odin, we cannot use the stack
|
||||
// unwinding version as the unwinding information isn't present. To opt-in to
|
||||
// the regular non-unwinding version we need a way to set this flag. Since the
|
||||
// location of the flag within the struct is not defined or part of the ABI and
|
||||
// can change between versions of UCRT, we must rely on setjmp to set it. It
|
||||
// turns out that setjmp receives this flag in the RDX register on Win64, this
|
||||
// just so happens to coincide with the second argument of a function in the
|
||||
// Win64 ABI. By giving our setjmp a second argument with the value of zero,
|
||||
// the RDX register will contain zero and correctly set the flag to disable
|
||||
// stack unwinding.
|
||||
@(link_name="_setjmp")
|
||||
setjmp :: proc(env: ^jmp_buf, hack: rawptr = nil) -> int ---
|
||||
}
|
||||
} else {
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.13.1 Save calling environment
|
||||
//
|
||||
// NOTE(dweiler): C11 requires setjmp be a macro, which means it won't
|
||||
// necessarily export a symbol named setjmp but rather _setjmp in the case
|
||||
// of musl, glibc, BSD libc, and msvcrt.
|
||||
@(link_name="_setjmp")
|
||||
setjmp :: proc(env: ^jmp_buf) -> int ---
|
||||
}
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.13.2 Restore calling environment
|
||||
longjmp :: proc(env: ^jmp_buf, val: int) -> ! ---
|
||||
}
|
||||
|
||||
// The C99 Rationale describes jmp_buf as being an array type for backward
|
||||
// compatibility. Odin does not need to honor this and couldn't as arrays in
|
||||
// Odin don't decay to pointers. It is somewhat easy for us to bind this, we
|
||||
// just need to ensure the structure contains enough storage with appropriate
|
||||
// alignment. Since there are no types in C with an alignment larger than
|
||||
// that of max_align_t, which cannot be larger than sizeof(long double) as any
|
||||
// other exposed type wouldn't be valid C, the maximum alignment possible in a
|
||||
// strictly conformant C implementation is 16 on the platforms we care about.
|
||||
// The choice of 4096 bytes for storage of this type is more than enough on all
|
||||
// relevant platforms.
|
||||
jmp_buf :: struct #align 16 { _: [4096]char, }
|
||||
@@ -0,0 +1,61 @@
|
||||
package libc
|
||||
|
||||
// 7.14 Signal handling
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
sig_atomic_t :: distinct atomic_int
|
||||
|
||||
SIG_ATOMIC_MIN :: min(sig_atomic_t)
|
||||
SIG_ATOMIC_MAX :: max(sig_atomic_t)
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
signal :: proc(sig: int, func: proc "c" (int)) -> proc "c" (int) ---
|
||||
raise :: proc(sig: int) -> int ---
|
||||
}
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
SIG_ERR :: rawptr(~uintptr(0))
|
||||
SIG_DFL :: rawptr(uintptr(0))
|
||||
SIG_IGN :: rawptr(uintptr(1))
|
||||
|
||||
SIGABRT :: 22
|
||||
SIGFPE :: 8
|
||||
SIGILL :: 4
|
||||
SIGINT :: 2
|
||||
SIGSEGV :: 11
|
||||
SIGTERM :: 15
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
SIGABRT :: 6
|
||||
SIGFPE :: 8
|
||||
SIGILL :: 4
|
||||
SIGINT :: 2
|
||||
SIGSEGV :: 11
|
||||
SIGTERM :: 15
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package libc
|
||||
|
||||
// 7.16 Variable arguments
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="none")
|
||||
foreign _ {
|
||||
@(link_name="llvm.va_start") _va_start :: proc(arglist: ^i8) ---
|
||||
@(link_name="llvm.va_end") _va_end :: proc(arglist: ^i8) ---
|
||||
@(link_name="llvm.va_copy") _va_copy :: proc(dst, src: ^i8) ---
|
||||
}
|
||||
|
||||
// Since there are no types in C with an alignment larger than that of
|
||||
// max_align_t, which cannot be larger than sizeof(long double) as any other
|
||||
// exposed type wouldn't be valid C, the maximum alignment possible in a
|
||||
// strictly conformant C implementation is 16 on the platforms we care about.
|
||||
// The choice of 4096 bytes for storage of this type is more than enough on all
|
||||
// relevant platforms.
|
||||
va_list :: struct #align 16 {
|
||||
_: [4096]u8,
|
||||
}
|
||||
|
||||
va_start :: #force_inline proc(ap: ^va_list, _: any) {
|
||||
_va_start(cast(^i8)ap)
|
||||
}
|
||||
|
||||
va_end :: #force_inline proc(ap: ^va_list) {
|
||||
_va_end(cast(^i8)ap)
|
||||
}
|
||||
|
||||
va_copy :: #force_inline proc(dst, src: ^va_list) {
|
||||
_va_copy(cast(^i8)dst, cast(^i8)src)
|
||||
}
|
||||
|
||||
// We cannot provide va_arg as there is no way to create "C" style procedures
|
||||
// in Odin which take variable arguments the C way. The #c_vararg attribute only
|
||||
// exists for foreign imports. That being said, being able to copy a va_list,
|
||||
// as well as start and end one is necessary in some functions, the va_list
|
||||
// taking functions in libc as an example.
|
||||
@@ -0,0 +1,416 @@
|
||||
package libc
|
||||
|
||||
// 7.17 Atomics
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
ATOMIC_BOOL_LOCK_FREE :: true
|
||||
ATOMIC_CHAR_LOCK_FREE :: true
|
||||
ATOMIC_CHAR16_T_LOCK_FREE :: true
|
||||
ATOMIC_CHAR32_T_LOCK_FREE :: true
|
||||
ATOMIC_WCHAR_T_LOCK_FREE :: true
|
||||
ATOMIC_SHORT_LOCK_FREE :: true
|
||||
ATOMIC_INT_LOCK_FREE :: true
|
||||
ATOMIC_LONG_LOCK_FREE :: true
|
||||
ATOMIC_LLONG_LOCK_FREE :: true
|
||||
ATOMIC_POINTER_LOCK_FREE :: true
|
||||
|
||||
// 7.17.3 Order and consistency
|
||||
memory_order :: enum int {
|
||||
relaxed,
|
||||
consume,
|
||||
acquire,
|
||||
release,
|
||||
acq_rel,
|
||||
seq_cst,
|
||||
}
|
||||
|
||||
memory_order_relaxed :: memory_order.relaxed
|
||||
memory_order_consume :: memory_order.consume
|
||||
memory_order_acquire :: memory_order.acquire
|
||||
memory_order_release :: memory_order.release
|
||||
memory_order_acq_rel :: memory_order.acq_rel
|
||||
memory_order_seq_cst :: memory_order.seq_cst
|
||||
|
||||
// 7.17.2 Initialization
|
||||
ATOMIC_VAR_INIT :: #force_inline proc(value: $T) -> T {
|
||||
return value
|
||||
}
|
||||
|
||||
atomic_init :: #force_inline proc(obj: ^$T, value: T) {
|
||||
intrinsics.atomic_store(obj, value)
|
||||
}
|
||||
|
||||
kill_dependency :: #force_inline proc(value: $T) -> T {
|
||||
return value
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
atomic_signal_fence :: #force_inline proc(order: memory_order) {
|
||||
atomic_thread_fence(order)
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
// 7.17.6 Atomic integer types
|
||||
atomic_bool :: distinct bool
|
||||
atomic_char :: distinct char
|
||||
atomic_schar :: distinct char
|
||||
atomic_uchar :: distinct uchar
|
||||
atomic_short :: distinct short
|
||||
atomic_ushort :: distinct ushort
|
||||
atomic_int :: distinct int
|
||||
atomic_uint :: distinct uint
|
||||
atomic_long :: distinct long
|
||||
atomic_ulong :: distinct ulong
|
||||
atomic_llong :: distinct longlong
|
||||
atomic_ullong :: distinct ulonglong
|
||||
atomic_char16_t :: distinct char16_t
|
||||
atomic_char32_t :: distinct char32_t
|
||||
atomic_wchar_t :: distinct wchar_t
|
||||
atomic_int_least8_t :: distinct int_least8_t
|
||||
atomic_uint_least8_t :: distinct uint_least8_t
|
||||
atomic_int_least16_t :: distinct int_least16_t
|
||||
atomic_uint_least16_t :: distinct uint_least16_t
|
||||
atomic_int_least32_t :: distinct int_least32_t
|
||||
atomic_uint_least32_t :: distinct uint_least32_t
|
||||
atomic_int_least64_t :: distinct int_least64_t
|
||||
atomic_uint_least64_t :: distinct uint_least64_t
|
||||
atomic_int_fast8_t :: distinct int_fast8_t
|
||||
atomic_uint_fast8_t :: distinct uint_fast8_t
|
||||
atomic_int_fast16_t :: distinct int_fast16_t
|
||||
atomic_uint_fast16_t :: distinct uint_fast16_t
|
||||
atomic_int_fast32_t :: distinct int_fast32_t
|
||||
atomic_uint_fast32_t :: distinct uint_fast32_t
|
||||
atomic_int_fast64_t :: distinct int_fast64_t
|
||||
atomic_uint_fast64_t :: distinct uint_fast64_t
|
||||
atomic_intptr_t :: distinct intptr_t
|
||||
atomic_uintptr_t :: distinct uintptr_t
|
||||
atomic_size_t :: distinct size_t
|
||||
atomic_ptrdiff_t :: distinct ptrdiff_t
|
||||
atomic_intmax_t :: distinct intmax_t
|
||||
atomic_uintmax_t :: distinct uintmax_t
|
||||
|
||||
// 7.17.7 Operations on atomic types
|
||||
atomic_store :: #force_inline proc(object: ^$T, desired: T) {
|
||||
intrinsics.atomic_store(object, desired)
|
||||
}
|
||||
|
||||
atomic_store_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) {
|
||||
assert(order != .consume)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
atomic_load :: #force_inline proc(object: ^$T) -> T {
|
||||
return intrinsics.atomic_load(object)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
atomic_exchange :: #force_inline proc(object: ^$T, desired: T) -> T {
|
||||
return intrinsics.atomic_xchg(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)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// C does not allow failure memory order to be order_release or acq_rel.
|
||||
// Similarly, it does not allow the failure order to be stronger than success
|
||||
// order. Since consume and acquire are both monotonic, we can count them as
|
||||
// one, for a total of three memory orders that are relevant in compare exchange.
|
||||
// relaxed, acquire (consume), seq_cst.
|
||||
// The requirement that the failure order cannot be stronger than success limits
|
||||
// the valid combinations for the failure order to this table:
|
||||
// [success = seq_cst, failure = seq_cst] => _
|
||||
// [success = acquire, failure = seq_cst] => acq
|
||||
// [success = release, failure = seq_cst] => rel
|
||||
// [success = acq_rel, failure = seq_cst] => acqrel
|
||||
// [success = relaxed, failure = relaxed] => relaxed
|
||||
// [success = seq_cst, failure = relaxed] => failrelaxed
|
||||
// [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)
|
||||
if !ok { expected^ = value }
|
||||
return ok
|
||||
}
|
||||
|
||||
atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) {
|
||||
assert(failure != .release)
|
||||
assert(failure != .acq_rel)
|
||||
|
||||
value: T; ok: bool
|
||||
#partial switch (failure) {
|
||||
case .seq_cst:
|
||||
assert(success != .relaxed)
|
||||
#partial switch (success) {
|
||||
case .seq_cst:
|
||||
value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
|
||||
case .acquire:
|
||||
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
|
||||
case .consume:
|
||||
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
|
||||
case .release:
|
||||
value, ok := intrinsics.atomic_cxchg_rel(object, expected^, desired)
|
||||
case .acq_rel:
|
||||
value, ok := intrinsics.atomic_cxchg_acqrel(object, expected^, desired)
|
||||
}
|
||||
case .relaxed:
|
||||
assert(success != .release)
|
||||
#partial switch (success) {
|
||||
case .relaxed:
|
||||
value, ok := intrinsics.atomic_cxchg_relaxed(object, expected^, desired)
|
||||
case .seq_cst:
|
||||
value, ok := intrinsics.atomic_cxchg_failrelaxed(object, expected^, desired)
|
||||
case .acquire:
|
||||
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
|
||||
case .consume:
|
||||
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
|
||||
case .acq_rel:
|
||||
value, ok := intrinsics.atomic_cxchg_acqrel_failrelaxed(object, expected^, desired)
|
||||
}
|
||||
case .consume:
|
||||
fallthrough
|
||||
case .acquire:
|
||||
assert(success == .seq_cst)
|
||||
value, ok := intrinsics.atomic_cxchg_failacq(object, expected^, desired)
|
||||
|
||||
}
|
||||
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)
|
||||
if !ok { expected^ = value }
|
||||
return ok
|
||||
}
|
||||
|
||||
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) {
|
||||
assert(failure != .release)
|
||||
assert(failure != .acq_rel)
|
||||
|
||||
value: T; ok: bool
|
||||
#partial switch (failure) {
|
||||
case .seq_cst:
|
||||
assert(success != .relaxed)
|
||||
#partial switch (success) {
|
||||
case .seq_cst:
|
||||
value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
|
||||
case .acquire:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
|
||||
case .consume:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
|
||||
case .release:
|
||||
value, ok := intrinsics.atomic_cxchgweak_rel(object, expected^, desired)
|
||||
case .acq_rel:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acqrel(object, expected^, desired)
|
||||
}
|
||||
case .relaxed:
|
||||
assert(success != .release)
|
||||
#partial switch (success) {
|
||||
case .relaxed:
|
||||
value, ok := intrinsics.atomic_cxchgweak_relaxed(object, expected^, desired)
|
||||
case .seq_cst:
|
||||
value, ok := intrinsics.atomic_cxchgweak_failrelaxed(object, expected^, desired)
|
||||
case .acquire:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
|
||||
case .consume:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
|
||||
case .acq_rel:
|
||||
value, ok := intrinsics.atomic_cxchgweak_acqrel_failrelaxed(object, expected^, desired)
|
||||
}
|
||||
case .consume:
|
||||
fallthrough
|
||||
case .acquire:
|
||||
assert(success == .seq_cst)
|
||||
value, ok := intrinsics.atomic_cxchgweak_failacq(object, expected^, desired)
|
||||
|
||||
}
|
||||
if !ok { expected^ = value }
|
||||
return ok
|
||||
}
|
||||
|
||||
// 7.17.7.5 The atomic_fetch and modify generic functions
|
||||
atomic_fetch_add :: #force_inline proc(object: ^$T, operand: T) -> T {
|
||||
return intrinsics.atomic_add(object, operand)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
atomic_fetch_sub :: #force_inline proc(object: ^$T, operand: T) -> T {
|
||||
return intrinsics.atomic_sub(object, operand)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
atomic_fetch_or :: #force_inline proc(object: ^$T, operand: T) -> T {
|
||||
return intrinsics.atomic_or(object, operand)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
atomic_fetch_xor :: #force_inline proc(object: ^$T, operand: T) -> T {
|
||||
return intrinsics.atomic_xor(object, operand)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 7.17.8 Atomic flag type and operations
|
||||
atomic_flag :: distinct atomic_bool
|
||||
|
||||
atomic_flag_test_and_set :: #force_inline proc(flag: ^atomic_flag) -> bool {
|
||||
return bool(atomic_exchange(flag, atomic_flag(true)))
|
||||
}
|
||||
|
||||
atomic_flag_test_and_set_explicit :: #force_inline proc(flag: ^atomic_flag, order: memory_order) -> bool {
|
||||
return bool(atomic_exchange_explicit(flag, atomic_flag(true), order))
|
||||
}
|
||||
|
||||
atomic_flag_clear :: #force_inline proc(flag: ^atomic_flag) {
|
||||
atomic_store(flag, atomic_flag(false))
|
||||
}
|
||||
|
||||
atomic_flag_clear_explicit :: #force_inline proc(flag: ^atomic_flag, order: memory_order) {
|
||||
atomic_store_explicit(flag, atomic_flag(false), order)
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package libc
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
// 7.21 Input/output
|
||||
|
||||
FILE :: struct {}
|
||||
|
||||
// MSVCRT compatible.
|
||||
when ODIN_OS == .Windows {
|
||||
_IOFBF :: 0x0000
|
||||
_IONBF :: 0x0004
|
||||
_IOLBF :: 0x0040
|
||||
|
||||
BUFSIZ :: 512
|
||||
|
||||
EOF :: int(-1)
|
||||
|
||||
FOPEN_MAX :: 20
|
||||
|
||||
FILENAME_MAX :: 260
|
||||
|
||||
L_tmpnam :: 15 // "\\" + 12 + NUL
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
TMP_MAX :: 32767 // SHRT_MAX
|
||||
|
||||
fpos_t :: distinct i64
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
__acrt_iob_func :: proc (index: uint) -> ^FILE ---
|
||||
}
|
||||
|
||||
stdin := __acrt_iob_func(0)
|
||||
stdout := __acrt_iob_func(1)
|
||||
stderr := __acrt_iob_func(2)
|
||||
}
|
||||
|
||||
// GLIBC and MUSL compatible.
|
||||
when ODIN_OS == .Linux {
|
||||
fpos_t :: struct #raw_union { _: [16]char, _: longlong, _: double, }
|
||||
|
||||
_IOFBF :: 0
|
||||
_IOLBF :: 1
|
||||
_IONBF :: 2
|
||||
|
||||
BUFSIZ :: 1024
|
||||
|
||||
EOF :: int(-1)
|
||||
|
||||
FOPEN_MAX :: 1000
|
||||
|
||||
FILENAME_MAX :: 4096
|
||||
|
||||
L_tmpnam :: 20
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
TMP_MAX :: 308915776
|
||||
|
||||
foreign libc {
|
||||
stderr: ^FILE
|
||||
stdin: ^FILE
|
||||
stdout: ^FILE
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .OpenBSD {
|
||||
fpos_t :: 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
|
||||
remove :: proc(filename: cstring) -> int ---
|
||||
rename :: proc(old, new: cstring) -> int ---
|
||||
tmpfile :: proc() -> ^FILE ---
|
||||
tmpnam :: proc(s: [^]char) -> [^]char ---
|
||||
|
||||
// 7.21.5 File access functions
|
||||
fclose :: proc(stream: ^FILE) -> int ---
|
||||
fflush :: proc(stream: ^FILE) -> int ---
|
||||
fopen :: proc(filename, mode: cstring) -> ^FILE ---
|
||||
freopen :: proc(filename, mode: cstring, stream: ^FILE) -> ^FILE ---
|
||||
setbuf :: proc(stream: ^FILE, buf: [^]char) ---
|
||||
setvbuf :: proc(stream: ^FILE, buf: [^]char, mode: int, size: size_t) -> int ---
|
||||
|
||||
// 7.21.6 Formatted input/output functions
|
||||
fprintf :: proc(stream: ^FILE, format: cstring, #c_vararg args: ..any) -> int ---
|
||||
fscanf :: proc(stream: ^FILE, format: cstring, #c_vararg args: ..any) -> int ---
|
||||
printf :: proc(format: cstring, #c_vararg args: ..any) -> int ---
|
||||
scanf :: proc(format: cstring, #c_vararg args: ..any) -> int ---
|
||||
snprintf :: proc(s: [^]char, format: cstring, #c_vararg args: ..any) -> int ---
|
||||
sscanf :: proc(s, format: cstring, #c_vararg args: ..any) -> int ---
|
||||
vfprintf :: proc(stream: ^FILE, format: cstring, arg: ^va_list) -> int ---
|
||||
vfscanf :: proc(stream: ^FILE, format: cstring, arg: ^va_list) -> int ---
|
||||
vprintf :: proc(format: cstring, arg: ^va_list) -> int ---
|
||||
vscanf :: proc(format: cstring, arg: ^va_list) -> int ---
|
||||
vsnprintf :: proc(s: [^]char, n: size_t, format: cstring, arg: ^va_list) -> int ---
|
||||
vsprintf :: proc(s: [^]char, format: cstring, arg: ^va_list) -> int ---
|
||||
vsscanf :: proc(s, format: cstring, arg: ^va_list) -> int ---
|
||||
|
||||
// 7.21.7 Character input/output functions
|
||||
fgetc :: proc(stream: ^FILE) -> int ---
|
||||
fgets :: proc(s: [^]char, n: int, stream: ^FILE) -> [^]char ---
|
||||
fputc :: proc(s: cstring, stream: ^FILE) -> int ---
|
||||
getc :: proc(stream: ^FILE) -> int ---
|
||||
getchar :: proc() -> int ---
|
||||
putc :: proc(c: int, stream: ^FILE) -> int ---
|
||||
putchar :: proc() -> int ---
|
||||
puts :: proc(s: cstring) -> int ---
|
||||
ungetc :: proc(c: int, stream: ^FILE) -> int ---
|
||||
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
|
||||
fgetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
|
||||
fseek :: proc(stream: ^FILE, offset: long, whence: int) -> int ---
|
||||
fsetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
|
||||
ftell :: proc(stream: ^FILE) -> long ---
|
||||
rewind :: proc(stream: ^FILE) ---
|
||||
|
||||
// 7.21.10 Error-handling functions
|
||||
clearerr :: proc(stream: ^FILE) ---
|
||||
feof :: proc(stream: ^FILE) -> int ---
|
||||
ferror :: proc(stream: ^FILE) -> int ---
|
||||
perror :: proc(s: cstring) ---
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package libc
|
||||
|
||||
// 7.22 General utilities
|
||||
|
||||
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 {
|
||||
RAND_MAX :: 0x7fff
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
___mb_cur_max_func :: proc() -> int ---
|
||||
}
|
||||
|
||||
MB_CUR_MAX :: #force_inline proc() -> size_t {
|
||||
return size_t(___mb_cur_max_func())
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Linux {
|
||||
RAND_MAX :: 0x7fffffff
|
||||
|
||||
// GLIBC and MUSL only
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
__ctype_get_mb_cur_max :: proc() -> size_t ---
|
||||
}
|
||||
|
||||
MB_CUR_MAX :: #force_inline proc() -> size_t {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
// C does not declare what these values should be, as an implementation is free
|
||||
// to use any two distinct values it wants to indicate success or failure.
|
||||
// However, nobody actually does and everyone appears to have agreed upon these
|
||||
// values.
|
||||
EXIT_SUCCESS :: 0
|
||||
EXIT_FAILURE :: 1
|
||||
|
||||
// C does not declare which order 'quot' and 'rem' should be for the divide
|
||||
// structures. An implementation could put 'rem' first. However, nobody actually
|
||||
// does and everyone appears to have agreed upon this layout.
|
||||
div_t :: struct { quot, rem: int, }
|
||||
ldiv_t :: struct { quot, rem: long, }
|
||||
lldiv_t :: struct { quot, rem: longlong, }
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.22.1 Numeric conversion functions
|
||||
atof :: proc(nptr: cstring) -> double ---
|
||||
atoi :: proc(nptr: cstring) -> int ---
|
||||
atol :: proc(nptr: cstring) -> long ---
|
||||
atoll :: proc(nptr: cstring) -> longlong ---
|
||||
strtod :: proc(nptr: cstring, endptr: ^[^]char) -> double ---
|
||||
strtof :: proc(nptr: cstring, endptr: ^[^]char) -> float ---
|
||||
strtol :: proc(nptr: cstring, endptr: ^[^]char, base: int) -> long ---
|
||||
strtoll :: proc(nptr: cstring, endptr: ^[^]char, base: int) -> longlong ---
|
||||
strtoul :: proc(nptr: cstring, endptr: ^[^]char, base: int) -> ulong ---
|
||||
strtoull :: proc(nptr: cstring, endptr: ^[^]char, base: int) -> ulonglong ---
|
||||
|
||||
// 7.22.2 Pseudo-random sequence generation functions
|
||||
rand :: proc() -> int ---
|
||||
srand :: proc(seed: uint) ---
|
||||
|
||||
// 7.22.3 Memory management functions
|
||||
aligned_alloc :: proc(aligment, size: size_t) -> rawptr ---
|
||||
calloc :: proc(nmemb, size: size_t) -> rawptr ---
|
||||
free :: proc(ptr: rawptr) ---
|
||||
malloc :: proc(size: size_t) -> rawptr ---
|
||||
realloc :: proc(ptr: rawptr, size: size_t) -> rawptr ---
|
||||
|
||||
// 7.22.4 Communication with the environment
|
||||
abort :: proc() -> ! ---
|
||||
atexit :: proc(func: proc "c" ()) -> int ---
|
||||
at_quick_exit :: proc(func: proc "c" ()) -> int ---
|
||||
exit :: proc(status: int) -> ! ---
|
||||
_Exit :: proc(status: int) -> ! ---
|
||||
getenv :: proc(name: cstring) -> [^]char ---
|
||||
quick_exit :: proc(status: int) -> ! ---
|
||||
system :: proc(cmd: cstring) -> int ---
|
||||
|
||||
// 7.22.5 Searching and sorting utilities
|
||||
bsearch :: proc(key, base: rawptr, nmemb, size: size_t, compar: proc "c" (lhs, rhs: rawptr) -> int) -> rawptr ---
|
||||
qsort :: proc(base: rawptr, nmemb, size: size_t, compar: proc "c" (lhs, rhs: rawptr) -> int) ---
|
||||
|
||||
// 7.22.6 Integer arithmetic functions
|
||||
abs :: proc(j: int) -> int ---
|
||||
labs :: proc(j: long) -> long ---
|
||||
llabs :: proc(j: longlong) -> longlong ---
|
||||
div :: proc(numer, denom: int) -> div_t ---
|
||||
ldiv :: proc(numer, denom: long) -> ldiv_t ---
|
||||
lldiv :: proc(numer, denom: longlong) -> lldiv_t ---
|
||||
|
||||
// 7.22.7 Multibyte/wide character conversion functions
|
||||
mblen :: proc(s: cstring, n: size_t) -> int ---
|
||||
mbtowc :: proc(pwc: ^wchar_t, s: cstring, n: size_t) -> int ---
|
||||
wctomb :: proc(s: [^]char, wc: wchar_t) -> int ---
|
||||
|
||||
// 7.22.8 Multibyte/wide string conversion functions
|
||||
mbstowcs :: proc(pwcs: ^wchar_t, s: cstring, n: size_t) -> size_t ---
|
||||
wcstombs :: proc(s: [^]char, pwcs: ^wchar_t, n: size_t) -> size_t ---
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package libc
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
// 7.24 String handling
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
foreign libc {
|
||||
// 7.24.2 Copying functions
|
||||
memcpy :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---
|
||||
memmove :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---
|
||||
strcpy :: proc(s1: [^]char, s2: cstring) -> [^]char ---
|
||||
strncpy :: proc(s1: [^]char, s2: cstring, n: size_t) -> [^]char ---
|
||||
|
||||
// 7.24.3 Concatenation functions
|
||||
strcat :: proc(s1: [^]char, s2: cstring) -> [^]char ---
|
||||
strncat :: proc(s1: [^]char, s2: cstring, n: size_t) -> [^]char ---
|
||||
|
||||
// 7.24.4 Comparison functions
|
||||
memcmp :: proc(s1, s2: rawptr, n: size_t) -> int ---
|
||||
strcmp :: proc(s1, s2: cstring) -> int ---
|
||||
strcoll :: proc(s1, s2: cstring) -> int ---
|
||||
strncmp :: proc(s1, s2: cstring, n: size_t) -> int ---
|
||||
strxfrm :: proc(s1: [^]char, s2: cstring, n: size_t) -> size_t ---
|
||||
|
||||
// 7.24.5 Search functions
|
||||
memchr :: proc(s: rawptr, c: int, n: size_t) -> rawptr ---
|
||||
strchr :: proc(s: cstring, c: int) -> [^]char ---
|
||||
strcspn :: proc(s1, s2: cstring) -> size_t ---
|
||||
strpbrk :: proc(s1, s2: cstring) -> [^]char ---
|
||||
strrchr :: proc(s: [^]char, c: int) -> [^]char ---
|
||||
strcpn :: proc(s1, s2: cstring) -> [^]char ---
|
||||
strtok :: proc(s1: [^]char, s2: cstring) -> [^]char ---
|
||||
|
||||
// 7.24.6 Miscellaneous functions
|
||||
strerror :: proc(errnum: int) -> [^]char ---
|
||||
strlen :: proc(s: cstring) -> size_t ---
|
||||
}
|
||||
memset :: proc "c" (s: rawptr, c: int, n: size_t) -> rawptr {
|
||||
return runtime.memset(s, c, auto_cast n)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package libc_tests
|
||||
|
||||
import "core:c/libc"
|
||||
|
||||
test_stdio :: proc() {
|
||||
c: libc.char = 'C';
|
||||
libc.puts("Hello from puts");
|
||||
libc.printf("Hello from printf in %c\n", c);
|
||||
}
|
||||
test_thread :: proc() {
|
||||
thread_proc :: proc "c" (rawptr) -> libc.int {
|
||||
libc.printf("Hello from thread");
|
||||
return 42;
|
||||
}
|
||||
thread: libc.thrd_t;
|
||||
libc.thrd_create(&thread, thread_proc, nil);
|
||||
result: libc.int;
|
||||
libc.thrd_join(thread, &result);
|
||||
libc.printf(" %d\n", result);
|
||||
}
|
||||
|
||||
jmp: libc.jmp_buf;
|
||||
test_sjlj :: proc() {
|
||||
if libc.setjmp(&jmp) != 0 {
|
||||
libc.printf("Hello from longjmp\n");
|
||||
return;
|
||||
}
|
||||
libc.printf("Hello from setjmp\n");
|
||||
libc.longjmp(&jmp, 1);
|
||||
}
|
||||
test_signal :: proc() {
|
||||
handler :: proc "c" (sig: libc.int) {
|
||||
libc.printf("Hello from signal handler\n");
|
||||
}
|
||||
libc.signal(libc.SIGABRT, handler);
|
||||
libc.raise(libc.SIGABRT);
|
||||
}
|
||||
test_atexit :: proc() {
|
||||
handler :: proc "c" () {
|
||||
libc.printf("Hello from atexit\n");
|
||||
}
|
||||
libc.atexit(handler);
|
||||
}
|
||||
main :: proc() {
|
||||
test_stdio();
|
||||
test_thread();
|
||||
test_sjlj();
|
||||
test_signal();
|
||||
test_atexit();
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package libc
|
||||
|
||||
// 7.26 Threads
|
||||
|
||||
thrd_start_t :: proc "c" (rawptr) -> int
|
||||
tss_dtor_t :: proc "c" (rawptr)
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc {
|
||||
"system:libucrt.lib",
|
||||
"system:msvcprt.lib"
|
||||
}
|
||||
|
||||
thrd_success :: 0 // _Thrd_success
|
||||
thrd_nomem :: 1 // _Thrd_nomem
|
||||
thrd_timedout :: 2 // _Thrd_timedout
|
||||
thrd_busy :: 3 // _Thrd_busy
|
||||
thrd_error :: 4 // _Thrd_error
|
||||
|
||||
mtx_plain :: 1 // _Mtx_plain
|
||||
mtx_recursive :: 0x100 // _Mtx_recursive
|
||||
mtx_timed :: 4 // _Mtx_timed
|
||||
|
||||
TSS_DTOR_ITERATIONS :: 4 // _TSS_DTOR_ITERATIONS_IMP
|
||||
|
||||
once_flag :: distinct i8 // _Once_flag_imp_t
|
||||
thrd_t :: struct { _: rawptr, _: uint, } // _Thrd_t
|
||||
tss_t :: distinct int // _Tss_imp_t
|
||||
cnd_t :: distinct rawptr // _Cnd_imp_t
|
||||
mtx_t :: distinct rawptr // _Mtx_imp_t
|
||||
|
||||
// MSVCRT does not expose the C11 symbol names as what they are in C11
|
||||
// because they held off implementing <threads.h> and C11 support for so
|
||||
// long that people started implementing their own. To prevent symbol
|
||||
// conflict with existing customers code they had to namespace them
|
||||
// differently. Thus we need to alias the correct symbol names with Odin's
|
||||
// link_name attribute.
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.26.2 Initialization functions
|
||||
@(link_name="_Call_once") call_once :: proc(flag: ^once_flag, func: proc "c" ()) ---
|
||||
// 7.26.3 Condition variable functions
|
||||
@(link_name="_Cnd_broadcast") cnd_broadcast :: proc(cond: ^cnd_t) -> int ---
|
||||
@(link_name="_Cnd_destroy") cnd_destroy :: proc(cond: ^cnd_t) ---
|
||||
@(link_name="_Cnd_init") cnd_init :: proc(cond: ^cnd_t) -> int ---
|
||||
@(link_name="_Cnd_signal") cnd_signal :: proc(cond: ^cnd_t) -> int ---
|
||||
@(link_name="_Cnd_timedwait") cnd_timedwait :: proc(cond: ^cnd_t, ts: ^timespec) -> int ---
|
||||
@(link_name="_Cnd_wait") cnd_wait :: proc(cond: ^cnd_t, mtx: ^mtx_t) -> int ---
|
||||
|
||||
// 7.26.4 Mutex functions
|
||||
@(link_name="_Mtx_destroy") mtx_destroy :: proc(mtx: ^mtx_t) ---
|
||||
@(link_name="_Mtx_init") mtx_init :: proc(mtx: ^mtx_t, type: int) -> int ---
|
||||
@(link_name="_Mtx_lock") mtx_lock :: proc(mtx: ^mtx_t) -> int ---
|
||||
@(link_name="_Mtx_timedlock") mtx_timedlock :: proc(mtx: ^mtx_t, ts: ^timespec) -> int ---
|
||||
@(link_name="_Mtx_trylock") mtx_trylock :: proc(mtx: ^mtx_t) -> int ---
|
||||
@(link_name="_Mtx_unlock") mtx_unlock :: proc(mtx: ^mtx_t) -> int ---
|
||||
|
||||
// 7.26.5 Thread functions
|
||||
@(link_name="_Thrd_create") thrd_create :: proc(thr: ^thrd_t, func: thrd_start_t, arg: rawptr) -> int ---
|
||||
@(link_name="_Thrd_current") thrd_current :: proc() -> thrd_t ---
|
||||
@(link_name="_Thrd_detach") thrd_detach :: proc(thr: thrd_t) -> int ---
|
||||
@(link_name="_Thrd_equal") thrd_equal :: proc(lhs, rhs: thrd_t) -> int ---
|
||||
@(link_name="_Thrd_exit") thrd_exit :: proc(res: int) -> ! ---
|
||||
@(link_name="_Thrd_join") thrd_join :: proc(thr: thrd_t, res: ^int) -> int ---
|
||||
@(link_name="_Thrd_sleep") thrd_sleep :: proc(duration, remaining: ^timespec) -> int ---
|
||||
@(link_name="_Thrd_yield") thrd_yield :: proc() ---
|
||||
|
||||
// 7.26.6 Thread-specific storage functions
|
||||
@(link_name="_Tss_create") tss_create :: proc(key: ^tss_t, dtor: tss_dtor_t) -> int ---
|
||||
@(link_name="_Tss_delete") tss_delete :: proc(key: tss_t) ---
|
||||
@(link_name="_Tss_get") tss_get :: proc(key: tss_t) -> rawptr ---
|
||||
@(link_name="_Tss_set") tss_set :: proc(key: tss_t, val: rawptr) -> int ---
|
||||
}
|
||||
}
|
||||
|
||||
// GLIBC and MUSL compatible constants and types.
|
||||
when ODIN_OS == .Linux {
|
||||
foreign import libc {
|
||||
"system:c",
|
||||
"system:pthread"
|
||||
}
|
||||
|
||||
thrd_success :: 0
|
||||
thrd_busy :: 1
|
||||
thrd_error :: 2
|
||||
thrd_nomem :: 3
|
||||
thrd_timedout :: 4
|
||||
|
||||
mtx_plain :: 0
|
||||
mtx_recursive :: 1
|
||||
mtx_timed :: 2
|
||||
|
||||
TSS_DTOR_ITERATIONS :: 4
|
||||
|
||||
once_flag :: distinct int
|
||||
thrd_t :: distinct ulong
|
||||
tss_t :: distinct uint
|
||||
cnd_t :: struct #raw_union { _: [12]int, _: [12 * size_of(int) / size_of(rawptr)]rawptr, }
|
||||
mtx_t :: struct #raw_union { _: [10 when size_of(long) == 8 else 6]int, _: [5 when size_of(long) == 8 else 6]rawptr, }
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.26.2 Initialization functions
|
||||
call_once :: proc(flag: ^once_flag, func: proc "c" ()) ---
|
||||
|
||||
// 7.26.3 Condition variable functions
|
||||
cnd_broadcast :: proc(cond: ^cnd_t) -> int ---
|
||||
cnd_destroy :: proc(cond: ^cnd_t) ---
|
||||
cnd_init :: proc(cond: ^cnd_t) -> int ---
|
||||
cnd_signal :: proc(cond: ^cnd_t) -> int ---
|
||||
cnd_timedwait :: proc(cond: ^cnd_t, ts: ^timespec) -> int ---
|
||||
cnd_wait :: proc(cond: ^cnd_t, mtx: ^mtx_t) -> int ---
|
||||
|
||||
// 7.26.4 Mutex functions
|
||||
mtx_destroy :: proc(mtx: ^mtx_t) ---
|
||||
mtx_init :: proc(mtx: ^mtx_t, type: int) -> int ---
|
||||
mtx_lock :: proc(mtx: ^mtx_t) -> int ---
|
||||
mtx_timedlock :: proc(mtx: ^mtx_t, ts: ^timespec) -> int ---
|
||||
mtx_trylock :: proc(mtx: ^mtx_t) -> int ---
|
||||
mtx_unlock :: proc(mtx: ^mtx_t) -> int ---
|
||||
|
||||
// 7.26.5 Thread functions
|
||||
thrd_create :: proc(thr: ^thrd_t, func: thrd_start_t, arg: rawptr) -> int ---
|
||||
thrd_current :: proc() -> thrd_t ---
|
||||
thrd_detach :: proc(thr: thrd_t) -> int ---
|
||||
thrd_equal :: proc(lhs, rhs: thrd_t) -> int ---
|
||||
thrd_exit :: proc(res: int) -> ! ---
|
||||
thrd_join :: proc(thr: thrd_t, res: ^int) -> int ---
|
||||
thrd_sleep :: proc(duration, remaining: ^timespec) -> int ---
|
||||
thrd_yield :: proc() ---
|
||||
|
||||
// 7.26.6 Thread-specific storage functions
|
||||
tss_create :: proc(key: ^tss_t, dtor: tss_dtor_t) -> int ---
|
||||
tss_delete :: proc(key: tss_t) ---
|
||||
tss_get :: proc(key: tss_t) -> rawptr ---
|
||||
tss_set :: proc(key: tss_t, val: rawptr) -> int ---
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
// TODO: find out what this is meant to be!
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package libc
|
||||
|
||||
// 7.27 Date and time
|
||||
|
||||
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 {
|
||||
foreign libc {
|
||||
// 7.27.2 Time manipulation functions
|
||||
clock :: proc() -> clock_t ---
|
||||
@(link_name="_difftime64") difftime :: proc(time1, time2: time_t) -> double ---
|
||||
mktime :: proc(timeptr: ^tm) -> time_t ---
|
||||
@(link_name="_time64") time :: proc(timer: ^time_t) -> time_t ---
|
||||
@(link_name="_timespec64_get") timespec_get :: proc(ts: ^timespec, base: int) -> int ---
|
||||
|
||||
// 7.27.3 Time conversion functions
|
||||
asctime :: proc(timeptr: ^tm) -> [^]char ---
|
||||
@(link_name="_ctime64") ctime :: proc(timer: ^time_t) -> [^]char ---
|
||||
@(link_name="_gmtime64") gmtime :: proc(timer: ^time_t) -> ^tm ---
|
||||
@(link_name="_localtime64") localtime :: proc(timer: ^time_t) -> ^tm ---
|
||||
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
|
||||
}
|
||||
|
||||
CLOCKS_PER_SEC :: 1000
|
||||
TIME_UTC :: 1
|
||||
|
||||
clock_t :: distinct long
|
||||
time_t :: distinct i64
|
||||
|
||||
timespec :: struct #align 8 {
|
||||
tv_sec: time_t,
|
||||
tv_nsec: long,
|
||||
}
|
||||
|
||||
tm :: struct #align 8 {
|
||||
tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst: int,
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
clock :: proc() -> clock_t ---
|
||||
difftime :: proc(time1, time2: time_t) -> double ---
|
||||
mktime :: proc(timeptr: ^tm) -> time_t ---
|
||||
time :: proc(timer: ^time_t) -> time_t ---
|
||||
timespec_get :: proc(ts: ^timespec, base: int) -> int ---
|
||||
|
||||
// 7.27.3 Time conversion functions
|
||||
asctime :: proc(timeptr: ^tm) -> [^]char ---
|
||||
ctime :: proc(timer: ^time_t) -> [^]char ---
|
||||
gmtime :: proc(timer: ^time_t) -> ^tm ---
|
||||
localtime :: proc(timer: ^time_t) -> ^tm ---
|
||||
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
|
||||
}
|
||||
|
||||
when ODIN_OS == .OpenBSD {
|
||||
CLOCKS_PER_SEC :: 100
|
||||
} else {
|
||||
CLOCKS_PER_SEC :: 1000000
|
||||
}
|
||||
|
||||
TIME_UTC :: 1
|
||||
|
||||
time_t :: distinct i64
|
||||
|
||||
clock_t :: long
|
||||
|
||||
timespec :: struct {
|
||||
tv_sec: time_t,
|
||||
tv_nsec: long,
|
||||
}
|
||||
|
||||
tm :: struct {
|
||||
tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst: int,
|
||||
tm_gmtoff: long,
|
||||
tm_zone: rawptr,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package libc
|
||||
|
||||
import "core:c"
|
||||
|
||||
char :: c.char // assuming -funsigned-char
|
||||
|
||||
schar :: c.schar
|
||||
short :: c.short
|
||||
int :: c.int
|
||||
long :: c.long
|
||||
longlong :: c.longlong
|
||||
|
||||
uchar :: c.uchar
|
||||
ushort :: c.ushort
|
||||
uint :: c.uint
|
||||
ulong :: c.ulong
|
||||
ulonglong :: c.ulonglong
|
||||
|
||||
bool :: c.bool
|
||||
|
||||
size_t :: c.size_t
|
||||
ssize_t :: c.ssize_t
|
||||
wchar_t :: c.wchar_t
|
||||
|
||||
float :: c.float
|
||||
double :: c.double
|
||||
|
||||
int8_t :: c.int8_t
|
||||
uint8_t :: c.uint8_t
|
||||
int16_t :: c.int16_t
|
||||
uint16_t :: c.uint16_t
|
||||
int32_t :: c.int32_t
|
||||
uint32_t :: c.uint32_t
|
||||
int64_t :: c.int64_t
|
||||
uint64_t :: c.uint64_t
|
||||
|
||||
int_least8_t :: c.int_least8_t
|
||||
uint_least8_t :: c.uint_least8_t
|
||||
int_least16_t :: c.int_least16_t
|
||||
uint_least16_t :: c.uint_least16_t
|
||||
int_least32_t :: c.int_least32_t
|
||||
uint_least32_t :: c.uint_least32_t
|
||||
int_least64_t :: c.int_least64_t
|
||||
uint_least64_t :: c.uint_least64_t
|
||||
|
||||
int_fast8_t :: c.int_fast8_t
|
||||
uint_fast8_t :: c.uint_fast8_t
|
||||
int_fast16_t :: c.int_fast16_t
|
||||
uint_fast16_t :: c.uint_fast16_t
|
||||
int_fast32_t :: c.int_fast32_t
|
||||
uint_fast32_t :: c.uint_fast32_t
|
||||
int_fast64_t :: c.int_fast64_t
|
||||
uint_fast64_t :: c.uint_fast64_t
|
||||
|
||||
intptr_t :: c.intptr_t
|
||||
uintptr_t :: c.uintptr_t
|
||||
ptrdiff_t :: c.ptrdiff_t
|
||||
|
||||
intmax_t :: c.intmax_t
|
||||
uintmax_t :: c.uintmax_t
|
||||
|
||||
// Copy C's rules for type promotion here by forcing the type on the literals.
|
||||
INT8_MAX :: c.INT8_MAX
|
||||
INT16_MAX :: c.INT16_MAX
|
||||
INT32_MAX :: c.INT32_MAX
|
||||
INT64_MAX :: c.INT64_MAX
|
||||
|
||||
UINT8_MAX :: c.UINT8_MAX
|
||||
UINT16_MAX :: c.UINT16_MAX
|
||||
UINT32_MAX :: c.UINT32_MAX
|
||||
UINT64_MAX :: c.UINT64_MAX
|
||||
|
||||
INT8_MIN :: c.INT8_MIN
|
||||
INT16_MIN :: c.INT16_MIN
|
||||
INT32_MIN :: c.INT32_MIN
|
||||
INT64_MIN :: c.INT64_MIN
|
||||
|
||||
SIZE_MAX :: c.SIZE_MAX
|
||||
|
||||
PTRDIFF_MIN :: c.PTRDIFF_MIN
|
||||
PTRDIFF_MAX :: c.PTRDIFF_MAX
|
||||
|
||||
WCHAR_MIN :: c.WCHAR_MIN
|
||||
WCHAR_MAX :: c.WCHAR_MAX
|
||||
|
||||
NULL :: rawptr(uintptr(0))
|
||||
|
||||
NDEBUG :: !ODIN_DEBUG
|
||||
|
||||
CHAR_BIT :: 8
|
||||
@@ -0,0 +1,23 @@
|
||||
package libc
|
||||
|
||||
// 7.28 Unicode utilities
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.28.1 Restartable multibyte/wide character conversion functions
|
||||
mbrtoc16 :: proc(pc16: [^]char16_t, s: cstring, n: size_t, ps: ^mbstate_t) -> size_t ---
|
||||
c16rtomb :: proc(s: ^char, c16: char16_t, ps: ^mbstate_t) -> size_t ---
|
||||
mbrtoc32 :: proc(pc32: [^]char32_t, s: cstring, n: size_t, ps: ^mbstate_t) -> size_t ---
|
||||
c32rtomb :: proc(s: ^char, c32: char32_t, ps: ^mbstate_t) -> size_t ---
|
||||
}
|
||||
|
||||
char16_t :: uint_least16_t
|
||||
char32_t :: uint_least32_t
|
||||
@@ -0,0 +1,110 @@
|
||||
package libc
|
||||
|
||||
// 7.29 Extended multibyte and wide character utilities
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.29.2 Formatted wide character input/output functions
|
||||
fwprintf :: proc(stream: ^FILE, format: [^]wchar_t, #c_vararg arg: ..any) -> int ---
|
||||
fwscanf :: proc(stream: ^FILE, format: [^]wchar_t, #c_vararg arg: ..any) -> int ---
|
||||
swprintf :: proc(stream: ^FILE, n: size_t, format: [^]wchar_t, #c_vararg arg: ..any) -> int ---
|
||||
swscanf :: proc(s, format: [^]wchar_t, #c_vararg arg: ..any) -> int ---
|
||||
vfwprintf :: proc(stream: ^FILE, format: [^]wchar_t, arg: va_list) -> int ---
|
||||
vfwscanf :: proc(stream: ^FILE, format: [^]wchar_t, arg: va_list) -> int ---
|
||||
vswprintf :: proc(s: [^]wchar_t, n: size_t, format: [^]wchar_t, arg: va_list) -> int ---
|
||||
vswscanf :: proc(s, format: [^]wchar_t, arg: va_list) -> int ---
|
||||
vwprintf :: proc(format: [^]wchar_t, arg: va_list) -> int ---
|
||||
vwscanf :: proc(format: [^]wchar_t, arg: va_list) -> int ---
|
||||
wprintf :: proc(format: [^]wchar_t, #c_vararg arg: ..any) -> int ---
|
||||
wscanf :: proc(format: [^]wchar_t, #c_vararg arg: ..any) -> int ---
|
||||
|
||||
// 7.29.3 Wide character input/output functions
|
||||
fwgetc :: proc(stream: ^FILE) -> wint_t ---
|
||||
fgetws :: proc(s: [^]wchar_t, n: int, stream: ^FILE) -> wchar_t ---
|
||||
fputwc :: proc(c: wchar_t, stream: ^FILE) -> wint_t ---
|
||||
fputws :: proc(s: [^]wchar_t, stream: ^FILE) -> int ---
|
||||
fwide :: proc(stream: ^FILE, mode: int) -> int ---
|
||||
getwc :: proc(stream: ^FILE) -> wint_t ---
|
||||
getwchar :: proc() -> wint_t ---
|
||||
putwc :: proc(c: wchar_t, stream: ^FILE) -> wint_t ---
|
||||
putwchar :: proc(c: wchar_t) -> wint_t ---
|
||||
ungetwc :: proc(c: wchar_t, stream: ^FILE) -> wint_t ---
|
||||
|
||||
// 7.29.4 General wide string utilities
|
||||
wcstod :: proc(nptr: [^]wchar_t, endptr: ^[^]wchar_t) -> double ---
|
||||
wcstof :: proc(nptr: [^]wchar_t, endptr: ^[^]wchar_t) -> float ---
|
||||
wcstol :: proc(nptr: [^]wchar_t, endptr: ^[^]wchar_t, base: int) -> long ---
|
||||
wcstoll :: proc(nptr: [^]wchar_t, endptr: ^[^]wchar_t, base: int) -> longlong ---
|
||||
wcstoul :: proc(nptr: [^]wchar_t, endptr: ^[^]wchar_t, base: int) -> ulong ---
|
||||
wcstoull :: proc(nptr: [^]wchar_t, endptr: ^[^]wchar_t, base: int) -> ulonglong ---
|
||||
|
||||
// 7.29.4.2 Wide string copying functions
|
||||
wcscpy :: proc(s1, s2: [^]wchar_t) -> [^]wchar_t ---
|
||||
wcsncpy :: proc(s1, s2: [^]wchar_t, n: size_t) -> [^]wchar_t ---
|
||||
wmemcpy :: proc(s1, s2: [^]wchar_t, n: size_t) -> [^]wchar_t ---
|
||||
wmemmove :: proc(s1, s2: [^]wchar_t, n: size_t) -> [^]wchar_t ---
|
||||
|
||||
// 7.29.4.3 Wide string concatenation functions
|
||||
wcscat :: proc(s1, s2: [^]wchar_t) -> [^]wchar_t ---
|
||||
wcsncat :: proc(s1, s2: [^]wchar_t, n: size_t) -> [^]wchar_t ---
|
||||
|
||||
// 7.29.4.4 Wide string comparison functions
|
||||
wcscmp :: proc(s1, s2: [^]wchar_t) -> int ---
|
||||
wcscoll :: proc(s1, s2: [^]wchar_t) -> int ---
|
||||
wcsncmp :: proc(s1, s2: [^]wchar_t, n: size_t) -> int ---
|
||||
wcsxfrm :: proc(s1, s2: [^]wchar_t, n: size_t) -> size_t ---
|
||||
wmemcmp :: proc(s1, s2: [^]wchar_t, n: size_t) -> int ---
|
||||
|
||||
// 7.29.4.5 Wide string search functions
|
||||
wcschr :: proc(s: [^]wchar_t, c: wchar_t) -> [^]wchar_t ---
|
||||
wcscspn :: proc(s1, s2: [^]wchar_t) -> size_t ---
|
||||
wcspbrk :: proc(s1, s2: [^]wchar_t) -> [^]wchar_t ---
|
||||
wcsrchr :: proc(s: [^]wchar_t, c: wchar_t) -> [^]wchar_t ---
|
||||
wcsspn :: proc(s1, s2: [^]wchar_t) -> size_t ---
|
||||
wcsstr :: proc(s1, s2: [^]wchar_t) -> [^]wchar_t ---
|
||||
wcstok :: proc(s1, s2: [^]wchar_t, ptr: ^[^]wchar_t) -> [^]wchar_t ---
|
||||
wmemchr :: proc(s: [^]wchar_t, c: wchar_t, n: size_t) -> [^]wchar_t ---
|
||||
|
||||
// 7.29.4.6 Miscellaneous functions
|
||||
wcslen :: proc(s: [^]wchar_t) -> size_t ---
|
||||
wmemset :: proc(s: [^]wchar_t, c: wchar_t, n: size_t) -> [^]wchar_t ---
|
||||
|
||||
// 7.29.5 Wide character time conversion functions
|
||||
wcsftime :: proc(s: [^]wchar_t, maxsize: size_t, format: [^]wchar_t, timeptr: ^tm) -> size_t ---
|
||||
|
||||
// 7.29.6.1 Single-byte/wide character conversion functions
|
||||
btowc :: proc(c: int) -> wint_t ---
|
||||
wctob :: proc(c: wint_t) -> int ---
|
||||
|
||||
// 7.29.6.2 Conversion state functions
|
||||
mbsinit :: proc(ps: ^mbstate_t) -> int ---
|
||||
|
||||
// 7.29.6.3 Restartable multibyte/wide character conversion functions
|
||||
mbrlen :: proc(s: cstring, n: size_t, ps: ^mbstate_t) -> size_t ---
|
||||
mbrtowc :: proc(pwc: [^]wchar_t, s: cstring, n: size_t, ps: ^mbstate_t) -> size_t ---
|
||||
wcrtomb :: proc(s: ^char, wc: wchar_t, ps: ^mbstate_t) -> size_t ---
|
||||
|
||||
// 7.29.6.4 Restartable multibyte/wide string conversion functions
|
||||
mbsrtowcs :: proc(dst: [^]wchar_t, src: ^cstring, len: size_t, ps: ^mbstate_t) -> size_t ---
|
||||
wcsrtombs :: proc(dst: ^char, src: ^[^]wchar_t, len: size_t, ps: ^mbstate_t) -> size_t ---
|
||||
}
|
||||
|
||||
// Large enough and aligned enough for any wide-spread in-use libc.
|
||||
mbstate_t :: struct #align 16 { _: [32]char, }
|
||||
|
||||
// Odin does not have default argument promotion so the need for a separate type
|
||||
// here isn't necessary, though make it distinct just to be safe.
|
||||
wint_t :: distinct wchar_t
|
||||
|
||||
// Calculate these values correctly regardless of what type wchar_t actually is.
|
||||
WINT_MIN :: 0
|
||||
WINT_MAX :: 1 << (size_of(wint_t) * 8)
|
||||
WEOF :: ~wint_t(0)
|
||||
@@ -0,0 +1,60 @@
|
||||
package libc
|
||||
|
||||
// 7.30 Wide character classification and mapping utilities
|
||||
|
||||
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 {
|
||||
wctrans_t :: distinct wchar_t
|
||||
wctype_t :: distinct ushort
|
||||
}
|
||||
|
||||
when ODIN_OS == .Linux {
|
||||
wctrans_t :: distinct intptr_t
|
||||
wctype_t :: distinct ulong
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
wctrans_t :: distinct int
|
||||
wctype_t :: distinct u32
|
||||
}
|
||||
|
||||
when ODIN_OS == .OpenBSD {
|
||||
wctrans_t :: distinct rawptr
|
||||
wctype_t :: distinct rawptr
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.30.2.1 Wide character classification functions
|
||||
iswalnum :: proc(wc: wint_t) -> int ---
|
||||
iswalpha :: proc(wc: wint_t) -> int ---
|
||||
iswblank :: proc(wc: wint_t) -> int ---
|
||||
iswcntrl :: proc(wc: wint_t) -> int ---
|
||||
iswdigit :: proc(wc: wint_t) -> int ---
|
||||
iswgraph :: proc(wc: wint_t) -> int ---
|
||||
iswlower :: proc(wc: wint_t) -> int ---
|
||||
iswprint :: proc(wc: wint_t) -> int ---
|
||||
iswpunct :: proc(wc: wint_t) -> int ---
|
||||
iswspace :: proc(wc: wint_t) -> int ---
|
||||
iswupper :: proc(wc: wint_t) -> int ---
|
||||
iswxdigit :: proc(wc: wint_t) -> int ---
|
||||
|
||||
// 7.30.2.2 Extensible wide character classification functions
|
||||
iswctype :: proc(wc: wint_t, desc: wctype_t) -> int ---
|
||||
wctype :: proc(property: cstring) -> wctype_t ---
|
||||
|
||||
// 7.30.3 Wide character case mapping utilities
|
||||
towlower :: proc(wc: wint_t) -> wint_t ---
|
||||
towupper :: proc(wc: wint_t) -> wint_t ---
|
||||
|
||||
// 7.30.3.2 Extensible wide character case mapping functions
|
||||
towctrans :: proc(wc: wint_t, desc: wctrans_t) -> wint_t ---
|
||||
wctrans :: proc(property: cstring) -> wctrans_t ---
|
||||
}
|
||||
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
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"
|
||||
import "core:bytes"
|
||||
import "core:runtime"
|
||||
|
||||
/*
|
||||
These settings bound how much compression algorithms will allocate for their output buffer.
|
||||
If streaming their output, these are unnecessary and will be ignored.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
When a decompression routine doesn't stream its output, but writes to a buffer,
|
||||
we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
|
||||
*/
|
||||
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20))
|
||||
|
||||
/*
|
||||
This bounds the maximum a buffer will resize to as needed, or the maximum we'll
|
||||
pre-allocate if you inform the decompression routine you know the payload size.
|
||||
|
||||
For reference, the largest payload size of a GZIP file is 4 GiB.
|
||||
|
||||
*/
|
||||
when size_of(uintptr) == 8 {
|
||||
/*
|
||||
For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
||||
which is GZIP and PKZIP's max payload size.
|
||||
*/
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
|
||||
} else {
|
||||
/*
|
||||
For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
||||
*/
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
||||
}
|
||||
|
||||
|
||||
Error :: union {
|
||||
General_Error,
|
||||
Deflate_Error,
|
||||
ZLIB_Error,
|
||||
GZIP_Error,
|
||||
ZIP_Error,
|
||||
|
||||
runtime.Allocator_Error,
|
||||
}
|
||||
|
||||
General_Error :: enum {
|
||||
File_Not_Found,
|
||||
Cannot_Open_File,
|
||||
File_Too_Short,
|
||||
Stream_Too_Short,
|
||||
Output_Too_Short,
|
||||
Unknown_Compression_Method,
|
||||
Checksum_Failed,
|
||||
Incompatible_Options,
|
||||
Unimplemented,
|
||||
|
||||
/*
|
||||
Memory errors
|
||||
*/
|
||||
Allocation_Failed,
|
||||
Resize_Failed,
|
||||
}
|
||||
|
||||
GZIP_Error :: enum {
|
||||
Invalid_GZIP_Signature,
|
||||
Reserved_Flag_Set,
|
||||
Invalid_Extra_Data,
|
||||
Original_Name_Too_Long,
|
||||
Comment_Too_Long,
|
||||
Payload_Length_Invalid,
|
||||
Payload_CRC_Invalid,
|
||||
|
||||
/*
|
||||
GZIP's payload can be a maximum of max(u32le), or 4 GiB.
|
||||
If you tell it you expect it to contain more, that's obviously an error.
|
||||
*/
|
||||
Payload_Size_Exceeds_Max_Payload,
|
||||
/*
|
||||
For buffered instead of streamed output, the payload size can't exceed
|
||||
the max set by the `COMPRESS_OUTPUT_ALLOCATE_MAX` switch in compress/common.odin.
|
||||
|
||||
You can tweak this setting using `-define:COMPRESS_OUTPUT_ALLOCATE_MAX=size_in_bytes`
|
||||
*/
|
||||
Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX,
|
||||
|
||||
}
|
||||
|
||||
ZIP_Error :: enum {
|
||||
Invalid_ZIP_File_Signature,
|
||||
Unexpected_Signature,
|
||||
Insert_Next_Disk,
|
||||
Expected_End_of_Central_Directory_Record,
|
||||
}
|
||||
|
||||
ZLIB_Error :: enum {
|
||||
Unsupported_Window_Size,
|
||||
FDICT_Unsupported,
|
||||
Unsupported_Compression_Level,
|
||||
Code_Buffer_Malformed,
|
||||
}
|
||||
|
||||
Deflate_Error :: enum {
|
||||
Huffman_Bad_Sizes,
|
||||
Huffman_Bad_Code_Lengths,
|
||||
Inflate_Error,
|
||||
Bad_Distance,
|
||||
Bad_Huffman_Code,
|
||||
Len_Nlen_Mismatch,
|
||||
BType_3,
|
||||
}
|
||||
|
||||
|
||||
// General I/O context for ZLIB, LZW, etc.
|
||||
Context_Memory_Input :: struct #packed {
|
||||
input_data: []u8,
|
||||
output: ^bytes.Buffer,
|
||||
bytes_written: i64,
|
||||
|
||||
code_buffer: u64,
|
||||
num_bits: u64,
|
||||
|
||||
/*
|
||||
If we know the data size, we can optimize the reads and writes.
|
||||
*/
|
||||
size_packed: i64,
|
||||
size_unpacked: i64,
|
||||
}
|
||||
#assert(size_of(Context_Memory_Input) == 64)
|
||||
|
||||
Context_Stream_Input :: struct #packed {
|
||||
input_data: []u8,
|
||||
input: io.Stream,
|
||||
output: ^bytes.Buffer,
|
||||
bytes_written: i64,
|
||||
|
||||
code_buffer: u64,
|
||||
num_bits: u64,
|
||||
|
||||
/*
|
||||
If we know the data size, we can optimize the reads and writes.
|
||||
*/
|
||||
size_packed: i64,
|
||||
size_unpacked: i64,
|
||||
|
||||
/*
|
||||
Flags:
|
||||
`input_fully_in_memory`
|
||||
true = This tells us we read input from `input_data` exclusively. [] = EOF.
|
||||
false = Try to refill `input_data` from the `input` stream.
|
||||
*/
|
||||
input_fully_in_memory: b8,
|
||||
|
||||
padding: [1]u8,
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: The stream versions should really only check if a certain method is available once, perhaps even during setup.
|
||||
|
||||
Bit and byte readers may be merged so that reading bytes will grab them from the bit buffer first.
|
||||
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
|
||||
}
|
||||
|
||||
input_size_from_stream :: proc(z: ^Context_Stream_Input) -> (res: i64, err: Error) {
|
||||
return io.size(z.input), nil
|
||||
}
|
||||
|
||||
input_size :: proc{input_size_from_memory, input_size_from_stream}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= size {
|
||||
res = z.input_data[:size]
|
||||
z.input_data = z.input_data[size:]
|
||||
return res, .None
|
||||
}
|
||||
}
|
||||
|
||||
if len(z.input_data) == 0 {
|
||||
return []u8{}, .EOF
|
||||
} else {
|
||||
return []u8{}, .Short_Buffer
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||
b := make([]u8, size, context.temp_allocator)
|
||||
_, e := z.input->impl_read(b[:])
|
||||
if e == .None {
|
||||
return b, .None
|
||||
}
|
||||
|
||||
return []u8{}, e
|
||||
}
|
||||
|
||||
read_slice :: proc{read_slice_from_memory, read_slice_from_stream}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
|
||||
b, e := read_slice(z, size_of(T))
|
||||
if e == .None {
|
||||
return (^T)(&b[0])^, .None
|
||||
}
|
||||
|
||||
return T{}, e
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) {
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= 1 {
|
||||
res = z.input_data[0]
|
||||
z.input_data = z.input_data[1:]
|
||||
return res, .None
|
||||
}
|
||||
}
|
||||
return 0, .EOF
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) {
|
||||
b, e := read_slice_from_stream(z, 1)
|
||||
if e == .None {
|
||||
return b[0], .None
|
||||
}
|
||||
|
||||
return 0, e
|
||||
}
|
||||
|
||||
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream}
|
||||
|
||||
/*
|
||||
You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
||||
preferentially.
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
||||
if z.num_bits >= 8 {
|
||||
res = u8(read_bits_no_refill_lsb(z, 8))
|
||||
} else {
|
||||
size, _ := input_size(z)
|
||||
if size > 0 {
|
||||
res, err = read_u8(z)
|
||||
} else {
|
||||
err = .EOF
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= size {
|
||||
buf := z.input_data[:size]
|
||||
return (^T)(&buf[0])^, .None
|
||||
}
|
||||
}
|
||||
|
||||
if len(z.input_data) == 0 {
|
||||
return T{}, .EOF
|
||||
} else {
|
||||
return T{}, .Short_Buffer
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
|
||||
// Get current position to read from.
|
||||
curr, e1 := z.input->impl_seek(0, .Current)
|
||||
if e1 != .None {
|
||||
return T{}, e1
|
||||
}
|
||||
r, e2 := io.to_reader_at(z.input)
|
||||
if !e2 {
|
||||
return T{}, .Empty
|
||||
}
|
||||
when size <= 128 {
|
||||
b: [size]u8
|
||||
} else {
|
||||
b := make([]u8, size, context.temp_allocator)
|
||||
}
|
||||
_, e3 := io.read_at(r, b[:], curr)
|
||||
if e3 != .None {
|
||||
return T{}, .Empty
|
||||
}
|
||||
|
||||
res = (^T)(&b[0])^
|
||||
return res, .None
|
||||
}
|
||||
|
||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream}
|
||||
|
||||
|
||||
|
||||
// Sliding window read back
|
||||
@(optimization_mode="speed")
|
||||
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
|
||||
// Look back into the sliding window.
|
||||
return z.output.buf[z.bytes_written - offset], .None
|
||||
}
|
||||
|
||||
// Generalized bit reader LSB
|
||||
@(optimization_mode="speed")
|
||||
refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) {
|
||||
refill := u64(width)
|
||||
b := u64(0)
|
||||
|
||||
if z.num_bits > refill {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
if len(z.input_data) != 0 {
|
||||
b = u64(z.input_data[0])
|
||||
z.input_data = z.input_data[1:]
|
||||
} else {
|
||||
b = 0
|
||||
}
|
||||
|
||||
z.code_buffer |= b << u8(z.num_bits)
|
||||
z.num_bits += 8
|
||||
if z.num_bits > refill {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generalized bit reader LSB
|
||||
@(optimization_mode="speed")
|
||||
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
||||
refill := u64(width)
|
||||
|
||||
for {
|
||||
if z.num_bits > refill {
|
||||
break
|
||||
}
|
||||
if z.code_buffer == 0 && z.num_bits > 63 {
|
||||
z.num_bits = 0
|
||||
}
|
||||
if z.code_buffer >= 1 << uint(z.num_bits) {
|
||||
// Code buffer is malformed.
|
||||
z.num_bits = max(u64)
|
||||
return
|
||||
}
|
||||
b, err := read_u8(z)
|
||||
if err != .None {
|
||||
// This is fine at the end of the file.
|
||||
return
|
||||
}
|
||||
z.code_buffer |= (u64(b) << u8(z.num_bits))
|
||||
z.num_bits += 8
|
||||
}
|
||||
}
|
||||
|
||||
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
|
||||
z.code_buffer >>= width
|
||||
z.num_bits -= u64(width)
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) {
|
||||
z.code_buffer >>= width
|
||||
z.num_bits -= u64(width)
|
||||
}
|
||||
|
||||
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
if z.num_bits < u64(width) {
|
||||
refill_lsb(z)
|
||||
}
|
||||
return u32(z.code_buffer & ~(~u64(0) << width))
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
if z.num_bits < u64(width) {
|
||||
refill_lsb(z)
|
||||
}
|
||||
return u32(z.code_buffer & ~(~u64(0) << width))
|
||||
}
|
||||
|
||||
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
assert(z.num_bits >= u64(width))
|
||||
return u32(z.code_buffer & ~(~u64(0) << width))
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
assert(z.num_bits >= u64(width))
|
||||
return u32(z.code_buffer & ~(~u64(0) << width))
|
||||
}
|
||||
|
||||
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
k := #force_inline peek_bits_lsb(z, width)
|
||||
#force_inline consume_bits_lsb(z, width)
|
||||
return k
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
k := peek_bits_lsb(z, width)
|
||||
consume_bits_lsb(z, width)
|
||||
return k
|
||||
}
|
||||
|
||||
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
k := #force_inline peek_bits_no_refill_lsb(z, width)
|
||||
#force_inline consume_bits_lsb(z, width)
|
||||
return k
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
k := peek_bits_no_refill_lsb(z, width)
|
||||
consume_bits_lsb(z, width)
|
||||
return k
|
||||
}
|
||||
|
||||
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
|
||||
discard := u8(z.num_bits & 7)
|
||||
#force_inline consume_bits_lsb(z, discard)
|
||||
}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
||||
discard := u8(z.num_bits & 7)
|
||||
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};
|
||||
@@ -0,0 +1,89 @@
|
||||
//+ignore
|
||||
package gzip
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
|
||||
A small GZIP implementation as an example.
|
||||
*/
|
||||
|
||||
import "core:bytes"
|
||||
import "core:os"
|
||||
import "core:compress"
|
||||
import "core:fmt"
|
||||
|
||||
// Small GZIP file with fextra, fname and fcomment present.
|
||||
@private
|
||||
TEST: []u8 = {
|
||||
0x1f, 0x8b, 0x08, 0x1c, 0xcb, 0x3b, 0x3a, 0x5a,
|
||||
0x02, 0x03, 0x07, 0x00, 0x61, 0x62, 0x03, 0x00,
|
||||
0x63, 0x64, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x00, 0x54, 0x68, 0x69, 0x73,
|
||||
0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f,
|
||||
0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2b, 0x48,
|
||||
0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x01, 0x00, 0x15,
|
||||
0x6a, 0x2c, 0x42, 0x07, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
// Set up output buffer.
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
stdout :: proc(s: string) {
|
||||
os.write_string(os.stdout, s)
|
||||
}
|
||||
stderr :: proc(s: string) {
|
||||
os.write_string(os.stderr, s)
|
||||
}
|
||||
|
||||
args := os.args
|
||||
|
||||
if len(args) < 2 {
|
||||
stderr("No input file specified.\n")
|
||||
err := load(slice=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
if err == nil {
|
||||
stdout("Displaying test vector: ")
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
stdout("\n")
|
||||
} else {
|
||||
fmt.printf("gzip.load returned %v\n", err)
|
||||
}
|
||||
bytes.buffer_destroy(&buf)
|
||||
os.exit(0)
|
||||
}
|
||||
|
||||
// The rest are all files.
|
||||
args = args[1:]
|
||||
err: Error
|
||||
|
||||
for file in args {
|
||||
if file == "-" {
|
||||
// Read from stdin
|
||||
s := os.stream_from_handle(os.stdin)
|
||||
ctx := &compress.Context_Stream_Input{
|
||||
input = s,
|
||||
}
|
||||
err = load(ctx, &buf)
|
||||
} else {
|
||||
err = load(file, &buf)
|
||||
}
|
||||
if err != nil {
|
||||
if err != E_General.File_Not_Found {
|
||||
stderr("File not found: ")
|
||||
stderr(file)
|
||||
stderr("\n")
|
||||
os.exit(1)
|
||||
}
|
||||
stderr("GZIP returned an error.\n")
|
||||
bytes.buffer_destroy(&buf)
|
||||
os.exit(2)
|
||||
}
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
}
|
||||
bytes.buffer_destroy(&buf)
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
package gzip
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
|
||||
This package implements support for the GZIP file format v4.3,
|
||||
as specified in RFC 1952.
|
||||
|
||||
It is implemented in such a way that it lends itself naturally
|
||||
to be the input to a complementary TAR implementation.
|
||||
*/
|
||||
|
||||
import "core:compress/zlib"
|
||||
import "core:compress"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
import "core:bytes"
|
||||
import "core:hash"
|
||||
|
||||
Magic :: enum u16le {
|
||||
GZIP = 0x8b << 8 | 0x1f,
|
||||
}
|
||||
|
||||
Header :: struct #packed {
|
||||
magic: Magic,
|
||||
compression_method: Compression,
|
||||
flags: Header_Flags,
|
||||
modification_time: u32le,
|
||||
xfl: Compression_Flags,
|
||||
os: OS,
|
||||
}
|
||||
#assert(size_of(Header) == 10)
|
||||
|
||||
Header_Flag :: enum u8 {
|
||||
// Order is important
|
||||
text = 0,
|
||||
header_crc = 1,
|
||||
extra = 2,
|
||||
name = 3,
|
||||
comment = 4,
|
||||
reserved_1 = 5,
|
||||
reserved_2 = 6,
|
||||
reserved_3 = 7,
|
||||
}
|
||||
Header_Flags :: distinct bit_set[Header_Flag; u8]
|
||||
|
||||
OS :: enum u8 {
|
||||
FAT = 0,
|
||||
Amiga = 1,
|
||||
VMS = 2,
|
||||
Unix = 3,
|
||||
VM_CMS = 4,
|
||||
Atari_TOS = 5,
|
||||
HPFS = 6,
|
||||
Macintosh = 7,
|
||||
Z_System = 8,
|
||||
CP_M = 9,
|
||||
TOPS_20 = 10,
|
||||
NTFS = 11,
|
||||
QDOS = 12,
|
||||
Acorn_RISCOS = 13,
|
||||
_Unknown = 14,
|
||||
Unknown = 255,
|
||||
}
|
||||
OS_Name :: #sparse[OS]string{
|
||||
._Unknown = "",
|
||||
.FAT = "FAT",
|
||||
.Amiga = "Amiga",
|
||||
.VMS = "VMS/OpenVMS",
|
||||
.Unix = "Unix",
|
||||
.VM_CMS = "VM/CMS",
|
||||
.Atari_TOS = "Atari TOS",
|
||||
.HPFS = "HPFS",
|
||||
.Macintosh = "Macintosh",
|
||||
.Z_System = "Z-System",
|
||||
.CP_M = "CP/M",
|
||||
.TOPS_20 = "TOPS-20",
|
||||
.NTFS = "NTFS",
|
||||
.QDOS = "QDOS",
|
||||
.Acorn_RISCOS = "Acorn RISCOS",
|
||||
.Unknown = "Unknown",
|
||||
}
|
||||
|
||||
Compression :: enum u8 {
|
||||
DEFLATE = 8,
|
||||
}
|
||||
|
||||
Compression_Flags :: enum u8 {
|
||||
Maximum_Compression = 2,
|
||||
Fastest_Compression = 4,
|
||||
}
|
||||
|
||||
Error :: compress.Error
|
||||
E_General :: compress.General_Error
|
||||
E_GZIP :: compress.GZIP_Error
|
||||
E_ZLIB :: compress.ZLIB_Error
|
||||
E_Deflate :: compress.Deflate_Error
|
||||
|
||||
GZIP_MAX_PAYLOAD_SIZE :: int(max(u32le))
|
||||
|
||||
load :: proc{load_from_slice, 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
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
|
||||
err = E_General.File_Not_Found
|
||||
if ok {
|
||||
err = load_from_slice(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) {
|
||||
buf := buf
|
||||
|
||||
z := &compress.Context_Memory_Input{
|
||||
input_data = slice,
|
||||
output = buf,
|
||||
}
|
||||
return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator)
|
||||
}
|
||||
|
||||
load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
buf := buf
|
||||
expected_output_size := expected_output_size
|
||||
|
||||
input_data_consumed := 0
|
||||
|
||||
z.output = buf
|
||||
|
||||
if expected_output_size > GZIP_MAX_PAYLOAD_SIZE {
|
||||
return E_GZIP.Payload_Size_Exceeds_Max_Payload
|
||||
}
|
||||
|
||||
if expected_output_size > compress.COMPRESS_OUTPUT_ALLOCATE_MAX {
|
||||
return E_GZIP.Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX
|
||||
}
|
||||
|
||||
b: []u8
|
||||
|
||||
header, e := compress.read_data(z, Header)
|
||||
if e != .None {
|
||||
return E_General.File_Too_Short
|
||||
}
|
||||
input_data_consumed += size_of(Header)
|
||||
|
||||
if header.magic != .GZIP {
|
||||
return E_GZIP.Invalid_GZIP_Signature
|
||||
}
|
||||
if header.compression_method != .DEFLATE {
|
||||
return E_General.Unknown_Compression_Method
|
||||
}
|
||||
|
||||
if header.os >= ._Unknown {
|
||||
header.os = .Unknown
|
||||
}
|
||||
|
||||
if .reserved_1 in header.flags || .reserved_2 in header.flags || .reserved_3 in header.flags {
|
||||
return E_GZIP.Reserved_Flag_Set
|
||||
}
|
||||
|
||||
// printf("signature: %v\n", header.magic);
|
||||
// printf("compression: %v\n", header.compression_method);
|
||||
// printf("flags: %v\n", header.flags);
|
||||
// printf("modification time: %v\n", time.unix(i64(header.modification_time), 0));
|
||||
// printf("xfl: %v (%v)\n", header.xfl, int(header.xfl));
|
||||
// printf("os: %v\n", OS_Name[header.os]);
|
||||
|
||||
if .extra in header.flags {
|
||||
xlen, e_extra := compress.read_data(z, u16le)
|
||||
input_data_consumed += 2
|
||||
|
||||
if e_extra != .None {
|
||||
return E_General.Stream_Too_Short
|
||||
}
|
||||
// printf("Extra data present (%v bytes)\n", xlen);
|
||||
if xlen < 4 {
|
||||
// Minimum length is 2 for ID + 2 for a field length, if set to zero.
|
||||
return E_GZIP.Invalid_Extra_Data
|
||||
}
|
||||
|
||||
field_id: [2]u8
|
||||
field_length: u16le
|
||||
field_error: io.Error
|
||||
|
||||
for xlen >= 4 {
|
||||
// println("Parsing Extra field(s).");
|
||||
field_id, field_error = compress.read_data(z, [2]u8)
|
||||
if field_error != .None {
|
||||
// printf("Parsing Extra returned: %v\n", field_error);
|
||||
return E_General.Stream_Too_Short
|
||||
}
|
||||
xlen -= 2
|
||||
input_data_consumed += 2
|
||||
|
||||
field_length, field_error = compress.read_data(z, u16le)
|
||||
if field_error != .None {
|
||||
// printf("Parsing Extra returned: %v\n", field_error);
|
||||
return E_General.Stream_Too_Short
|
||||
}
|
||||
xlen -= 2
|
||||
input_data_consumed += 2
|
||||
|
||||
if xlen <= 0 {
|
||||
// We're not going to try and recover by scanning for a ZLIB header.
|
||||
// Who knows what else is wrong with this file.
|
||||
return E_GZIP.Invalid_Extra_Data
|
||||
}
|
||||
|
||||
// printf(" Field \"%v\" of length %v found: ", string(field_id[:]), field_length);
|
||||
if field_length > 0 {
|
||||
b, field_error = compress.read_slice(z, int(field_length))
|
||||
if field_error != .None {
|
||||
// printf("Parsing Extra returned: %v\n", field_error);
|
||||
return E_General.Stream_Too_Short
|
||||
}
|
||||
xlen -= field_length
|
||||
input_data_consumed += int(field_length)
|
||||
|
||||
// printf("%v\n", string(field_data));
|
||||
}
|
||||
|
||||
if xlen != 0 {
|
||||
return E_GZIP.Invalid_Extra_Data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if .name in header.flags {
|
||||
// Should be enough.
|
||||
name: [1024]u8
|
||||
i := 0
|
||||
name_error: io.Error
|
||||
|
||||
for i < len(name) {
|
||||
b, name_error = compress.read_slice(z, 1)
|
||||
if name_error != .None {
|
||||
return E_General.Stream_Too_Short
|
||||
}
|
||||
input_data_consumed += 1
|
||||
if b[0] == 0 {
|
||||
break
|
||||
}
|
||||
name[i] = b[0]
|
||||
i += 1
|
||||
if i >= len(name) {
|
||||
return E_GZIP.Original_Name_Too_Long
|
||||
}
|
||||
}
|
||||
// printf("Original filename: %v\n", string(name[:i]));
|
||||
}
|
||||
|
||||
if .comment in header.flags {
|
||||
// Should be enough.
|
||||
comment: [1024]u8
|
||||
i := 0
|
||||
comment_error: io.Error
|
||||
|
||||
for i < len(comment) {
|
||||
b, comment_error = compress.read_slice(z, 1)
|
||||
if comment_error != .None {
|
||||
return E_General.Stream_Too_Short
|
||||
}
|
||||
input_data_consumed += 1
|
||||
if b[0] == 0 {
|
||||
break
|
||||
}
|
||||
comment[i] = b[0]
|
||||
i += 1
|
||||
if i >= len(comment) {
|
||||
return E_GZIP.Comment_Too_Long
|
||||
}
|
||||
}
|
||||
// printf("Comment: %v\n", string(comment[:i]));
|
||||
}
|
||||
|
||||
if .header_crc in header.flags {
|
||||
crc_error: io.Error
|
||||
_, crc_error = compress.read_slice(z, 2)
|
||||
input_data_consumed += 2
|
||||
if crc_error != .None {
|
||||
return E_General.Stream_Too_Short
|
||||
}
|
||||
/*
|
||||
We don't actually check the CRC16 (lower 2 bytes of CRC32 of header data until the CRC field).
|
||||
If we find a gzip file in the wild that sets this field, we can add proper support for it.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
We should have arrived at the ZLIB payload.
|
||||
*/
|
||||
payload_u32le: u32le
|
||||
|
||||
// fmt.printf("known_gzip_size: %v | expected_output_size: %v\n", known_gzip_size, expected_output_size);
|
||||
|
||||
if expected_output_size > -1 {
|
||||
/*
|
||||
We already checked that it's not larger than the output buffer max,
|
||||
or GZIP length field's max.
|
||||
|
||||
We'll just pass it on to `zlib.inflate_raw`;
|
||||
*/
|
||||
} else {
|
||||
/*
|
||||
If we know the size of the GZIP file *and* it is fully in memory,
|
||||
then we can peek at the unpacked size at the end.
|
||||
|
||||
We'll still want to ensure there's capacity left in the output buffer when we write, of course.
|
||||
|
||||
*/
|
||||
if known_gzip_size > -1 {
|
||||
offset := i64(known_gzip_size - input_data_consumed - 4)
|
||||
size, _ := compress.input_size(z)
|
||||
if size >= offset + 4 {
|
||||
length_bytes := z.input_data[offset:][:4]
|
||||
payload_u32le = (^u32le)(&length_bytes[0])^
|
||||
expected_output_size = int(payload_u32le)
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
TODO(Jeroen): When reading a GZIP from a stream, check if impl_seek is present.
|
||||
If so, we can seek to the end, grab the size from the footer, and seek back to payload start.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// fmt.printf("GZIP: Expected Payload Size: %v\n", expected_output_size);
|
||||
|
||||
zlib_error := zlib.inflate_raw(z=z, expected_output_size=expected_output_size)
|
||||
if zlib_error != nil {
|
||||
return zlib_error
|
||||
}
|
||||
/*
|
||||
Read CRC32 using the ctx bit reader because zlib may leave bytes in there.
|
||||
*/
|
||||
compress.discard_to_next_byte_lsb(z)
|
||||
|
||||
footer_error: io.Error
|
||||
|
||||
payload_crc_b: [4]u8
|
||||
for _, i in payload_crc_b {
|
||||
payload_crc_b[i], footer_error = compress.read_u8_prefer_code_buffer_lsb(z)
|
||||
}
|
||||
payload_crc := transmute(u32le)payload_crc_b
|
||||
|
||||
payload := bytes.buffer_to_bytes(buf)
|
||||
crc32 := u32le(hash.crc32(payload))
|
||||
if crc32 != payload_crc {
|
||||
return E_GZIP.Payload_CRC_Invalid
|
||||
}
|
||||
|
||||
payload_len_b: [4]u8
|
||||
for _, i in payload_len_b {
|
||||
payload_len_b[i], footer_error = compress.read_u8_prefer_code_buffer_lsb(z)
|
||||
}
|
||||
payload_len := transmute(u32le)payload_len_b
|
||||
|
||||
if len(payload) != int(payload_len) {
|
||||
return E_GZIP.Payload_Length_Invalid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//+ignore
|
||||
package zlib
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
|
||||
An example of how to use `zlib.inflate`.
|
||||
*/
|
||||
|
||||
import "core:bytes"
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
ODIN_DEMO := []u8{
|
||||
120, 218, 101, 144, 65, 110, 131, 48, 16, 69, 215, 246, 41, 190, 44, 69, 73, 32, 148, 182,
|
||||
75, 75, 28, 32, 251, 46, 217, 88, 238, 0, 86, 192, 32, 219, 36, 170, 170, 172, 122, 137,
|
||||
238, 122, 197, 30, 161, 70, 162, 20, 81, 203, 139, 25, 191, 255, 191, 60, 51, 40, 125, 81,
|
||||
53, 33, 144, 15, 156, 155, 110, 232, 93, 128, 208, 189, 35, 89, 117, 65, 112, 222, 41, 99,
|
||||
33, 37, 6, 215, 235, 195, 17, 239, 156, 197, 170, 118, 170, 131, 44, 32, 82, 164, 72, 240,
|
||||
253, 245, 249, 129, 12, 185, 224, 76, 105, 61, 118, 99, 171, 66, 239, 38, 193, 35, 103, 85,
|
||||
172, 66, 127, 33, 139, 24, 244, 235, 141, 49, 204, 223, 76, 208, 205, 204, 166, 7, 173, 60,
|
||||
97, 159, 238, 37, 214, 41, 105, 129, 167, 5, 102, 27, 152, 173, 97, 178, 129, 73, 129, 231,
|
||||
5, 230, 27, 152, 175, 225, 52, 192, 127, 243, 170, 157, 149, 18, 121, 142, 115, 109, 227, 122,
|
||||
64, 87, 114, 111, 161, 49, 182, 6, 181, 158, 162, 226, 206, 167, 27, 215, 246, 48, 56, 99,
|
||||
67, 117, 16, 47, 13, 45, 35, 151, 98, 231, 75, 1, 173, 90, 61, 101, 146, 71, 136, 244,
|
||||
170, 218, 145, 176, 123, 45, 173, 56, 113, 134, 191, 51, 219, 78, 235, 95, 28, 249, 253, 7,
|
||||
159, 150, 133, 125,
|
||||
}
|
||||
OUTPUT_SIZE :: 432
|
||||
|
||||
buf: bytes.Buffer
|
||||
|
||||
// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
|
||||
err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
|
||||
defer bytes.buffer_destroy(&buf)
|
||||
|
||||
if err != nil {
|
||||
fmt.printf("\nError: %v\n", err)
|
||||
}
|
||||
s := bytes.buffer_to_string(&buf)
|
||||
fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s)
|
||||
assert(len(s) == OUTPUT_SIZE)
|
||||
}
|
||||
@@ -0,0 +1,680 @@
|
||||
package zlib
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
*/
|
||||
|
||||
import "core:compress"
|
||||
|
||||
import "core:mem"
|
||||
import "core:io"
|
||||
import "core:hash"
|
||||
import "core:bytes"
|
||||
|
||||
/*
|
||||
zlib.inflate decompresses a ZLIB stream passed in as a []u8 or io.Stream.
|
||||
Returns: Error.
|
||||
*/
|
||||
|
||||
/*
|
||||
Do we do Adler32 as we write bytes to output?
|
||||
It used to be faster to do it inline, now it's faster to do it at the end of `inflate`.
|
||||
|
||||
We'll see what's faster after more optimization, and might end up removing
|
||||
`Context.rolling_hash` if not inlining it is still faster.
|
||||
|
||||
*/
|
||||
|
||||
Compression_Method :: enum u8 {
|
||||
DEFLATE = 8,
|
||||
Reserved = 15,
|
||||
}
|
||||
|
||||
Compression_Level :: enum u8 {
|
||||
Fastest = 0,
|
||||
Fast = 1,
|
||||
Default = 2,
|
||||
Maximum = 3,
|
||||
}
|
||||
|
||||
Options :: struct {
|
||||
window_size: u16,
|
||||
level: u8,
|
||||
}
|
||||
|
||||
Error :: compress.Error
|
||||
E_General :: compress.General_Error
|
||||
E_ZLIB :: compress.ZLIB_Error
|
||||
E_Deflate :: compress.Deflate_Error
|
||||
|
||||
DEFLATE_MAX_CHUNK_SIZE :: 65535
|
||||
DEFLATE_MAX_LITERAL_SIZE :: 65535
|
||||
DEFLATE_MAX_DISTANCE :: 32768
|
||||
DEFLATE_MAX_LENGTH :: 258
|
||||
|
||||
HUFFMAN_MAX_BITS :: 16
|
||||
HUFFMAN_FAST_BITS :: 9
|
||||
HUFFMAN_FAST_MASK :: ((1 << HUFFMAN_FAST_BITS) - 1)
|
||||
|
||||
Z_LENGTH_BASE := [31]u16{
|
||||
3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,
|
||||
67,83,99,115,131,163,195,227,258,0,0,
|
||||
}
|
||||
|
||||
Z_LENGTH_EXTRA := [31]u8{
|
||||
0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,
|
||||
}
|
||||
|
||||
Z_DIST_BASE := [32]u16{
|
||||
1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
|
||||
257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0,
|
||||
}
|
||||
|
||||
Z_DIST_EXTRA := [32]u8{
|
||||
0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0,
|
||||
}
|
||||
|
||||
Z_LENGTH_DEZIGZAG := []u8{
|
||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
|
||||
}
|
||||
|
||||
Z_FIXED_LENGTH := [288]u8{
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,
|
||||
}
|
||||
|
||||
Z_FIXED_DIST := [32]u8{
|
||||
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
}
|
||||
|
||||
/*
|
||||
Accelerate all cases in default tables.
|
||||
*/
|
||||
ZFAST_BITS :: 9
|
||||
ZFAST_MASK :: ((1 << ZFAST_BITS) - 1)
|
||||
|
||||
/*
|
||||
ZLIB-style Huffman encoding.
|
||||
JPEG packs from left, ZLIB from right. We can't share code.
|
||||
*/
|
||||
Huffman_Table :: struct {
|
||||
fast: [1 << ZFAST_BITS]u16,
|
||||
firstcode: [17]u16,
|
||||
maxcode: [17]int,
|
||||
firstsymbol: [17]u16,
|
||||
size: [288]u8,
|
||||
value: [288]u16,
|
||||
}
|
||||
|
||||
// Implementation starts here
|
||||
@(optimization_mode="speed")
|
||||
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
||||
assert(bits <= 16)
|
||||
// NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling
|
||||
// by reversing all of the bits and masking out the unneeded ones.
|
||||
r = n
|
||||
r = ((r & 0xAAAA) >> 1) | ((r & 0x5555) << 1)
|
||||
r = ((r & 0xCCCC) >> 2) | ((r & 0x3333) << 2)
|
||||
r = ((r & 0xF0F0) >> 4) | ((r & 0x0F0F) << 4)
|
||||
r = ((r & 0xFF00) >> 8) | ((r & 0x00FF) << 8)
|
||||
|
||||
r >>= (16 - bits)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
|
||||
/*
|
||||
That we get here at all means that we didn't pass an expected output size,
|
||||
or that it was too little.
|
||||
*/
|
||||
|
||||
/*
|
||||
Double until we reach the maximum allowed.
|
||||
*/
|
||||
new_size := min(len(buf) << 1, compress.COMPRESS_OUTPUT_ALLOCATE_MAX)
|
||||
resize(buf, new_size)
|
||||
if len(buf) != new_size {
|
||||
/*
|
||||
Resize failed.
|
||||
*/
|
||||
return .Resize_Failed
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Make these return compress.Error.
|
||||
*/
|
||||
|
||||
@(optimization_mode="speed")
|
||||
write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_check {
|
||||
/*
|
||||
Resize if needed.
|
||||
*/
|
||||
if int(z.bytes_written) + 1 >= len(z.output.buf) {
|
||||
e := grow_buffer(&z.output.buf)
|
||||
if e != nil {
|
||||
return .Short_Write
|
||||
}
|
||||
}
|
||||
|
||||
#no_bounds_check {
|
||||
z.output.buf[z.bytes_written] = c
|
||||
}
|
||||
z.bytes_written += 1
|
||||
return .None
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check {
|
||||
/*
|
||||
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
||||
without having to worry about wrapping, so no need for a temp allocation to give to
|
||||
the output stream, just give it _that_ slice.
|
||||
*/
|
||||
|
||||
/*
|
||||
Resize if needed.
|
||||
*/
|
||||
if int(z.bytes_written) + int(count) >= len(z.output.buf) {
|
||||
e := grow_buffer(&z.output.buf)
|
||||
if e != nil {
|
||||
return .Short_Write
|
||||
}
|
||||
}
|
||||
|
||||
#no_bounds_check {
|
||||
for _ in 0..<count {
|
||||
z.output.buf[z.bytes_written] = c
|
||||
z.bytes_written += 1
|
||||
}
|
||||
}
|
||||
|
||||
return .None
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
repl_bytes :: proc(z: ^$C, count: u16, distance: u16) -> (err: io.Error) {
|
||||
/*
|
||||
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
||||
without having to worry about wrapping, so no need for a temp allocation to give to
|
||||
the output stream, just give it _that_ slice.
|
||||
*/
|
||||
|
||||
offset := i64(distance)
|
||||
|
||||
if int(z.bytes_written) + int(count) >= len(z.output.buf) {
|
||||
e := grow_buffer(&z.output.buf)
|
||||
if e != nil {
|
||||
return .Short_Write
|
||||
}
|
||||
}
|
||||
|
||||
#no_bounds_check {
|
||||
for _ in 0..<count {
|
||||
c := z.output.buf[z.bytes_written - offset]
|
||||
z.output.buf[z.bytes_written] = c
|
||||
z.bytes_written += 1
|
||||
}
|
||||
}
|
||||
|
||||
return .None
|
||||
}
|
||||
|
||||
|
||||
allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_Table, err: Error) {
|
||||
return new(Huffman_Table, allocator), nil
|
||||
}
|
||||
|
||||
@(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+1]int
|
||||
|
||||
k := int(0)
|
||||
|
||||
mem.zero_slice(sizes[:])
|
||||
mem.zero_slice(z.fast[:])
|
||||
|
||||
for v in code_lengths {
|
||||
sizes[v] += 1
|
||||
}
|
||||
sizes[0] = 0
|
||||
|
||||
for i in 1 ..< HUFFMAN_MAX_BITS {
|
||||
if sizes[i] > (1 << uint(i)) {
|
||||
return E_Deflate.Huffman_Bad_Sizes
|
||||
}
|
||||
}
|
||||
code := int(0)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i))
|
||||
code <<= 1
|
||||
k += int(sizes[i])
|
||||
}
|
||||
|
||||
z.maxcode[HUFFMAN_MAX_BITS] = 0x10000 // Sentinel
|
||||
c: int
|
||||
|
||||
for v, ci in code_lengths {
|
||||
if v != 0 {
|
||||
c = next_code[v] - int(z.firstcode[v]) + int(z.firstsymbol[v])
|
||||
fastv := u16((u16(v) << 9) | u16(ci))
|
||||
z.size[c] = u8(v)
|
||||
z.value[c] = u16(ci)
|
||||
if v <= ZFAST_BITS {
|
||||
j := z_bit_reverse(u16(next_code[v]), v)
|
||||
for j < (1 << ZFAST_BITS) {
|
||||
z.fast[j] = fastv
|
||||
j += (1 << v)
|
||||
}
|
||||
}
|
||||
next_code[v] += 1
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||
code := u16(compress.peek_bits_lsb(z,16))
|
||||
|
||||
k := int(z_bit_reverse(code, 16))
|
||||
s: u8
|
||||
|
||||
#no_bounds_check for s = HUFFMAN_FAST_BITS+1; ; {
|
||||
if k < t.maxcode[s] {
|
||||
break
|
||||
}
|
||||
s += 1
|
||||
}
|
||||
if s >= 16 {
|
||||
return 0, E_Deflate.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
|
||||
}
|
||||
if t.size[b] != s {
|
||||
return 0, E_Deflate.Bad_Huffman_Code
|
||||
}
|
||||
|
||||
compress.consume_bits_lsb(z, s)
|
||||
|
||||
r = t.value[b]
|
||||
return r, nil
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
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
|
||||
}
|
||||
compress.refill_lsb(z)
|
||||
if z.num_bits > 63 {
|
||||
return 0, E_General.Stream_Too_Short
|
||||
}
|
||||
}
|
||||
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]
|
||||
if b != 0 {
|
||||
s := u8(b >> ZFAST_BITS)
|
||||
compress.consume_bits_lsb(z, s)
|
||||
return b & 511, nil
|
||||
}
|
||||
return decode_huffman_slowpath(z, t)
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
|
||||
#no_bounds_check for {
|
||||
value, e := decode_huffman(z, z_repeat)
|
||||
if e != nil {
|
||||
return err
|
||||
}
|
||||
if value < 256 {
|
||||
e := write_byte(z, u8(value))
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short
|
||||
}
|
||||
} else {
|
||||
if value == 256 {
|
||||
// End of block
|
||||
return nil
|
||||
}
|
||||
|
||||
value -= 257
|
||||
length := Z_LENGTH_BASE[value]
|
||||
if Z_LENGTH_EXTRA[value] > 0 {
|
||||
length += u16(compress.read_bits_lsb(z, Z_LENGTH_EXTRA[value]))
|
||||
}
|
||||
|
||||
value, e = decode_huffman(z, z_offset)
|
||||
if e != nil {
|
||||
return E_Deflate.Bad_Huffman_Code
|
||||
}
|
||||
|
||||
distance := Z_DIST_BASE[value]
|
||||
if Z_DIST_EXTRA[value] > 0 {
|
||||
distance += u16(compress.read_bits_lsb(z, Z_DIST_EXTRA[value]))
|
||||
}
|
||||
|
||||
if z.bytes_written < i64(distance) {
|
||||
// Distance is longer than we've decoded so far.
|
||||
return E_Deflate.Bad_Distance
|
||||
}
|
||||
|
||||
/*
|
||||
These might be sped up with a repl_byte call that copies
|
||||
from the already written output more directly, and that
|
||||
update the Adler checksum once after.
|
||||
|
||||
That way we'd suffer less Stream vtable overhead.
|
||||
*/
|
||||
if distance == 1 {
|
||||
/*
|
||||
Replicate the last outputted byte, length times.
|
||||
*/
|
||||
if length > 0 {
|
||||
c := z.output.buf[z.bytes_written - i64(distance)]
|
||||
e := repl_byte(z, length, c)
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if length > 0 {
|
||||
e := repl_bytes(z, length, distance)
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := false, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
||||
/*
|
||||
ctx.output must be a bytes.Buffer for now. We'll add a separate implementation that writes to a stream.
|
||||
|
||||
raw determines whether the ZLIB header is processed, or we're inflating a raw
|
||||
DEFLATE stream.
|
||||
*/
|
||||
|
||||
if !raw {
|
||||
size, size_err := compress.input_size(ctx)
|
||||
if size < 6 || size_err != nil {
|
||||
return E_General.Stream_Too_Short
|
||||
}
|
||||
|
||||
cmf, _ := compress.read_u8(ctx)
|
||||
|
||||
method := Compression_Method(cmf & 0xf)
|
||||
if method != .DEFLATE {
|
||||
return E_General.Unknown_Compression_Method
|
||||
}
|
||||
|
||||
if cinfo := (cmf >> 4) & 0xf; cinfo > 7 {
|
||||
return E_ZLIB.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
|
||||
}
|
||||
|
||||
/*
|
||||
We don't handle built-in dictionaries for now.
|
||||
They're application specific and PNG doesn't use them.
|
||||
*/
|
||||
if fdict := (flg >> 5) & 1; fdict != 0 {
|
||||
return E_ZLIB.FDICT_Unsupported
|
||||
}
|
||||
|
||||
// flevel := Compression_Level((flg >> 6) & 3);
|
||||
/*
|
||||
Inflate can consume bits belonging to the Adler checksum.
|
||||
We pass the entire stream to Inflate and will unget bytes if we need to
|
||||
at the end to compare checksums.
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
// Parse ZLIB stream without header.
|
||||
inflate_raw(z=ctx, expected_output_size=expected_output_size) or_return
|
||||
|
||||
if !raw {
|
||||
compress.discard_to_next_byte_lsb(ctx)
|
||||
|
||||
adler_b: [4]u8
|
||||
for _, i in adler_b {
|
||||
adler_b[i], _ = compress.read_u8_prefer_code_buffer_lsb(ctx)
|
||||
}
|
||||
adler := transmute(u32be)adler_b
|
||||
|
||||
output_hash := hash.adler32(ctx.output.buf[:])
|
||||
|
||||
if output_hash != u32(adler) {
|
||||
return E_General.Checksum_Failed
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Check alignment of reserve/resize.
|
||||
|
||||
@(optimization_mode="speed")
|
||||
inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
||||
context.allocator = allocator
|
||||
expected_output_size := expected_output_size
|
||||
|
||||
/*
|
||||
Always set up a minimum allocation size.
|
||||
*/
|
||||
expected_output_size = max(max(expected_output_size, compress.COMPRESS_OUTPUT_ALLOCATE_MIN), 512)
|
||||
|
||||
// fmt.printf("\nZLIB: Expected Payload Size: %v\n\n", expected_output_size);
|
||||
|
||||
if expected_output_size > 0 && expected_output_size <= compress.COMPRESS_OUTPUT_ALLOCATE_MAX {
|
||||
/*
|
||||
Try to pre-allocate the output buffer.
|
||||
*/
|
||||
reserve(&z.output.buf, expected_output_size)
|
||||
resize (&z.output.buf, expected_output_size)
|
||||
}
|
||||
|
||||
if len(z.output.buf) != expected_output_size {
|
||||
return .Resize_Failed
|
||||
}
|
||||
|
||||
z.num_bits = 0
|
||||
z.code_buffer = 0
|
||||
|
||||
z_repeat: ^Huffman_Table
|
||||
z_offset: ^Huffman_Table
|
||||
codelength_ht: ^Huffman_Table
|
||||
defer free(z_repeat)
|
||||
defer free(z_offset)
|
||||
defer free(codelength_ht)
|
||||
|
||||
z_repeat = allocate_huffman_table() or_return
|
||||
z_offset = allocate_huffman_table() or_return
|
||||
codelength_ht = allocate_huffman_table() or_return
|
||||
|
||||
final := u32(0)
|
||||
type := u32(0)
|
||||
|
||||
for {
|
||||
final = compress.read_bits_lsb(z, 1)
|
||||
type = compress.read_bits_lsb(z, 2)
|
||||
|
||||
// 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 := 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)
|
||||
|
||||
|
||||
if ~uncompressed_len != length_check {
|
||||
return E_Deflate.Len_Nlen_Mismatch
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Maybe speed this up with a stream-to-stream copy (read_from)
|
||||
and a single Adler32 update after.
|
||||
*/
|
||||
#no_bounds_check for uncompressed_len > 0 {
|
||||
compress.refill_lsb(z)
|
||||
lit := compress.read_bits_lsb(z, 8)
|
||||
write_byte(z, u8(lit))
|
||||
uncompressed_len -= 1
|
||||
}
|
||||
assert(uncompressed_len == 0)
|
||||
|
||||
case 3:
|
||||
return E_Deflate.BType_3
|
||||
case:
|
||||
// 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
|
||||
build_huffman(z_offset, Z_FIXED_DIST[:]) or_return
|
||||
} else {
|
||||
lencodes: [286+32+137]u8
|
||||
codelength_sizes: [19]u8
|
||||
|
||||
//i: u32;
|
||||
n: u32
|
||||
|
||||
compress.refill_lsb(z, 14)
|
||||
hlit := compress.read_bits_no_refill_lsb(z, 5) + 257
|
||||
hdist := compress.read_bits_no_refill_lsb(z, 5) + 1
|
||||
hclen := compress.read_bits_no_refill_lsb(z, 4) + 4
|
||||
ntot := hlit + hdist
|
||||
|
||||
#no_bounds_check for i in 0..<hclen {
|
||||
s := compress.read_bits_lsb(z, 3)
|
||||
codelength_sizes[Z_LENGTH_DEZIGZAG[i]] = u8(s)
|
||||
}
|
||||
build_huffman(codelength_ht, codelength_sizes[:]) or_return
|
||||
|
||||
n = 0
|
||||
c: u16
|
||||
|
||||
for n < ntot {
|
||||
c = decode_huffman(z, codelength_ht) or_return
|
||||
|
||||
if c < 0 || c >= 19 {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
||||
}
|
||||
if c < 16 {
|
||||
lencodes[n] = u8(c)
|
||||
n += 1
|
||||
} else {
|
||||
fill := u8(0)
|
||||
compress.refill_lsb(z, 7)
|
||||
switch c {
|
||||
case 16:
|
||||
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3)
|
||||
if n == 0 {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
||||
}
|
||||
fill = lencodes[n - 1]
|
||||
case 17:
|
||||
c = u16(compress.read_bits_no_refill_lsb(z, 3) + 3)
|
||||
case 18:
|
||||
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11)
|
||||
case:
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
||||
}
|
||||
|
||||
if ntot - n < u32(c) {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
||||
}
|
||||
|
||||
nc := n + u32(c)
|
||||
#no_bounds_check for ; n < nc; n += 1 {
|
||||
lencodes[n] = fill
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if n != ntot {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths
|
||||
}
|
||||
|
||||
build_huffman(z_repeat, lencodes[:hlit]) or_return
|
||||
build_huffman(z_offset, lencodes[hlit:ntot]) or_return
|
||||
}
|
||||
parse_huffman_block(z, z_repeat, z_offset) or_return
|
||||
}
|
||||
if final == 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if int(z.bytes_written) != len(z.output.buf) {
|
||||
resize(&z.output.buf, int(z.bytes_written))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) {
|
||||
ctx := compress.Context_Memory_Input{}
|
||||
|
||||
ctx.input_data = input
|
||||
ctx.output = buf
|
||||
|
||||
return inflate_from_context(ctx=&ctx, raw=raw, expected_output_size=expected_output_size)
|
||||
}
|
||||
|
||||
inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) {
|
||||
ctx := compress.Context_Memory_Input{}
|
||||
|
||||
ctx.input_data = input
|
||||
ctx.output = buf
|
||||
|
||||
return inflate_raw(z=&ctx, expected_output_size=expected_output_size)
|
||||
}
|
||||
|
||||
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,234 @@
|
||||
package dynamic_bit_array
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
/*
|
||||
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,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/*
|
||||
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 = Bit_Array{
|
||||
bias = min_index,
|
||||
max_index = max_index,
|
||||
}
|
||||
return res, resize_if_needed(&res, legs)
|
||||
}
|
||||
|
||||
/*
|
||||
Sets all bits to `false`.
|
||||
*/
|
||||
clear :: proc(ba: ^Bit_Array) {
|
||||
if ba == nil { return }
|
||||
ba.bits = {}
|
||||
}
|
||||
|
||||
/*
|
||||
Releases the memory used by the Bit Array.
|
||||
*/
|
||||
destroy :: proc(ba: ^Bit_Array) {
|
||||
if ba == nil { return }
|
||||
delete(ba.bits)
|
||||
}
|
||||
|
||||
/*
|
||||
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,52 @@
|
||||
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)
|
||||
}
|
||||
|
||||
-- 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,192 @@
|
||||
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
|
||||
return nil
|
||||
}
|
||||
|
||||
e := new(Node(Key, Value), c.node_allocator) or_return
|
||||
e.key = key
|
||||
e.value = value
|
||||
|
||||
_push_front_node(c, e)
|
||||
if c.count > c.capacity {
|
||||
_remove_node(c, c.tail)
|
||||
}
|
||||
|
||||
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)
|
||||
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
|
||||
|
||||
c.count -= 1
|
||||
|
||||
delete_key(&c.entries, node.key)
|
||||
|
||||
_call_on_remove(c, node)
|
||||
|
||||
free(node, c.node_allocator)
|
||||
|
||||
}
|
||||
|
||||
@(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
|
||||
|
||||
c.count += 1
|
||||
}
|
||||
|
||||
@(private)
|
||||
_pop_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
|
||||
if e == nil {
|
||||
return
|
||||
}
|
||||
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 "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(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(Value)) -> ^Map_Entry(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(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(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,113 +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) do break;
|
||||
s[i] = s[p];
|
||||
i = p;
|
||||
}
|
||||
|
||||
q.len += 1;
|
||||
if q.len > 0 do s[i] = item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
priority_queue_pop :: proc(q: ^$Q/Priority_Queue($T)) -> T {
|
||||
assert(q.len > 0);
|
||||
|
||||
s := array_slice(q.data);
|
||||
min := s[0];
|
||||
root := s[q.len-1];
|
||||
q.len -= 1;
|
||||
|
||||
i := 0;
|
||||
for i * 2 + 1 < q.len {
|
||||
a := i * 2 + 1;
|
||||
b := i * 2 + 2;
|
||||
c := b < q.len && q.priority(s[b]) < q.priority(s[a]) ? b : a;
|
||||
|
||||
if q.priority(s[c]) >= q.priority(root) do break;
|
||||
s[i] = s[c];
|
||||
i = c;
|
||||
}
|
||||
|
||||
if q.len > 0 do s[i] = root;
|
||||
return min;
|
||||
}
|
||||
|
||||
priority_queue_peek :: proc(q: ^$Q/Priority_Queue($T)) -> T {
|
||||
assert(q.len > 0);
|
||||
|
||||
s := array_slice(q.data);
|
||||
return s[0];
|
||||
}
|
||||
@@ -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,209 @@
|
||||
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]
|
||||
}
|
||||
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,73 +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 {
|
||||
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(&p); 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, loc := #caller_location) -> T {
|
||||
return a.data[index]
|
||||
}
|
||||
get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
|
||||
return &a.data[index]
|
||||
}
|
||||
|
||||
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
|
||||
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)) -> (T, 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
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
# crypto
|
||||
A crypto library for the Odin language
|
||||
|
||||
## Supported
|
||||
This library offers various algorithms implemented in Odin.
|
||||
Please see the chart below for the options.
|
||||
|
||||
## Hashing algorithms
|
||||
| Algorithm | |
|
||||
|:-------------------------------------------------------------------------------------------------------------|:-----------------|
|
||||
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | ✔️ |
|
||||
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
||||
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | ✔️ |
|
||||
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | ✔️ |
|
||||
| [Grøstl](http://www.groestl.info/Groestl.zip) | ✔️ |
|
||||
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | ✔️ |
|
||||
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | ✔️ |
|
||||
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | ✔️ |
|
||||
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | ✔️ |
|
||||
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | ✔️ |
|
||||
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | ✔️ |
|
||||
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | ✔️ |
|
||||
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | ✔️ |
|
||||
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | ✔️ |
|
||||
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | ✔️ |
|
||||
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | ✔️ |
|
||||
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ |
|
||||
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | ✔️ |
|
||||
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | ✔️ |
|
||||
|
||||
#### High level API
|
||||
Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_<size>`\*.
|
||||
Included in these groups are six procedures.
|
||||
* `hash_string` - Hash a given string and return the computed hash. Just calls `hash_bytes` internally
|
||||
* `hash_bytes` - Hash a given byte slice and return the computed hash
|
||||
* `hash_string_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. Just calls `hash_bytes_to_buffer` internally
|
||||
* `hash_bytes_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. The destination buffer has to be at least as big as the digest size of the hash
|
||||
* `hash_stream` - Takes a stream from io.Stream and returns the computed hash from it
|
||||
* `hash_file` - Takes a file handle and returns the computed hash from it. A second optional boolean parameter controls if the file is streamed (this is the default) or read at once (set to true)
|
||||
|
||||
\* On some algorithms there is another part to the name, since they might offer control about additional parameters.
|
||||
For instance, `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(...)`.
|
||||
|
||||
#### Low level API
|
||||
The above mentioned procedures internally call three procedures: `init`, `update` and `final`.
|
||||
You may also directly call them, if you wish.
|
||||
|
||||
#### Example
|
||||
```odin
|
||||
package crypto_example
|
||||
|
||||
// Import the desired package
|
||||
import "core:crypto/md4"
|
||||
|
||||
main :: proc() {
|
||||
input := "foo"
|
||||
|
||||
// Compute the hash, using the high level API
|
||||
computed_hash := md4.hash(input)
|
||||
|
||||
// Variant that takes a destination buffer, instead of returning the computed hash
|
||||
hash := make([]byte, md4.DIGEST_SIZE) // @note: Destination buffer has to be at least as big as the digest size of the hash
|
||||
md4.hash(input, hash[:])
|
||||
|
||||
// Compute the hash, using the low level API
|
||||
ctx: md4.Md4_Context
|
||||
computed_hash_low: [16]byte
|
||||
md4.init(&ctx)
|
||||
md4.update(&ctx, transmute([]byte)input)
|
||||
md4.final(&ctx, computed_hash_low[:])
|
||||
}
|
||||
```
|
||||
For example uses of all available algorithms, please see the tests within `tests/core/crypto`.
|
||||
|
||||
#### Thread safety
|
||||
The crypto package is not thread-safe at the moment. This may change in the future.
|
||||
|
||||
### Disclaimer
|
||||
The algorithms were ported out of curiosity and due to interest in the field.
|
||||
We have not had any of the code verified by a third party or tested/fuzzed by any automatic means.
|
||||
Whereever we were able to find official test vectors, those were used to verify the implementation.
|
||||
We do not recommend using them in a production environment, without any additional testing and/or verification.
|
||||
|
||||
### ToDo
|
||||
* Ciphers (Symmetric, Asymmetric)
|
||||
* MACs (Message Authentication Code)
|
||||
* CSPRNGs (Cryptographically Secure PseudoRandom Number Generator)
|
||||
* KDFs (Key Derivation Function)
|
||||
* KEAs (Key Exchange Algorithm)
|
||||
|
||||
### License
|
||||
This library is made available under the BSD-3 license.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package _sha3
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the Keccak hashing algorithm, standardized as SHA3 in <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf>
|
||||
To use the original Keccak padding, set the is_keccak bool to true, otherwise it will use SHA3 padding.
|
||||
*/
|
||||
|
||||
import "../util"
|
||||
|
||||
ROUNDS :: 24
|
||||
|
||||
Sha3_Context :: struct {
|
||||
st: struct #raw_union {
|
||||
b: [200]u8,
|
||||
q: [25]u64,
|
||||
},
|
||||
pt: int,
|
||||
rsiz: int,
|
||||
mdlen: int,
|
||||
is_keccak: bool,
|
||||
}
|
||||
|
||||
keccakf :: proc "contextless" (st: ^[25]u64) {
|
||||
keccakf_rndc := [?]u64 {
|
||||
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
||||
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
||||
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
|
||||
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
||||
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
|
||||
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
|
||||
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
||||
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
|
||||
}
|
||||
|
||||
keccakf_rotc := [?]i32 {
|
||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
||||
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
||||
}
|
||||
|
||||
keccakf_piln := [?]i32 {
|
||||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
||||
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
||||
}
|
||||
|
||||
i, j, r: i32 = ---, ---, ---
|
||||
t: u64 = ---
|
||||
bc: [5]u64 = ---
|
||||
|
||||
when ODIN_ENDIAN != .Little {
|
||||
v: uintptr = ---
|
||||
for i = 0; i < 25; i += 1 {
|
||||
v := uintptr(&st[i])
|
||||
st[i] = u64((^u8)(v + 0)^ << 0) | u64((^u8)(v + 1)^ << 8) |
|
||||
u64((^u8)(v + 2)^ << 16) | u64((^u8)(v + 3)^ << 24) |
|
||||
u64((^u8)(v + 4)^ << 32) | u64((^u8)(v + 5)^ << 40) |
|
||||
u64((^u8)(v + 6)^ << 48) | u64((^u8)(v + 7)^ << 56)
|
||||
}
|
||||
}
|
||||
|
||||
for r = 0; r < ROUNDS; r += 1 {
|
||||
// theta
|
||||
for i = 0; i < 5; i += 1 {
|
||||
bc[i] = st[i] ~ st[i + 5] ~ st[i + 10] ~ st[i + 15] ~ st[i + 20]
|
||||
}
|
||||
|
||||
for i = 0; i < 5; i += 1 {
|
||||
t = bc[(i + 4) % 5] ~ util.ROTL64(bc[(i + 1) % 5], 1)
|
||||
for j = 0; j < 25; j += 5 {
|
||||
st[j + i] ~= t
|
||||
}
|
||||
}
|
||||
|
||||
// rho pi
|
||||
t = st[1]
|
||||
for i = 0; i < 24; i += 1 {
|
||||
j = keccakf_piln[i]
|
||||
bc[0] = st[j]
|
||||
st[j] = util.ROTL64(t, u64(keccakf_rotc[i]))
|
||||
t = bc[0]
|
||||
}
|
||||
|
||||
// chi
|
||||
for j = 0; j < 25; j += 5 {
|
||||
for i = 0; i < 5; i += 1 {
|
||||
bc[i] = st[j + i]
|
||||
}
|
||||
for i = 0; i < 5; i += 1 {
|
||||
st[j + i] ~= ~bc[(i + 1) % 5] & bc[(i + 2) % 5]
|
||||
}
|
||||
}
|
||||
|
||||
st[0] ~= keccakf_rndc[r]
|
||||
}
|
||||
|
||||
when ODIN_ENDIAN != .Little {
|
||||
for i = 0; i < 25; i += 1 {
|
||||
v = uintptr(&st[i])
|
||||
t = st[i]
|
||||
(^u8)(v + 0)^ = (t >> 0) & 0xff
|
||||
(^u8)(v + 1)^ = (t >> 8) & 0xff
|
||||
(^u8)(v + 2)^ = (t >> 16) & 0xff
|
||||
(^u8)(v + 3)^ = (t >> 24) & 0xff
|
||||
(^u8)(v + 4)^ = (t >> 32) & 0xff
|
||||
(^u8)(v + 5)^ = (t >> 40) & 0xff
|
||||
(^u8)(v + 6)^ = (t >> 48) & 0xff
|
||||
(^u8)(v + 7)^ = (t >> 56) & 0xff
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
|
||||
j := c.pt
|
||||
for i := 0; i < len(data); i += 1 {
|
||||
c.st.b[j] ~= data[i]
|
||||
j += 1
|
||||
if j >= c.rsiz {
|
||||
keccakf(&c.st.q)
|
||||
j = 0
|
||||
}
|
||||
}
|
||||
c.pt = j
|
||||
}
|
||||
|
||||
final :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
if c.is_keccak {
|
||||
c.st.b[c.pt] ~= 0x01
|
||||
} else {
|
||||
c.st.b[c.pt] ~= 0x06
|
||||
}
|
||||
|
||||
c.st.b[c.rsiz - 1] ~= 0x80
|
||||
keccakf(&c.st.q)
|
||||
for i := 0; i < c.mdlen; i += 1 {
|
||||
hash[i] = c.st.b[i]
|
||||
}
|
||||
}
|
||||
|
||||
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 :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
|
||||
j := c.pt
|
||||
for i := 0; i < len(hash); i += 1 {
|
||||
if j >= c.rsiz {
|
||||
keccakf(&c.st.q)
|
||||
j = 0
|
||||
}
|
||||
hash[i] = c.st.b[j]
|
||||
j += 1
|
||||
}
|
||||
c.pt = j
|
||||
}
|
||||
@@ -0,0 +1,410 @@
|
||||
package _tiger
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the Tiger hashing algorithm, as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
|
||||
*/
|
||||
|
||||
import "../util"
|
||||
|
||||
T1 := [?]u64 {
|
||||
0x02aab17cf7e90c5e, 0xac424b03e243a8ec, 0x72cd5be30dd5fcd3, 0x6d019b93f6f97f3a,
|
||||
0xcd9978ffd21f9193, 0x7573a1c9708029e2, 0xb164326b922a83c3, 0x46883eee04915870,
|
||||
0xeaace3057103ece6, 0xc54169b808a3535c, 0x4ce754918ddec47c, 0x0aa2f4dfdc0df40c,
|
||||
0x10b76f18a74dbefa, 0xc6ccb6235ad1ab6a, 0x13726121572fe2ff, 0x1a488c6f199d921e,
|
||||
0x4bc9f9f4da0007ca, 0x26f5e6f6e85241c7, 0x859079dbea5947b6, 0x4f1885c5c99e8c92,
|
||||
0xd78e761ea96f864b, 0x8e36428c52b5c17d, 0x69cf6827373063c1, 0xb607c93d9bb4c56e,
|
||||
0x7d820e760e76b5ea, 0x645c9cc6f07fdc42, 0xbf38a078243342e0, 0x5f6b343c9d2e7d04,
|
||||
0xf2c28aeb600b0ec6, 0x6c0ed85f7254bcac, 0x71592281a4db4fe5, 0x1967fa69ce0fed9f,
|
||||
0xfd5293f8b96545db, 0xc879e9d7f2a7600b, 0x860248920193194e, 0xa4f9533b2d9cc0b3,
|
||||
0x9053836c15957613, 0xdb6dcf8afc357bf1, 0x18beea7a7a370f57, 0x037117ca50b99066,
|
||||
0x6ab30a9774424a35, 0xf4e92f02e325249b, 0x7739db07061ccae1, 0xd8f3b49ceca42a05,
|
||||
0xbd56be3f51382f73, 0x45faed5843b0bb28, 0x1c813d5c11bf1f83, 0x8af0e4b6d75fa169,
|
||||
0x33ee18a487ad9999, 0x3c26e8eab1c94410, 0xb510102bc0a822f9, 0x141eef310ce6123b,
|
||||
0xfc65b90059ddb154, 0xe0158640c5e0e607, 0x884e079826c3a3cf, 0x930d0d9523c535fd,
|
||||
0x35638d754e9a2b00, 0x4085fccf40469dd5, 0xc4b17ad28be23a4c, 0xcab2f0fc6a3e6a2e,
|
||||
0x2860971a6b943fcd, 0x3dde6ee212e30446, 0x6222f32ae01765ae, 0x5d550bb5478308fe,
|
||||
0xa9efa98da0eda22a, 0xc351a71686c40da7, 0x1105586d9c867c84, 0xdcffee85fda22853,
|
||||
0xccfbd0262c5eef76, 0xbaf294cb8990d201, 0xe69464f52afad975, 0x94b013afdf133e14,
|
||||
0x06a7d1a32823c958, 0x6f95fe5130f61119, 0xd92ab34e462c06c0, 0xed7bde33887c71d2,
|
||||
0x79746d6e6518393e, 0x5ba419385d713329, 0x7c1ba6b948a97564, 0x31987c197bfdac67,
|
||||
0xde6c23c44b053d02, 0x581c49fed002d64d, 0xdd474d6338261571, 0xaa4546c3e473d062,
|
||||
0x928fce349455f860, 0x48161bbacaab94d9, 0x63912430770e6f68, 0x6ec8a5e602c6641c,
|
||||
0x87282515337ddd2b, 0x2cda6b42034b701b, 0xb03d37c181cb096d, 0xe108438266c71c6f,
|
||||
0x2b3180c7eb51b255, 0xdf92b82f96c08bbc, 0x5c68c8c0a632f3ba, 0x5504cc861c3d0556,
|
||||
0xabbfa4e55fb26b8f, 0x41848b0ab3baceb4, 0xb334a273aa445d32, 0xbca696f0a85ad881,
|
||||
0x24f6ec65b528d56c, 0x0ce1512e90f4524a, 0x4e9dd79d5506d35a, 0x258905fac6ce9779,
|
||||
0x2019295b3e109b33, 0xf8a9478b73a054cc, 0x2924f2f934417eb0, 0x3993357d536d1bc4,
|
||||
0x38a81ac21db6ff8b, 0x47c4fbf17d6016bf, 0x1e0faadd7667e3f5, 0x7abcff62938beb96,
|
||||
0xa78dad948fc179c9, 0x8f1f98b72911e50d, 0x61e48eae27121a91, 0x4d62f7ad31859808,
|
||||
0xeceba345ef5ceaeb, 0xf5ceb25ebc9684ce, 0xf633e20cb7f76221, 0xa32cdf06ab8293e4,
|
||||
0x985a202ca5ee2ca4, 0xcf0b8447cc8a8fb1, 0x9f765244979859a3, 0xa8d516b1a1240017,
|
||||
0x0bd7ba3ebb5dc726, 0xe54bca55b86adb39, 0x1d7a3afd6c478063, 0x519ec608e7669edd,
|
||||
0x0e5715a2d149aa23, 0x177d4571848ff194, 0xeeb55f3241014c22, 0x0f5e5ca13a6e2ec2,
|
||||
0x8029927b75f5c361, 0xad139fabc3d6e436, 0x0d5df1a94ccf402f, 0x3e8bd948bea5dfc8,
|
||||
0xa5a0d357bd3ff77e, 0xa2d12e251f74f645, 0x66fd9e525e81a082, 0x2e0c90ce7f687a49,
|
||||
0xc2e8bcbeba973bc5, 0x000001bce509745f, 0x423777bbe6dab3d6, 0xd1661c7eaef06eb5,
|
||||
0xa1781f354daacfd8, 0x2d11284a2b16affc, 0xf1fc4f67fa891d1f, 0x73ecc25dcb920ada,
|
||||
0xae610c22c2a12651, 0x96e0a810d356b78a, 0x5a9a381f2fe7870f, 0xd5ad62ede94e5530,
|
||||
0xd225e5e8368d1427, 0x65977b70c7af4631, 0x99f889b2de39d74f, 0x233f30bf54e1d143,
|
||||
0x9a9675d3d9a63c97, 0x5470554ff334f9a8, 0x166acb744a4f5688, 0x70c74caab2e4aead,
|
||||
0xf0d091646f294d12, 0x57b82a89684031d1, 0xefd95a5a61be0b6b, 0x2fbd12e969f2f29a,
|
||||
0x9bd37013feff9fe8, 0x3f9b0404d6085a06, 0x4940c1f3166cfe15, 0x09542c4dcdf3defb,
|
||||
0xb4c5218385cd5ce3, 0xc935b7dc4462a641, 0x3417f8a68ed3b63f, 0xb80959295b215b40,
|
||||
0xf99cdaef3b8c8572, 0x018c0614f8fcb95d, 0x1b14accd1a3acdf3, 0x84d471f200bb732d,
|
||||
0xc1a3110e95e8da16, 0x430a7220bf1a82b8, 0xb77e090d39df210e, 0x5ef4bd9f3cd05e9d,
|
||||
0x9d4ff6da7e57a444, 0xda1d60e183d4a5f8, 0xb287c38417998e47, 0xfe3edc121bb31886,
|
||||
0xc7fe3ccc980ccbef, 0xe46fb590189bfd03, 0x3732fd469a4c57dc, 0x7ef700a07cf1ad65,
|
||||
0x59c64468a31d8859, 0x762fb0b4d45b61f6, 0x155baed099047718, 0x68755e4c3d50baa6,
|
||||
0xe9214e7f22d8b4df, 0x2addbf532eac95f4, 0x32ae3909b4bd0109, 0x834df537b08e3450,
|
||||
0xfa209da84220728d, 0x9e691d9b9efe23f7, 0x0446d288c4ae8d7f, 0x7b4cc524e169785b,
|
||||
0x21d87f0135ca1385, 0xcebb400f137b8aa5, 0x272e2b66580796be, 0x3612264125c2b0de,
|
||||
0x057702bdad1efbb2, 0xd4babb8eacf84be9, 0x91583139641bc67b, 0x8bdc2de08036e024,
|
||||
0x603c8156f49f68ed, 0xf7d236f7dbef5111, 0x9727c4598ad21e80, 0xa08a0896670a5fd7,
|
||||
0xcb4a8f4309eba9cb, 0x81af564b0f7036a1, 0xc0b99aa778199abd, 0x959f1ec83fc8e952,
|
||||
0x8c505077794a81b9, 0x3acaaf8f056338f0, 0x07b43f50627a6778, 0x4a44ab49f5eccc77,
|
||||
0x3bc3d6e4b679ee98, 0x9cc0d4d1cf14108c, 0x4406c00b206bc8a0, 0x82a18854c8d72d89,
|
||||
0x67e366b35c3c432c, 0xb923dd61102b37f2, 0x56ab2779d884271d, 0xbe83e1b0ff1525af,
|
||||
0xfb7c65d4217e49a9, 0x6bdbe0e76d48e7d4, 0x08df828745d9179e, 0x22ea6a9add53bd34,
|
||||
0xe36e141c5622200a, 0x7f805d1b8cb750ee, 0xafe5c7a59f58e837, 0xe27f996a4fb1c23c,
|
||||
0xd3867dfb0775f0d0, 0xd0e673de6e88891a, 0x123aeb9eafb86c25, 0x30f1d5d5c145b895,
|
||||
0xbb434a2dee7269e7, 0x78cb67ecf931fa38, 0xf33b0372323bbf9c, 0x52d66336fb279c74,
|
||||
0x505f33ac0afb4eaa, 0xe8a5cd99a2cce187, 0x534974801e2d30bb, 0x8d2d5711d5876d90,
|
||||
0x1f1a412891bc038e, 0xd6e2e71d82e56648, 0x74036c3a497732b7, 0x89b67ed96361f5ab,
|
||||
0xffed95d8f1ea02a2, 0xe72b3bd61464d43d, 0xa6300f170bdc4820, 0xebc18760ed78a77a,
|
||||
}
|
||||
|
||||
T2 := [?]u64 {
|
||||
0xe6a6be5a05a12138, 0xb5a122a5b4f87c98, 0x563c6089140b6990, 0x4c46cb2e391f5dd5,
|
||||
0xd932addbc9b79434, 0x08ea70e42015aff5, 0xd765a6673e478cf1, 0xc4fb757eab278d99,
|
||||
0xdf11c6862d6e0692, 0xddeb84f10d7f3b16, 0x6f2ef604a665ea04, 0x4a8e0f0ff0e0dfb3,
|
||||
0xa5edeef83dbcba51, 0xfc4f0a2a0ea4371e, 0xe83e1da85cb38429, 0xdc8ff882ba1b1ce2,
|
||||
0xcd45505e8353e80d, 0x18d19a00d4db0717, 0x34a0cfeda5f38101, 0x0be77e518887caf2,
|
||||
0x1e341438b3c45136, 0xe05797f49089ccf9, 0xffd23f9df2591d14, 0x543dda228595c5cd,
|
||||
0x661f81fd99052a33, 0x8736e641db0f7b76, 0x15227725418e5307, 0xe25f7f46162eb2fa,
|
||||
0x48a8b2126c13d9fe, 0xafdc541792e76eea, 0x03d912bfc6d1898f, 0x31b1aafa1b83f51b,
|
||||
0xf1ac2796e42ab7d9, 0x40a3a7d7fcd2ebac, 0x1056136d0afbbcc5, 0x7889e1dd9a6d0c85,
|
||||
0xd33525782a7974aa, 0xa7e25d09078ac09b, 0xbd4138b3eac6edd0, 0x920abfbe71eb9e70,
|
||||
0xa2a5d0f54fc2625c, 0xc054e36b0b1290a3, 0xf6dd59ff62fe932b, 0x3537354511a8ac7d,
|
||||
0xca845e9172fadcd4, 0x84f82b60329d20dc, 0x79c62ce1cd672f18, 0x8b09a2add124642c,
|
||||
0xd0c1e96a19d9e726, 0x5a786a9b4ba9500c, 0x0e020336634c43f3, 0xc17b474aeb66d822,
|
||||
0x6a731ae3ec9baac2, 0x8226667ae0840258, 0x67d4567691caeca5, 0x1d94155c4875adb5,
|
||||
0x6d00fd985b813fdf, 0x51286efcb774cd06, 0x5e8834471fa744af, 0xf72ca0aee761ae2e,
|
||||
0xbe40e4cdaee8e09a, 0xe9970bbb5118f665, 0x726e4beb33df1964, 0x703b000729199762,
|
||||
0x4631d816f5ef30a7, 0xb880b5b51504a6be, 0x641793c37ed84b6c, 0x7b21ed77f6e97d96,
|
||||
0x776306312ef96b73, 0xae528948e86ff3f4, 0x53dbd7f286a3f8f8, 0x16cadce74cfc1063,
|
||||
0x005c19bdfa52c6dd, 0x68868f5d64d46ad3, 0x3a9d512ccf1e186a, 0x367e62c2385660ae,
|
||||
0xe359e7ea77dcb1d7, 0x526c0773749abe6e, 0x735ae5f9d09f734b, 0x493fc7cc8a558ba8,
|
||||
0xb0b9c1533041ab45, 0x321958ba470a59bd, 0x852db00b5f46c393, 0x91209b2bd336b0e5,
|
||||
0x6e604f7d659ef19f, 0xb99a8ae2782ccb24, 0xccf52ab6c814c4c7, 0x4727d9afbe11727b,
|
||||
0x7e950d0c0121b34d, 0x756f435670ad471f, 0xf5add442615a6849, 0x4e87e09980b9957a,
|
||||
0x2acfa1df50aee355, 0xd898263afd2fd556, 0xc8f4924dd80c8fd6, 0xcf99ca3d754a173a,
|
||||
0xfe477bacaf91bf3c, 0xed5371f6d690c12d, 0x831a5c285e687094, 0xc5d3c90a3708a0a4,
|
||||
0x0f7f903717d06580, 0x19f9bb13b8fdf27f, 0xb1bd6f1b4d502843, 0x1c761ba38fff4012,
|
||||
0x0d1530c4e2e21f3b, 0x8943ce69a7372c8a, 0xe5184e11feb5ce66, 0x618bdb80bd736621,
|
||||
0x7d29bad68b574d0b, 0x81bb613e25e6fe5b, 0x071c9c10bc07913f, 0xc7beeb7909ac2d97,
|
||||
0xc3e58d353bc5d757, 0xeb017892f38f61e8, 0xd4effb9c9b1cc21a, 0x99727d26f494f7ab,
|
||||
0xa3e063a2956b3e03, 0x9d4a8b9a4aa09c30, 0x3f6ab7d500090fb4, 0x9cc0f2a057268ac0,
|
||||
0x3dee9d2dedbf42d1, 0x330f49c87960a972, 0xc6b2720287421b41, 0x0ac59ec07c00369c,
|
||||
0xef4eac49cb353425, 0xf450244eef0129d8, 0x8acc46e5caf4deb6, 0x2ffeab63989263f7,
|
||||
0x8f7cb9fe5d7a4578, 0x5bd8f7644e634635, 0x427a7315bf2dc900, 0x17d0c4aa2125261c,
|
||||
0x3992486c93518e50, 0xb4cbfee0a2d7d4c3, 0x7c75d6202c5ddd8d, 0xdbc295d8e35b6c61,
|
||||
0x60b369d302032b19, 0xce42685fdce44132, 0x06f3ddb9ddf65610, 0x8ea4d21db5e148f0,
|
||||
0x20b0fce62fcd496f, 0x2c1b912358b0ee31, 0xb28317b818f5a308, 0xa89c1e189ca6d2cf,
|
||||
0x0c6b18576aaadbc8, 0xb65deaa91299fae3, 0xfb2b794b7f1027e7, 0x04e4317f443b5beb,
|
||||
0x4b852d325939d0a6, 0xd5ae6beefb207ffc, 0x309682b281c7d374, 0xbae309a194c3b475,
|
||||
0x8cc3f97b13b49f05, 0x98a9422ff8293967, 0x244b16b01076ff7c, 0xf8bf571c663d67ee,
|
||||
0x1f0d6758eee30da1, 0xc9b611d97adeb9b7, 0xb7afd5887b6c57a2, 0x6290ae846b984fe1,
|
||||
0x94df4cdeacc1a5fd, 0x058a5bd1c5483aff, 0x63166cc142ba3c37, 0x8db8526eb2f76f40,
|
||||
0xe10880036f0d6d4e, 0x9e0523c9971d311d, 0x45ec2824cc7cd691, 0x575b8359e62382c9,
|
||||
0xfa9e400dc4889995, 0xd1823ecb45721568, 0xdafd983b8206082f, 0xaa7d29082386a8cb,
|
||||
0x269fcd4403b87588, 0x1b91f5f728bdd1e0, 0xe4669f39040201f6, 0x7a1d7c218cf04ade,
|
||||
0x65623c29d79ce5ce, 0x2368449096c00bb1, 0xab9bf1879da503ba, 0xbc23ecb1a458058e,
|
||||
0x9a58df01bb401ecc, 0xa070e868a85f143d, 0x4ff188307df2239e, 0x14d565b41a641183,
|
||||
0xee13337452701602, 0x950e3dcf3f285e09, 0x59930254b9c80953, 0x3bf299408930da6d,
|
||||
0xa955943f53691387, 0xa15edecaa9cb8784, 0x29142127352be9a0, 0x76f0371fff4e7afb,
|
||||
0x0239f450274f2228, 0xbb073af01d5e868b, 0xbfc80571c10e96c1, 0xd267088568222e23,
|
||||
0x9671a3d48e80b5b0, 0x55b5d38ae193bb81, 0x693ae2d0a18b04b8, 0x5c48b4ecadd5335f,
|
||||
0xfd743b194916a1ca, 0x2577018134be98c4, 0xe77987e83c54a4ad, 0x28e11014da33e1b9,
|
||||
0x270cc59e226aa213, 0x71495f756d1a5f60, 0x9be853fb60afef77, 0xadc786a7f7443dbf,
|
||||
0x0904456173b29a82, 0x58bc7a66c232bd5e, 0xf306558c673ac8b2, 0x41f639c6b6c9772a,
|
||||
0x216defe99fda35da, 0x11640cc71c7be615, 0x93c43694565c5527, 0xea038e6246777839,
|
||||
0xf9abf3ce5a3e2469, 0x741e768d0fd312d2, 0x0144b883ced652c6, 0xc20b5a5ba33f8552,
|
||||
0x1ae69633c3435a9d, 0x97a28ca4088cfdec, 0x8824a43c1e96f420, 0x37612fa66eeea746,
|
||||
0x6b4cb165f9cf0e5a, 0x43aa1c06a0abfb4a, 0x7f4dc26ff162796b, 0x6cbacc8e54ed9b0f,
|
||||
0xa6b7ffefd2bb253e, 0x2e25bc95b0a29d4f, 0x86d6a58bdef1388c, 0xded74ac576b6f054,
|
||||
0x8030bdbc2b45805d, 0x3c81af70e94d9289, 0x3eff6dda9e3100db, 0xb38dc39fdfcc8847,
|
||||
0x123885528d17b87e, 0xf2da0ed240b1b642, 0x44cefadcd54bf9a9, 0x1312200e433c7ee6,
|
||||
0x9ffcc84f3a78c748, 0xf0cd1f72248576bb, 0xec6974053638cfe4, 0x2ba7b67c0cec4e4c,
|
||||
0xac2f4df3e5ce32ed, 0xcb33d14326ea4c11, 0xa4e9044cc77e58bc, 0x5f513293d934fcef,
|
||||
0x5dc9645506e55444, 0x50de418f317de40a, 0x388cb31a69dde259, 0x2db4a83455820a86,
|
||||
0x9010a91e84711ae9, 0x4df7f0b7b1498371, 0xd62a2eabc0977179, 0x22fac097aa8d5c0e,
|
||||
}
|
||||
|
||||
T3 := [?]u64 {
|
||||
0xf49fcc2ff1daf39b, 0x487fd5c66ff29281, 0xe8a30667fcdca83f, 0x2c9b4be3d2fcce63,
|
||||
0xda3ff74b93fbbbc2, 0x2fa165d2fe70ba66, 0xa103e279970e93d4, 0xbecdec77b0e45e71,
|
||||
0xcfb41e723985e497, 0xb70aaa025ef75017, 0xd42309f03840b8e0, 0x8efc1ad035898579,
|
||||
0x96c6920be2b2abc5, 0x66af4163375a9172, 0x2174abdcca7127fb, 0xb33ccea64a72ff41,
|
||||
0xf04a4933083066a5, 0x8d970acdd7289af5, 0x8f96e8e031c8c25e, 0xf3fec02276875d47,
|
||||
0xec7bf310056190dd, 0xf5adb0aebb0f1491, 0x9b50f8850fd58892, 0x4975488358b74de8,
|
||||
0xa3354ff691531c61, 0x0702bbe481d2c6ee, 0x89fb24057deded98, 0xac3075138596e902,
|
||||
0x1d2d3580172772ed, 0xeb738fc28e6bc30d, 0x5854ef8f63044326, 0x9e5c52325add3bbe,
|
||||
0x90aa53cf325c4623, 0xc1d24d51349dd067, 0x2051cfeea69ea624, 0x13220f0a862e7e4f,
|
||||
0xce39399404e04864, 0xd9c42ca47086fcb7, 0x685ad2238a03e7cc, 0x066484b2ab2ff1db,
|
||||
0xfe9d5d70efbf79ec, 0x5b13b9dd9c481854, 0x15f0d475ed1509ad, 0x0bebcd060ec79851,
|
||||
0xd58c6791183ab7f8, 0xd1187c5052f3eee4, 0xc95d1192e54e82ff, 0x86eea14cb9ac6ca2,
|
||||
0x3485beb153677d5d, 0xdd191d781f8c492a, 0xf60866baa784ebf9, 0x518f643ba2d08c74,
|
||||
0x8852e956e1087c22, 0xa768cb8dc410ae8d, 0x38047726bfec8e1a, 0xa67738b4cd3b45aa,
|
||||
0xad16691cec0dde19, 0xc6d4319380462e07, 0xc5a5876d0ba61938, 0x16b9fa1fa58fd840,
|
||||
0x188ab1173ca74f18, 0xabda2f98c99c021f, 0x3e0580ab134ae816, 0x5f3b05b773645abb,
|
||||
0x2501a2be5575f2f6, 0x1b2f74004e7e8ba9, 0x1cd7580371e8d953, 0x7f6ed89562764e30,
|
||||
0xb15926ff596f003d, 0x9f65293da8c5d6b9, 0x6ecef04dd690f84c, 0x4782275fff33af88,
|
||||
0xe41433083f820801, 0xfd0dfe409a1af9b5, 0x4325a3342cdb396b, 0x8ae77e62b301b252,
|
||||
0xc36f9e9f6655615a, 0x85455a2d92d32c09, 0xf2c7dea949477485, 0x63cfb4c133a39eba,
|
||||
0x83b040cc6ebc5462, 0x3b9454c8fdb326b0, 0x56f56a9e87ffd78c, 0x2dc2940d99f42bc6,
|
||||
0x98f7df096b096e2d, 0x19a6e01e3ad852bf, 0x42a99ccbdbd4b40b, 0xa59998af45e9c559,
|
||||
0x366295e807d93186, 0x6b48181bfaa1f773, 0x1fec57e2157a0a1d, 0x4667446af6201ad5,
|
||||
0xe615ebcacfb0f075, 0xb8f31f4f68290778, 0x22713ed6ce22d11e, 0x3057c1a72ec3c93b,
|
||||
0xcb46acc37c3f1f2f, 0xdbb893fd02aaf50e, 0x331fd92e600b9fcf, 0xa498f96148ea3ad6,
|
||||
0xa8d8426e8b6a83ea, 0xa089b274b7735cdc, 0x87f6b3731e524a11, 0x118808e5cbc96749,
|
||||
0x9906e4c7b19bd394, 0xafed7f7e9b24a20c, 0x6509eadeeb3644a7, 0x6c1ef1d3e8ef0ede,
|
||||
0xb9c97d43e9798fb4, 0xa2f2d784740c28a3, 0x7b8496476197566f, 0x7a5be3e6b65f069d,
|
||||
0xf96330ed78be6f10, 0xeee60de77a076a15, 0x2b4bee4aa08b9bd0, 0x6a56a63ec7b8894e,
|
||||
0x02121359ba34fef4, 0x4cbf99f8283703fc, 0x398071350caf30c8, 0xd0a77a89f017687a,
|
||||
0xf1c1a9eb9e423569, 0x8c7976282dee8199, 0x5d1737a5dd1f7abd, 0x4f53433c09a9fa80,
|
||||
0xfa8b0c53df7ca1d9, 0x3fd9dcbc886ccb77, 0xc040917ca91b4720, 0x7dd00142f9d1dcdf,
|
||||
0x8476fc1d4f387b58, 0x23f8e7c5f3316503, 0x032a2244e7e37339, 0x5c87a5d750f5a74b,
|
||||
0x082b4cc43698992e, 0xdf917becb858f63c, 0x3270b8fc5bf86dda, 0x10ae72bb29b5dd76,
|
||||
0x576ac94e7700362b, 0x1ad112dac61efb8f, 0x691bc30ec5faa427, 0xff246311cc327143,
|
||||
0x3142368e30e53206, 0x71380e31e02ca396, 0x958d5c960aad76f1, 0xf8d6f430c16da536,
|
||||
0xc8ffd13f1be7e1d2, 0x7578ae66004ddbe1, 0x05833f01067be646, 0xbb34b5ad3bfe586d,
|
||||
0x095f34c9a12b97f0, 0x247ab64525d60ca8, 0xdcdbc6f3017477d1, 0x4a2e14d4decad24d,
|
||||
0xbdb5e6d9be0a1eeb, 0x2a7e70f7794301ab, 0xdef42d8a270540fd, 0x01078ec0a34c22c1,
|
||||
0xe5de511af4c16387, 0x7ebb3a52bd9a330a, 0x77697857aa7d6435, 0x004e831603ae4c32,
|
||||
0xe7a21020ad78e312, 0x9d41a70c6ab420f2, 0x28e06c18ea1141e6, 0xd2b28cbd984f6b28,
|
||||
0x26b75f6c446e9d83, 0xba47568c4d418d7f, 0xd80badbfe6183d8e, 0x0e206d7f5f166044,
|
||||
0xe258a43911cbca3e, 0x723a1746b21dc0bc, 0xc7caa854f5d7cdd3, 0x7cac32883d261d9c,
|
||||
0x7690c26423ba942c, 0x17e55524478042b8, 0xe0be477656a2389f, 0x4d289b5e67ab2da0,
|
||||
0x44862b9c8fbbfd31, 0xb47cc8049d141365, 0x822c1b362b91c793, 0x4eb14655fb13dfd8,
|
||||
0x1ecbba0714e2a97b, 0x6143459d5cde5f14, 0x53a8fbf1d5f0ac89, 0x97ea04d81c5e5b00,
|
||||
0x622181a8d4fdb3f3, 0xe9bcd341572a1208, 0x1411258643cce58a, 0x9144c5fea4c6e0a4,
|
||||
0x0d33d06565cf620f, 0x54a48d489f219ca1, 0xc43e5eac6d63c821, 0xa9728b3a72770daf,
|
||||
0xd7934e7b20df87ef, 0xe35503b61a3e86e5, 0xcae321fbc819d504, 0x129a50b3ac60bfa6,
|
||||
0xcd5e68ea7e9fb6c3, 0xb01c90199483b1c7, 0x3de93cd5c295376c, 0xaed52edf2ab9ad13,
|
||||
0x2e60f512c0a07884, 0xbc3d86a3e36210c9, 0x35269d9b163951ce, 0x0c7d6e2ad0cdb5fa,
|
||||
0x59e86297d87f5733, 0x298ef221898db0e7, 0x55000029d1a5aa7e, 0x8bc08ae1b5061b45,
|
||||
0xc2c31c2b6c92703a, 0x94cc596baf25ef42, 0x0a1d73db22540456, 0x04b6a0f9d9c4179a,
|
||||
0xeffdafa2ae3d3c60, 0xf7c8075bb49496c4, 0x9cc5c7141d1cd4e3, 0x78bd1638218e5534,
|
||||
0xb2f11568f850246a, 0xedfabcfa9502bc29, 0x796ce5f2da23051b, 0xaae128b0dc93537c,
|
||||
0x3a493da0ee4b29ae, 0xb5df6b2c416895d7, 0xfcabbd25122d7f37, 0x70810b58105dc4b1,
|
||||
0xe10fdd37f7882a90, 0x524dcab5518a3f5c, 0x3c9e85878451255b, 0x4029828119bd34e2,
|
||||
0x74a05b6f5d3ceccb, 0xb610021542e13eca, 0x0ff979d12f59e2ac, 0x6037da27e4f9cc50,
|
||||
0x5e92975a0df1847d, 0xd66de190d3e623fe, 0x5032d6b87b568048, 0x9a36b7ce8235216e,
|
||||
0x80272a7a24f64b4a, 0x93efed8b8c6916f7, 0x37ddbff44cce1555, 0x4b95db5d4b99bd25,
|
||||
0x92d3fda169812fc0, 0xfb1a4a9a90660bb6, 0x730c196946a4b9b2, 0x81e289aa7f49da68,
|
||||
0x64669a0f83b1a05f, 0x27b3ff7d9644f48b, 0xcc6b615c8db675b3, 0x674f20b9bcebbe95,
|
||||
0x6f31238275655982, 0x5ae488713e45cf05, 0xbf619f9954c21157, 0xeabac46040a8eae9,
|
||||
0x454c6fe9f2c0c1cd, 0x419cf6496412691c, 0xd3dc3bef265b0f70, 0x6d0e60f5c3578a9e,
|
||||
}
|
||||
|
||||
T4 := [?]u64 {
|
||||
0x5b0e608526323c55, 0x1a46c1a9fa1b59f5, 0xa9e245a17c4c8ffa, 0x65ca5159db2955d7,
|
||||
0x05db0a76ce35afc2, 0x81eac77ea9113d45, 0x528ef88ab6ac0a0d, 0xa09ea253597be3ff,
|
||||
0x430ddfb3ac48cd56, 0xc4b3a67af45ce46f, 0x4ececfd8fbe2d05e, 0x3ef56f10b39935f0,
|
||||
0x0b22d6829cd619c6, 0x17fd460a74df2069, 0x6cf8cc8e8510ed40, 0xd6c824bf3a6ecaa7,
|
||||
0x61243d581a817049, 0x048bacb6bbc163a2, 0xd9a38ac27d44cc32, 0x7fddff5baaf410ab,
|
||||
0xad6d495aa804824b, 0xe1a6a74f2d8c9f94, 0xd4f7851235dee8e3, 0xfd4b7f886540d893,
|
||||
0x247c20042aa4bfda, 0x096ea1c517d1327c, 0xd56966b4361a6685, 0x277da5c31221057d,
|
||||
0x94d59893a43acff7, 0x64f0c51ccdc02281, 0x3d33bcc4ff6189db, 0xe005cb184ce66af1,
|
||||
0xff5ccd1d1db99bea, 0xb0b854a7fe42980f, 0x7bd46a6a718d4b9f, 0xd10fa8cc22a5fd8c,
|
||||
0xd31484952be4bd31, 0xc7fa975fcb243847, 0x4886ed1e5846c407, 0x28cddb791eb70b04,
|
||||
0xc2b00be2f573417f, 0x5c9590452180f877, 0x7a6bddfff370eb00, 0xce509e38d6d9d6a4,
|
||||
0xebeb0f00647fa702, 0x1dcc06cf76606f06, 0xe4d9f28ba286ff0a, 0xd85a305dc918c262,
|
||||
0x475b1d8732225f54, 0x2d4fb51668ccb5fe, 0xa679b9d9d72bba20, 0x53841c0d912d43a5,
|
||||
0x3b7eaa48bf12a4e8, 0x781e0e47f22f1ddf, 0xeff20ce60ab50973, 0x20d261d19dffb742,
|
||||
0x16a12b03062a2e39, 0x1960eb2239650495, 0x251c16fed50eb8b8, 0x9ac0c330f826016e,
|
||||
0xed152665953e7671, 0x02d63194a6369570, 0x5074f08394b1c987, 0x70ba598c90b25ce1,
|
||||
0x794a15810b9742f6, 0x0d5925e9fcaf8c6c, 0x3067716cd868744e, 0x910ab077e8d7731b,
|
||||
0x6a61bbdb5ac42f61, 0x93513efbf0851567, 0xf494724b9e83e9d5, 0xe887e1985c09648d,
|
||||
0x34b1d3c675370cfd, 0xdc35e433bc0d255d, 0xd0aab84234131be0, 0x08042a50b48b7eaf,
|
||||
0x9997c4ee44a3ab35, 0x829a7b49201799d0, 0x263b8307b7c54441, 0x752f95f4fd6a6ca6,
|
||||
0x927217402c08c6e5, 0x2a8ab754a795d9ee, 0xa442f7552f72943d, 0x2c31334e19781208,
|
||||
0x4fa98d7ceaee6291, 0x55c3862f665db309, 0xbd0610175d53b1f3, 0x46fe6cb840413f27,
|
||||
0x3fe03792df0cfa59, 0xcfe700372eb85e8f, 0xa7be29e7adbce118, 0xe544ee5cde8431dd,
|
||||
0x8a781b1b41f1873e, 0xa5c94c78a0d2f0e7, 0x39412e2877b60728, 0xa1265ef3afc9a62c,
|
||||
0xbcc2770c6a2506c5, 0x3ab66dd5dce1ce12, 0xe65499d04a675b37, 0x7d8f523481bfd216,
|
||||
0x0f6f64fcec15f389, 0x74efbe618b5b13c8, 0xacdc82b714273e1d, 0xdd40bfe003199d17,
|
||||
0x37e99257e7e061f8, 0xfa52626904775aaa, 0x8bbbf63a463d56f9, 0xf0013f1543a26e64,
|
||||
0xa8307e9f879ec898, 0xcc4c27a4150177cc, 0x1b432f2cca1d3348, 0xde1d1f8f9f6fa013,
|
||||
0x606602a047a7ddd6, 0xd237ab64cc1cb2c7, 0x9b938e7225fcd1d3, 0xec4e03708e0ff476,
|
||||
0xfeb2fbda3d03c12d, 0xae0bced2ee43889a, 0x22cb8923ebfb4f43, 0x69360d013cf7396d,
|
||||
0x855e3602d2d4e022, 0x073805bad01f784c, 0x33e17a133852f546, 0xdf4874058ac7b638,
|
||||
0xba92b29c678aa14a, 0x0ce89fc76cfaadcd, 0x5f9d4e0908339e34, 0xf1afe9291f5923b9,
|
||||
0x6e3480f60f4a265f, 0xeebf3a2ab29b841c, 0xe21938a88f91b4ad, 0x57dfeff845c6d3c3,
|
||||
0x2f006b0bf62caaf2, 0x62f479ef6f75ee78, 0x11a55ad41c8916a9, 0xf229d29084fed453,
|
||||
0x42f1c27b16b000e6, 0x2b1f76749823c074, 0x4b76eca3c2745360, 0x8c98f463b91691bd,
|
||||
0x14bcc93cf1ade66a, 0x8885213e6d458397, 0x8e177df0274d4711, 0xb49b73b5503f2951,
|
||||
0x10168168c3f96b6b, 0x0e3d963b63cab0ae, 0x8dfc4b5655a1db14, 0xf789f1356e14de5c,
|
||||
0x683e68af4e51dac1, 0xc9a84f9d8d4b0fd9, 0x3691e03f52a0f9d1, 0x5ed86e46e1878e80,
|
||||
0x3c711a0e99d07150, 0x5a0865b20c4e9310, 0x56fbfc1fe4f0682e, 0xea8d5de3105edf9b,
|
||||
0x71abfdb12379187a, 0x2eb99de1bee77b9c, 0x21ecc0ea33cf4523, 0x59a4d7521805c7a1,
|
||||
0x3896f5eb56ae7c72, 0xaa638f3db18f75dc, 0x9f39358dabe9808e, 0xb7defa91c00b72ac,
|
||||
0x6b5541fd62492d92, 0x6dc6dee8f92e4d5b, 0x353f57abc4beea7e, 0x735769d6da5690ce,
|
||||
0x0a234aa642391484, 0xf6f9508028f80d9d, 0xb8e319a27ab3f215, 0x31ad9c1151341a4d,
|
||||
0x773c22a57bef5805, 0x45c7561a07968633, 0xf913da9e249dbe36, 0xda652d9b78a64c68,
|
||||
0x4c27a97f3bc334ef, 0x76621220e66b17f4, 0x967743899acd7d0b, 0xf3ee5bcae0ed6782,
|
||||
0x409f753600c879fc, 0x06d09a39b5926db6, 0x6f83aeb0317ac588, 0x01e6ca4a86381f21,
|
||||
0x66ff3462d19f3025, 0x72207c24ddfd3bfb, 0x4af6b6d3e2ece2eb, 0x9c994dbec7ea08de,
|
||||
0x49ace597b09a8bc4, 0xb38c4766cf0797ba, 0x131b9373c57c2a75, 0xb1822cce61931e58,
|
||||
0x9d7555b909ba1c0c, 0x127fafdd937d11d2, 0x29da3badc66d92e4, 0xa2c1d57154c2ecbc,
|
||||
0x58c5134d82f6fe24, 0x1c3ae3515b62274f, 0xe907c82e01cb8126, 0xf8ed091913e37fcb,
|
||||
0x3249d8f9c80046c9, 0x80cf9bede388fb63, 0x1881539a116cf19e, 0x5103f3f76bd52457,
|
||||
0x15b7e6f5ae47f7a8, 0xdbd7c6ded47e9ccf, 0x44e55c410228bb1a, 0xb647d4255edb4e99,
|
||||
0x5d11882bb8aafc30, 0xf5098bbb29d3212a, 0x8fb5ea14e90296b3, 0x677b942157dd025a,
|
||||
0xfb58e7c0a390acb5, 0x89d3674c83bd4a01, 0x9e2da4df4bf3b93b, 0xfcc41e328cab4829,
|
||||
0x03f38c96ba582c52, 0xcad1bdbd7fd85db2, 0xbbb442c16082ae83, 0xb95fe86ba5da9ab0,
|
||||
0xb22e04673771a93f, 0x845358c9493152d8, 0xbe2a488697b4541e, 0x95a2dc2dd38e6966,
|
||||
0xc02c11ac923c852b, 0x2388b1990df2a87b, 0x7c8008fa1b4f37be, 0x1f70d0c84d54e503,
|
||||
0x5490adec7ece57d4, 0x002b3c27d9063a3a, 0x7eaea3848030a2bf, 0xc602326ded2003c0,
|
||||
0x83a7287d69a94086, 0xc57a5fcb30f57a8a, 0xb56844e479ebe779, 0xa373b40f05dcbce9,
|
||||
0xd71a786e88570ee2, 0x879cbacdbde8f6a0, 0x976ad1bcc164a32f, 0xab21e25e9666d78b,
|
||||
0x901063aae5e5c33c, 0x9818b34448698d90, 0xe36487ae3e1e8abb, 0xafbdf931893bdcb4,
|
||||
0x6345a0dc5fbbd519, 0x8628fe269b9465ca, 0x1e5d01603f9c51ec, 0x4de44006a15049b7,
|
||||
0xbf6c70e5f776cbb1, 0x411218f2ef552bed, 0xcb0c0708705a36a3, 0xe74d14754f986044,
|
||||
0xcd56d9430ea8280e, 0xc12591d7535f5065, 0xc83223f1720aef96, 0xc3a0396f7363a51f,
|
||||
}
|
||||
|
||||
Tiger_Context :: struct {
|
||||
a: u64,
|
||||
b: u64,
|
||||
c: u64,
|
||||
x: [64]byte,
|
||||
nx: int,
|
||||
length: u64,
|
||||
ver: int,
|
||||
}
|
||||
|
||||
round :: #force_inline proc "contextless" (a, b, c, x, mul: u64) -> (u64, u64, u64) {
|
||||
a, b, c := a, b, c
|
||||
c ~= x
|
||||
a -= T1[c & 0xff] ~ T2[(c >> 16) & 0xff] ~ T3[(c >> 32) & 0xff] ~ T4[(c >> 48) & 0xff]
|
||||
b += T4[(c >> 8) & 0xff] ~ T3[(c >> 24) & 0xff] ~ T2[(c >> 40) & 0xff] ~ T1[(c >> 56) & 0xff]
|
||||
b *= mul
|
||||
return a, b, c
|
||||
}
|
||||
|
||||
pass :: #force_inline proc "contextless" (a, b, c: u64, d: []u64, mul: u64) -> (x, y, z: u64) {
|
||||
x, y, z = round(a, b, c, d[0], mul)
|
||||
y, z, x = round(y, z, x, d[1], mul)
|
||||
z, x, y = round(z, x, y, d[2], mul)
|
||||
x, y, z = round(x, y, z, d[3], mul)
|
||||
y, z, x = round(y, z, x, d[4], mul)
|
||||
z, x, y = round(z, x, y, d[5], mul)
|
||||
x, y, z = round(x, y, z, d[6], mul)
|
||||
y, z, x = round(y, z, x, d[7], mul)
|
||||
return
|
||||
}
|
||||
|
||||
key_schedule :: #force_inline proc "contextless" (x: []u64) {
|
||||
x[0] -= x[7] ~ 0xa5a5a5a5a5a5a5a5
|
||||
x[1] ~= x[0]
|
||||
x[2] += x[1]
|
||||
x[3] -= x[2] ~ ((~x[1]) << 19)
|
||||
x[4] ~= x[3]
|
||||
x[5] += x[4]
|
||||
x[6] -= x[5] ~ ((~x[4]) >> 23)
|
||||
x[7] ~= x[6]
|
||||
x[0] += x[7]
|
||||
x[1] -= x[0] ~ ((~x[7]) << 19)
|
||||
x[2] ~= x[1]
|
||||
x[3] += x[2]
|
||||
x[4] -= x[3] ~ ((~x[2]) >> 23)
|
||||
x[5] ~= x[4]
|
||||
x[6] += x[5]
|
||||
x[7] -= x[6] ~ 0x0123456789abcdef
|
||||
}
|
||||
|
||||
compress :: #force_inline proc "contextless" (ctx: ^Tiger_Context, data: []byte) {
|
||||
a := ctx.a
|
||||
b := ctx.b
|
||||
c := ctx.c
|
||||
x := util.cast_slice([]u64, data)
|
||||
ctx.a, ctx.b, ctx.c = pass(ctx.a, ctx.b, ctx.c, x, 5)
|
||||
key_schedule(x)
|
||||
ctx.c, ctx.a, ctx.b = pass(ctx.c, ctx.a, ctx.b, x, 7)
|
||||
key_schedule(x)
|
||||
ctx.b, ctx.c, ctx.a = pass(ctx.b, ctx.c, ctx.a, x, 9)
|
||||
ctx.a ~= a
|
||||
ctx.b -= b
|
||||
ctx.c += c
|
||||
}
|
||||
|
||||
init :: proc "contextless" (ctx: ^Tiger_Context) {
|
||||
ctx.a = 0x0123456789abcdef
|
||||
ctx.b = 0xfedcba9876543210
|
||||
ctx.c = 0xf096a5b4c3b2e187
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^Tiger_Context, input: []byte) {
|
||||
p := make([]byte, len(input))
|
||||
copy(p, input)
|
||||
|
||||
length := len(p)
|
||||
ctx.length += u64(length)
|
||||
if ctx.nx > 0 {
|
||||
n := len(p)
|
||||
if n > 64 - ctx.nx {
|
||||
n = 64 - ctx.nx
|
||||
}
|
||||
copy(ctx.x[ctx.nx:ctx.nx + n], p[:n])
|
||||
ctx.nx += n
|
||||
if ctx.nx == 64 {
|
||||
compress(ctx, ctx.x[:64 - 1])
|
||||
ctx.nx = 0
|
||||
}
|
||||
p = p[n:]
|
||||
}
|
||||
for len(p) >= 64 {
|
||||
compress(ctx, p[:64])
|
||||
p = p[64:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], p)
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Tiger_Context, hash: []byte) {
|
||||
length := ctx.length
|
||||
tmp: [64]byte
|
||||
if ctx.ver == 1 {
|
||||
tmp[0] = 0x01
|
||||
} else {
|
||||
tmp[0] = 0x80
|
||||
}
|
||||
|
||||
size := length & 0x3f
|
||||
if size < 56 {
|
||||
update(ctx, tmp[:56 - size])
|
||||
} else {
|
||||
update(ctx, tmp[:64 + 56 - size])
|
||||
}
|
||||
|
||||
length <<= 3
|
||||
for i := uint(0); i < 8; i += 1 {
|
||||
tmp[i] = byte(length >> (8 * i))
|
||||
}
|
||||
update(ctx, tmp[:8])
|
||||
|
||||
for i := uint(0); i < 8; i += 1 {
|
||||
tmp[i] = byte(ctx.a >> (8 * i))
|
||||
tmp[i + 8] = byte(ctx.b >> (8 * i))
|
||||
tmp[i + 16] = byte(ctx.c >> (8 * i))
|
||||
}
|
||||
copy(hash[:], tmp[:len(hash)])
|
||||
}
|
||||
@@ -0,0 +1,726 @@
|
||||
package blake
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the BLAKE hashing algorithm, as defined in <https://web.archive.org/web/20190915215948/https://131002.net/blake>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_224 :: 28
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_384 :: 48
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc "contextless" (data: string) -> [DIGEST_SIZE_224]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_224]byte {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = true
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = 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) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_224(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_224(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_224]byte{}, false
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
hash_bytes_to_buffer_224,
|
||||
hash_string_to_buffer_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc "contextless" (data: string) -> [DIGEST_SIZE_256]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Blake256_Context
|
||||
ctx.is224 = false
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = 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) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_256(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_256(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_256]byte{}, false
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
hash_bytes_to_buffer_256,
|
||||
hash_string_to_buffer_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc "contextless" (data: string) -> [DIGEST_SIZE_384]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_384]byte {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = true
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = true
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = 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) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_384(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_384(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_384]byte{}, false
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
hash_bytes_to_buffer_384,
|
||||
hash_string_to_buffer_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc "contextless" (data: string) -> [DIGEST_SIZE_512]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = false
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Blake512_Context
|
||||
ctx.is384 = false
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = 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) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_512(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_512(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_512]byte{}, false
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc "contextless" (ctx: ^$T) {
|
||||
when T == Blake256_Context {
|
||||
if ctx.is224 {
|
||||
ctx.h[0] = 0xc1059ed8
|
||||
ctx.h[1] = 0x367cd507
|
||||
ctx.h[2] = 0x3070dd17
|
||||
ctx.h[3] = 0xf70e5939
|
||||
ctx.h[4] = 0xffc00b31
|
||||
ctx.h[5] = 0x68581511
|
||||
ctx.h[6] = 0x64f98fa7
|
||||
ctx.h[7] = 0xbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667
|
||||
ctx.h[1] = 0xbb67ae85
|
||||
ctx.h[2] = 0x3c6ef372
|
||||
ctx.h[3] = 0xa54ff53a
|
||||
ctx.h[4] = 0x510e527f
|
||||
ctx.h[5] = 0x9b05688c
|
||||
ctx.h[6] = 0x1f83d9ab
|
||||
ctx.h[7] = 0x5be0cd19
|
||||
}
|
||||
} else when T == Blake512_Context {
|
||||
if ctx.is384 {
|
||||
ctx.h[0] = 0xcbbb9d5dc1059ed8
|
||||
ctx.h[1] = 0x629a292a367cd507
|
||||
ctx.h[2] = 0x9159015a3070dd17
|
||||
ctx.h[3] = 0x152fecd8f70e5939
|
||||
ctx.h[4] = 0x67332667ffc00b31
|
||||
ctx.h[5] = 0x8eb44a8768581511
|
||||
ctx.h[6] = 0xdb0c2e0d64f98fa7
|
||||
ctx.h[7] = 0x47b5481dbefa4fa4
|
||||
} else {
|
||||
ctx.h[0] = 0x6a09e667f3bcc908
|
||||
ctx.h[1] = 0xbb67ae8584caa73b
|
||||
ctx.h[2] = 0x3c6ef372fe94f82b
|
||||
ctx.h[3] = 0xa54ff53a5f1d36f1
|
||||
ctx.h[4] = 0x510e527fade682d1
|
||||
ctx.h[5] = 0x9b05688c2b3e6c1f
|
||||
ctx.h[6] = 0x1f83d9abfb41bd6b
|
||||
ctx.h[7] = 0x5be0cd19137e2179
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update :: proc "contextless" (ctx: ^$T, data: []byte) {
|
||||
data := data
|
||||
when T == Blake256_Context {
|
||||
if ctx.nx > 0 {
|
||||
n := copy(ctx.x[ctx.nx:], data)
|
||||
ctx.nx += n
|
||||
if ctx.nx == BLOCKSIZE_256 {
|
||||
block256(ctx, ctx.x[:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) >= BLOCKSIZE_256 {
|
||||
n := len(data) &~ (BLOCKSIZE_256 - 1)
|
||||
block256(ctx, data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], data)
|
||||
}
|
||||
} else when T == Blake512_Context {
|
||||
if ctx.nx > 0 {
|
||||
n := copy(ctx.x[ctx.nx:], data)
|
||||
ctx.nx += n
|
||||
if ctx.nx == BLOCKSIZE_512 {
|
||||
block512(ctx, ctx.x[:])
|
||||
ctx.nx = 0
|
||||
}
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) >= BLOCKSIZE_512 {
|
||||
n := len(data) &~ (BLOCKSIZE_512 - 1)
|
||||
block512(ctx, data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
if len(data) > 0 {
|
||||
ctx.nx = copy(ctx.x[:], data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc "contextless" (ctx: ^$T, hash: []byte) {
|
||||
when T == Blake256_Context {
|
||||
tmp: [65]byte
|
||||
} else when T == Blake512_Context {
|
||||
tmp: [129]byte
|
||||
}
|
||||
nx := u64(ctx.nx)
|
||||
tmp[0] = 0x80
|
||||
length := (ctx.t + nx) << 3
|
||||
|
||||
when T == Blake256_Context {
|
||||
if nx == 55 {
|
||||
if ctx.is224 {
|
||||
write_additional(ctx, {0x80})
|
||||
} else {
|
||||
write_additional(ctx, {0x81})
|
||||
}
|
||||
} else {
|
||||
if nx < 55 {
|
||||
if nx == 0 {
|
||||
ctx.nullt = true
|
||||
}
|
||||
write_additional(ctx, tmp[0 : 55 - nx])
|
||||
} else {
|
||||
write_additional(ctx, tmp[0 : 64 - nx])
|
||||
write_additional(ctx, tmp[1:56])
|
||||
ctx.nullt = true
|
||||
}
|
||||
if ctx.is224 {
|
||||
write_additional(ctx, {0x00})
|
||||
} else {
|
||||
write_additional(ctx, {0x01})
|
||||
}
|
||||
}
|
||||
|
||||
for i : uint = 0; i < 8; i += 1 {
|
||||
tmp[i] = byte(length >> (56 - 8 * i))
|
||||
}
|
||||
write_additional(ctx, tmp[0:8])
|
||||
|
||||
h := ctx.h[:]
|
||||
if ctx.is224 {
|
||||
h = h[0:7]
|
||||
}
|
||||
for s, i in h {
|
||||
hash[i * 4] = byte(s >> 24)
|
||||
hash[i * 4 + 1] = byte(s >> 16)
|
||||
hash[i * 4 + 2] = byte(s >> 8)
|
||||
hash[i * 4 + 3] = byte(s)
|
||||
}
|
||||
} else when T == Blake512_Context {
|
||||
if nx == 111 {
|
||||
if ctx.is384 {
|
||||
write_additional(ctx, {0x80})
|
||||
} else {
|
||||
write_additional(ctx, {0x81})
|
||||
}
|
||||
} else {
|
||||
if nx < 111 {
|
||||
if nx == 0 {
|
||||
ctx.nullt = true
|
||||
}
|
||||
write_additional(ctx, tmp[0 : 111 - nx])
|
||||
} else {
|
||||
write_additional(ctx, tmp[0 : 128 - nx])
|
||||
write_additional(ctx, tmp[1:112])
|
||||
ctx.nullt = true
|
||||
}
|
||||
if ctx.is384 {
|
||||
write_additional(ctx, {0x00})
|
||||
} else {
|
||||
write_additional(ctx, {0x01})
|
||||
}
|
||||
}
|
||||
|
||||
for i : uint = 0; i < 16; i += 1 {
|
||||
tmp[i] = byte(length >> (120 - 8 * i))
|
||||
}
|
||||
write_additional(ctx, tmp[0:16])
|
||||
|
||||
h := ctx.h[:]
|
||||
if ctx.is384 {
|
||||
h = h[0:6]
|
||||
}
|
||||
for s, i in h {
|
||||
hash[i * 8] = byte(s >> 56)
|
||||
hash[i * 8 + 1] = byte(s >> 48)
|
||||
hash[i * 8 + 2] = byte(s >> 40)
|
||||
hash[i * 8 + 3] = byte(s >> 32)
|
||||
hash[i * 8 + 4] = byte(s >> 24)
|
||||
hash[i * 8 + 5] = byte(s >> 16)
|
||||
hash[i * 8 + 6] = byte(s >> 8)
|
||||
hash[i * 8 + 7] = byte(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SIZE_224 :: 28
|
||||
SIZE_256 :: 32
|
||||
SIZE_384 :: 48
|
||||
SIZE_512 :: 64
|
||||
BLOCKSIZE_256 :: 64
|
||||
BLOCKSIZE_512 :: 128
|
||||
|
||||
Blake256_Context :: struct {
|
||||
h: [8]u32,
|
||||
s: [4]u32,
|
||||
t: u64,
|
||||
x: [64]byte,
|
||||
nx: int,
|
||||
is224: bool,
|
||||
nullt: bool,
|
||||
}
|
||||
|
||||
Blake512_Context :: struct {
|
||||
h: [8]u64,
|
||||
s: [4]u64,
|
||||
t: u64,
|
||||
x: [128]byte,
|
||||
nx: int,
|
||||
is384: bool,
|
||||
nullt: bool,
|
||||
}
|
||||
|
||||
SIGMA := [?]int {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,
|
||||
11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,
|
||||
7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,
|
||||
9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,
|
||||
2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,
|
||||
12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,
|
||||
13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,
|
||||
6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,
|
||||
10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,
|
||||
}
|
||||
|
||||
U256 := [16]u32 {
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
||||
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
|
||||
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
|
||||
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
|
||||
}
|
||||
|
||||
U512 := [16]u64 {
|
||||
0x243f6a8885a308d3, 0x13198a2e03707344, 0xa4093822299f31d0, 0x082efa98ec4e6c89,
|
||||
0x452821e638d01377, 0xbe5466cf34e90c6c, 0xc0ac29b7c97c50dd, 0x3f84d5b5b5470917,
|
||||
0x9216d5d98979fb1b, 0xd1310ba698dfb5ac, 0x2ffd72dbd01adfb7, 0xb8e1afed6a267e96,
|
||||
0xba7c9045f12c7f99, 0x24a19947b3916cf7, 0x0801f2e2858efc16, 0x636920d871574e69,
|
||||
}
|
||||
|
||||
G256 :: #force_inline proc "contextless" (a, b, c, d: u32, m: [16]u32, i, j: int) -> (u32, u32, u32, u32) {
|
||||
a, b, c, d := a, b, c, d
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j)]] ~ U256[SIGMA[(i % 10) * 16 + (2 * j + 1)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (32 - 16) | d >> 16
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (32 - 12) | b >> 12
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j + 1)]] ~ U256[SIGMA[(i % 10) * 16 + (2 * j)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (32 - 8) | d >> 8
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (32 - 7) | b >> 7
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
G512 :: #force_inline proc "contextless" (a, b, c, d: u64, m: [16]u64, i, j: int) -> (u64, u64, u64, u64) {
|
||||
a, b, c, d := a, b, c, d
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j)]] ~ U512[SIGMA[(i % 10) * 16 + (2 * j + 1)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (64 - 32) | d >> 32
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (64 - 25) | b >> 25
|
||||
a += m[SIGMA[(i % 10) * 16 + (2 * j + 1)]] ~ U512[SIGMA[(i % 10) * 16 + (2 * j)]]
|
||||
a += b
|
||||
d ~= a
|
||||
d = d << (64 - 16) | d >> 16
|
||||
c += d
|
||||
b ~= c
|
||||
b = b << (64 - 11) | b >> 11
|
||||
return a, b, c, d
|
||||
}
|
||||
|
||||
block256 :: proc "contextless" (ctx: ^Blake256_Context, p: []byte) #no_bounds_check {
|
||||
i, j: int = ---, ---
|
||||
v, m: [16]u32 = ---, ---
|
||||
p := p
|
||||
for len(p) >= BLOCKSIZE_256 {
|
||||
v[0] = ctx.h[0]
|
||||
v[1] = ctx.h[1]
|
||||
v[2] = ctx.h[2]
|
||||
v[3] = ctx.h[3]
|
||||
v[4] = ctx.h[4]
|
||||
v[5] = ctx.h[5]
|
||||
v[6] = ctx.h[6]
|
||||
v[7] = ctx.h[7]
|
||||
v[8] = ctx.s[0] ~ U256[0]
|
||||
v[9] = ctx.s[1] ~ U256[1]
|
||||
v[10] = ctx.s[2] ~ U256[2]
|
||||
v[11] = ctx.s[3] ~ U256[3]
|
||||
v[12] = U256[4]
|
||||
v[13] = U256[5]
|
||||
v[14] = U256[6]
|
||||
v[15] = U256[7]
|
||||
|
||||
ctx.t += 512
|
||||
if !ctx.nullt {
|
||||
v[12] ~= u32(ctx.t)
|
||||
v[13] ~= u32(ctx.t)
|
||||
v[14] ~= u32(ctx.t >> 32)
|
||||
v[15] ~= u32(ctx.t >> 32)
|
||||
}
|
||||
|
||||
for i, j = 0, 0; i < 16; i, j = i+1, j+4 {
|
||||
m[i] = u32(p[j]) << 24 | u32(p[j + 1]) << 16 | u32(p[j + 2]) << 8 | u32(p[j + 3])
|
||||
}
|
||||
|
||||
for i = 0; i < 14; i += 1 {
|
||||
v[0], v[4], v[8], v[12] = G256(v[0], v[4], v[8], v[12], m, i, 0)
|
||||
v[1], v[5], v[9], v[13] = G256(v[1], v[5], v[9], v[13], m, i, 1)
|
||||
v[2], v[6], v[10], v[14] = G256(v[2], v[6], v[10], v[14], m, i, 2)
|
||||
v[3], v[7], v[11], v[15] = G256(v[3], v[7], v[11], v[15], m, i, 3)
|
||||
v[0], v[5], v[10], v[15] = G256(v[0], v[5], v[10], v[15], m, i, 4)
|
||||
v[1], v[6], v[11], v[12] = G256(v[1], v[6], v[11], v[12], m, i, 5)
|
||||
v[2], v[7], v[8], v[13] = G256(v[2], v[7], v[8], v[13], m, i, 6)
|
||||
v[3], v[4], v[9], v[14] = G256(v[3], v[4], v[9], v[14], m, i, 7)
|
||||
}
|
||||
|
||||
for i = 0; i < 8; i += 1 {
|
||||
ctx.h[i] ~= ctx.s[i % 4] ~ v[i] ~ v[i + 8]
|
||||
}
|
||||
p = p[BLOCKSIZE_256:]
|
||||
}
|
||||
}
|
||||
|
||||
block512 :: proc "contextless" (ctx: ^Blake512_Context, p: []byte) #no_bounds_check {
|
||||
i, j: int = ---, ---
|
||||
v, m: [16]u64 = ---, ---
|
||||
p := p
|
||||
for len(p) >= BLOCKSIZE_512 {
|
||||
v[0] = ctx.h[0]
|
||||
v[1] = ctx.h[1]
|
||||
v[2] = ctx.h[2]
|
||||
v[3] = ctx.h[3]
|
||||
v[4] = ctx.h[4]
|
||||
v[5] = ctx.h[5]
|
||||
v[6] = ctx.h[6]
|
||||
v[7] = ctx.h[7]
|
||||
v[8] = ctx.s[0] ~ U512[0]
|
||||
v[9] = ctx.s[1] ~ U512[1]
|
||||
v[10] = ctx.s[2] ~ U512[2]
|
||||
v[11] = ctx.s[3] ~ U512[3]
|
||||
v[12] = U512[4]
|
||||
v[13] = U512[5]
|
||||
v[14] = U512[6]
|
||||
v[15] = U512[7]
|
||||
|
||||
ctx.t += 1024
|
||||
if !ctx.nullt {
|
||||
v[12] ~= ctx.t
|
||||
v[13] ~= ctx.t
|
||||
v[14] ~= 0
|
||||
v[15] ~= 0
|
||||
}
|
||||
|
||||
for i, j = 0, 0; i < 16; i, j = i + 1, j + 8 {
|
||||
m[i] = u64(p[j]) << 56 | u64(p[j + 1]) << 48 | u64(p[j + 2]) << 40 | u64(p[j + 3]) << 32 |
|
||||
u64(p[j + 4]) << 24 | u64(p[j + 5]) << 16 | u64(p[j + 6]) << 8 | u64(p[j + 7])
|
||||
}
|
||||
for i = 0; i < 16; i += 1 {
|
||||
v[0], v[4], v[8], v[12] = G512(v[0], v[4], v[8], v[12], m, i, 0)
|
||||
v[1], v[5], v[9], v[13] = G512(v[1], v[5], v[9], v[13], m, i, 1)
|
||||
v[2], v[6], v[10], v[14] = G512(v[2], v[6], v[10], v[14], m, i, 2)
|
||||
v[3], v[7], v[11], v[15] = G512(v[3], v[7], v[11], v[15], m, i, 3)
|
||||
v[0], v[5], v[10], v[15] = G512(v[0], v[5], v[10], v[15], m, i, 4)
|
||||
v[1], v[6], v[11], v[12] = G512(v[1], v[6], v[11], v[12], m, i, 5)
|
||||
v[2], v[7], v[8], v[13] = G512(v[2], v[7], v[8], v[13], m, i, 6)
|
||||
v[3], v[4], v[9], v[14] = G512(v[3], v[4], v[9], v[14], m, i, 7)
|
||||
}
|
||||
|
||||
for i = 0; i < 8; i += 1 {
|
||||
ctx.h[i] ~= ctx.s[i % 4] ~ v[i] ~ v[i + 8]
|
||||
}
|
||||
p = p[BLOCKSIZE_512:]
|
||||
}
|
||||
}
|
||||
|
||||
write_additional :: proc "contextless" (ctx: ^$T, data: []byte) {
|
||||
ctx.t -= u64(len(data)) << 3
|
||||
update(ctx, data)
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package blake2b
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Interface for the BLAKE2B hashing algorithm.
|
||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_blake2"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 64
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: _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) -> ([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) -> ([DIGEST_SIZE]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE]byte{}, false
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
Blake2b_Context :: _blake2.Blake2b_Context
|
||||
|
||||
init :: proc(ctx: ^_blake2.Blake2b_Context) {
|
||||
_blake2.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, hash: []byte) {
|
||||
_blake2.final(ctx, hash)
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package blake2s
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Interface for the BLAKE2S hashing algorithm.
|
||||
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_blake2"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 32
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: _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) -> ([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) -> ([DIGEST_SIZE]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE]byte{}, false
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
Blake2s_Context :: _blake2.Blake2b_Context
|
||||
|
||||
init :: proc(ctx: ^_blake2.Blake2s_Context) {
|
||||
_blake2.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, data: []byte) {
|
||||
_blake2.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, hash: []byte) {
|
||||
_blake2.final(ctx, hash)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
package gost
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the GOST hashing algorithm, as defined in RFC 5831 <https://datatracker.ietf.org/doc/html/rfc5831>
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 32
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Gost_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Gost_Context
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: Gost_Context
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = 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) -> ([DIGEST_SIZE]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE]byte{}, false
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc "contextless" (ctx: ^Gost_Context) {
|
||||
sbox: [8][16]u32 = {
|
||||
{ 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15 },
|
||||
{ 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8 },
|
||||
{ 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13 },
|
||||
{ 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3 },
|
||||
{ 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5 },
|
||||
{ 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3 },
|
||||
{ 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11 },
|
||||
{ 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 },
|
||||
}
|
||||
|
||||
i := 0
|
||||
for a := 0; a < 16; a += 1 {
|
||||
ax := sbox[1][a] << 15
|
||||
bx := sbox[3][a] << 23
|
||||
cx := sbox[5][a]
|
||||
cx = (cx >> 1) | (cx << 31)
|
||||
dx := sbox[7][a] << 7
|
||||
for b := 0; b < 16; b, i = b + 1, i + 1 {
|
||||
SBOX_1[i] = ax | (sbox[0][b] << 11)
|
||||
SBOX_2[i] = bx | (sbox[2][b] << 19)
|
||||
SBOX_3[i] = cx | (sbox[4][b] << 27)
|
||||
SBOX_4[i] = dx | (sbox[6][b] << 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^Gost_Context, data: []byte) {
|
||||
length := byte(len(data))
|
||||
j: byte
|
||||
|
||||
i := ctx.partial_bytes
|
||||
for i < 32 && j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
|
||||
if i < 32 {
|
||||
ctx.partial_bytes = i
|
||||
return
|
||||
}
|
||||
bytes(ctx, ctx.partial[:], 256)
|
||||
|
||||
for (j + 32) < length {
|
||||
bytes(ctx, data[j:], 256)
|
||||
j += 32
|
||||
}
|
||||
|
||||
i = 0
|
||||
for j < length {
|
||||
ctx.partial[i] = data[j]
|
||||
i, j = i + 1, j + 1
|
||||
}
|
||||
ctx.partial_bytes = i
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Gost_Context, hash: []byte) {
|
||||
if ctx.partial_bytes > 0 {
|
||||
mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
|
||||
bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
|
||||
}
|
||||
|
||||
compress(ctx.hash[:], ctx.len[:])
|
||||
compress(ctx.hash[:], ctx.sum[:])
|
||||
|
||||
for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 {
|
||||
hash[j] = byte(ctx.hash[i])
|
||||
hash[j + 1] = byte(ctx.hash[i] >> 8)
|
||||
hash[j + 2] = byte(ctx.hash[i] >> 16)
|
||||
hash[j + 3] = byte(ctx.hash[i] >> 24)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GOST implementation
|
||||
*/
|
||||
|
||||
Gost_Context :: struct {
|
||||
sum: [8]u32,
|
||||
hash: [8]u32,
|
||||
len: [8]u32,
|
||||
partial: [32]byte,
|
||||
partial_bytes: byte,
|
||||
}
|
||||
|
||||
SBOX_1: [256]u32
|
||||
SBOX_2: [256]u32
|
||||
SBOX_3: [256]u32
|
||||
SBOX_4: [256]u32
|
||||
|
||||
ENCRYPT_ROUND :: #force_inline proc "contextless" (l, r, t, k1, k2: u32) -> (u32, u32, u32) {
|
||||
l, r, t := l, r, t
|
||||
t = (k1) + r
|
||||
l ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
|
||||
t = (k2) + l
|
||||
r ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
|
||||
return l, r, t
|
||||
}
|
||||
|
||||
ENCRYPT :: #force_inline proc "contextless" (a, b, c: u32, key: []u32) -> (l, r, t: u32) {
|
||||
l, r, t = ENCRYPT_ROUND(a, b, c, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[7], key[6])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[5], key[4])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[3], key[2])
|
||||
l, r, t = ENCRYPT_ROUND(l, r, t, key[1], key[0])
|
||||
t = r
|
||||
r = l
|
||||
l = t
|
||||
return
|
||||
}
|
||||
|
||||
bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
|
||||
a, c: u32
|
||||
m: [8]u32
|
||||
|
||||
for i, j := 0, 0; i < 8; i += 1 {
|
||||
a = u32(buf[j]) | u32(buf[j + 1]) << 8 | u32(buf[j + 2]) << 16 | u32(buf[j + 3]) << 24
|
||||
j += 4
|
||||
m[i] = a
|
||||
c = a + c + ctx.sum[i]
|
||||
ctx.sum[i] = c
|
||||
c = c < a ? 1 : 0
|
||||
}
|
||||
|
||||
compress(ctx.hash[:], m[:])
|
||||
ctx.len[0] += bits
|
||||
if ctx.len[0] < bits {
|
||||
ctx.len[1] += 1
|
||||
}
|
||||
}
|
||||
|
||||
compress :: proc(h, m: []u32) {
|
||||
key, u, v, w, s: [8]u32
|
||||
|
||||
copy(u[:], h)
|
||||
copy(v[:], m)
|
||||
|
||||
for i := 0; i < 8; i += 2 {
|
||||
w[0] = u[0] ~ v[0]
|
||||
w[1] = u[1] ~ v[1]
|
||||
w[2] = u[2] ~ v[2]
|
||||
w[3] = u[3] ~ v[3]
|
||||
w[4] = u[4] ~ v[4]
|
||||
w[5] = u[5] ~ v[5]
|
||||
w[6] = u[6] ~ v[6]
|
||||
w[7] = u[7] ~ v[7]
|
||||
|
||||
key[0] = (w[0] & 0x000000ff) | (w[2] & 0x000000ff) << 8 | (w[4] & 0x000000ff) << 16 | (w[6] & 0x000000ff) << 24
|
||||
key[1] = (w[0] & 0x0000ff00) >> 8 | (w[2] & 0x0000ff00) | (w[4] & 0x0000ff00) << 8 | (w[6] & 0x0000ff00) << 16
|
||||
key[2] = (w[0] & 0x00ff0000) >> 16 | (w[2] & 0x00ff0000) >> 8 | (w[4] & 0x00ff0000) | (w[6] & 0x00ff0000) << 8
|
||||
key[3] = (w[0] & 0xff000000) >> 24 | (w[2] & 0xff000000) >> 16 | (w[4] & 0xff000000) >> 8 | (w[6] & 0xff000000)
|
||||
key[4] = (w[1] & 0x000000ff) | (w[3] & 0x000000ff) << 8 | (w[5] & 0x000000ff) << 16 | (w[7] & 0x000000ff) << 24
|
||||
key[5] = (w[1] & 0x0000ff00) >> 8 | (w[3] & 0x0000ff00) | (w[5] & 0x0000ff00) << 8 | (w[7] & 0x0000ff00) << 16
|
||||
key[6] = (w[1] & 0x00ff0000) >> 16 | (w[3] & 0x00ff0000) >> 8 | (w[5] & 0x00ff0000) | (w[7] & 0x00ff0000) << 8
|
||||
key[7] = (w[1] & 0xff000000) >> 24 | (w[3] & 0xff000000) >> 16 | (w[5] & 0xff000000) >> 8 | (w[7] & 0xff000000)
|
||||
|
||||
r := h[i]
|
||||
l := h[i + 1]
|
||||
t: u32
|
||||
l, r, t = ENCRYPT(l, r, 0, key[:])
|
||||
|
||||
s[i] = r
|
||||
s[i + 1] = l
|
||||
|
||||
if i == 6 {
|
||||
break
|
||||
}
|
||||
|
||||
l = u[0] ~ u[2]
|
||||
r = u[1] ~ u[3]
|
||||
u[0] = u[2]
|
||||
u[1] = u[3]
|
||||
u[2] = u[4]
|
||||
u[3] = u[5]
|
||||
u[4] = u[6]
|
||||
u[5] = u[7]
|
||||
u[6] = l
|
||||
u[7] = r
|
||||
|
||||
if i == 2 {
|
||||
u[0] ~= 0xff00ff00
|
||||
u[1] ~= 0xff00ff00
|
||||
u[2] ~= 0x00ff00ff
|
||||
u[3] ~= 0x00ff00ff
|
||||
u[4] ~= 0x00ffff00
|
||||
u[5] ~= 0xff0000ff
|
||||
u[6] ~= 0x000000ff
|
||||
u[7] ~= 0xff00ffff
|
||||
}
|
||||
|
||||
l = v[0]
|
||||
r = v[2]
|
||||
v[0] = v[4]
|
||||
v[2] = v[6]
|
||||
v[4] = l ~ r
|
||||
v[6] = v[0] ~ r
|
||||
l = v[1]
|
||||
r = v[3]
|
||||
v[1] = v[5]
|
||||
v[3] = v[7]
|
||||
v[5] = l ~ r
|
||||
v[7] = v[1] ~ r
|
||||
}
|
||||
|
||||
u[0] = m[0] ~ s[6]
|
||||
u[1] = m[1] ~ s[7]
|
||||
u[2] = m[2] ~ (s[0] << 16) ~ (s[0] >> 16) ~ (s[0] & 0xffff) ~
|
||||
(s[1] & 0xffff) ~ (s[1] >> 16) ~ (s[2] << 16) ~ s[6] ~ (s[6] << 16) ~
|
||||
(s[7] & 0xffff0000) ~ (s[7] >> 16)
|
||||
u[3] = m[3] ~ (s[0] & 0xffff) ~ (s[0] << 16) ~ (s[1] & 0xffff) ~
|
||||
(s[1] << 16) ~ (s[1] >> 16) ~ (s[2] << 16) ~ (s[2] >> 16) ~
|
||||
(s[3] << 16) ~ s[6] ~ (s[6] << 16) ~ (s[6] >> 16) ~ (s[7] & 0xffff) ~
|
||||
(s[7] << 16) ~ (s[7] >> 16)
|
||||
u[4] = m[4] ~
|
||||
(s[0] & 0xffff0000) ~ (s[0] << 16) ~ (s[0] >> 16) ~
|
||||
(s[1] & 0xffff0000) ~ (s[1] >> 16) ~ (s[2] << 16) ~ (s[2] >> 16) ~
|
||||
(s[3] << 16) ~ (s[3] >> 16) ~ (s[4] << 16) ~ (s[6] << 16) ~
|
||||
(s[6] >> 16) ~(s[7] & 0xffff) ~ (s[7] << 16) ~ (s[7] >> 16)
|
||||
u[5] = m[5] ~ (s[0] << 16) ~ (s[0] >> 16) ~ (s[0] & 0xffff0000) ~
|
||||
(s[1] & 0xffff) ~ s[2] ~ (s[2] >> 16) ~ (s[3] << 16) ~ (s[3] >> 16) ~
|
||||
(s[4] << 16) ~ (s[4] >> 16) ~ (s[5] << 16) ~ (s[6] << 16) ~
|
||||
(s[6] >> 16) ~ (s[7] & 0xffff0000) ~ (s[7] << 16) ~ (s[7] >> 16)
|
||||
u[6] = m[6] ~ s[0] ~ (s[1] >> 16) ~ (s[2] << 16) ~ s[3] ~ (s[3] >> 16) ~
|
||||
(s[4] << 16) ~ (s[4] >> 16) ~ (s[5] << 16) ~ (s[5] >> 16) ~ s[6] ~
|
||||
(s[6] << 16) ~ (s[6] >> 16) ~ (s[7] << 16)
|
||||
u[7] = m[7] ~ (s[0] & 0xffff0000) ~ (s[0] << 16) ~ (s[1] & 0xffff) ~
|
||||
(s[1] << 16) ~ (s[2] >> 16) ~ (s[3] << 16) ~ s[4] ~ (s[4] >> 16) ~
|
||||
(s[5] << 16) ~ (s[5] >> 16) ~ (s[6] >> 16) ~ (s[7] & 0xffff) ~
|
||||
(s[7] << 16) ~ (s[7] >> 16)
|
||||
|
||||
v[0] = h[0] ~ (u[1] << 16) ~ (u[0] >> 16)
|
||||
v[1] = h[1] ~ (u[2] << 16) ~ (u[1] >> 16)
|
||||
v[2] = h[2] ~ (u[3] << 16) ~ (u[2] >> 16)
|
||||
v[3] = h[3] ~ (u[4] << 16) ~ (u[3] >> 16)
|
||||
v[4] = h[4] ~ (u[5] << 16) ~ (u[4] >> 16)
|
||||
v[5] = h[5] ~ (u[6] << 16) ~ (u[5] >> 16)
|
||||
v[6] = h[6] ~ (u[7] << 16) ~ (u[6] >> 16)
|
||||
v[7] = h[7] ~ (u[0] & 0xffff0000) ~ (u[0] << 16) ~ (u[7] >> 16) ~ (u[1] & 0xffff0000) ~ (u[1] << 16) ~ (u[6] << 16) ~ (u[7] & 0xffff0000)
|
||||
|
||||
h[0] = (v[0] & 0xffff0000) ~ (v[0] << 16) ~ (v[0] >> 16) ~ (v[1] >> 16) ~
|
||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ (v[3] >> 16) ~ (v[4] << 16) ~
|
||||
(v[5] >> 16) ~ v[5] ~ (v[6] >> 16) ~ (v[7] << 16) ~ (v[7] >> 16) ~
|
||||
(v[7] & 0xffff)
|
||||
h[1] = (v[0] << 16) ~ (v[0] >> 16) ~ (v[0] & 0xffff0000) ~ (v[1] & 0xffff) ~
|
||||
v[2] ~ (v[2] >> 16) ~ (v[3] << 16) ~ (v[4] >> 16) ~ (v[5] << 16) ~
|
||||
(v[6] << 16) ~ v[6] ~ (v[7] & 0xffff0000) ~ (v[7] >> 16)
|
||||
h[2] = (v[0] & 0xffff) ~ (v[0] << 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~
|
||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ (v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~
|
||||
(v[5] >> 16) ~ v[6] ~ (v[6] >> 16) ~ (v[7] & 0xffff) ~ (v[7] << 16) ~
|
||||
(v[7] >> 16)
|
||||
h[3] = (v[0] << 16) ~ (v[0] >> 16) ~ (v[0] & 0xffff0000) ~
|
||||
(v[1] & 0xffff0000) ~ (v[1] >> 16) ~ (v[2] << 16) ~ (v[2] >> 16) ~ v[2] ~
|
||||
(v[3] << 16) ~ (v[4] >> 16) ~ v[4] ~ (v[5] << 16) ~ (v[6] << 16) ~
|
||||
(v[7] & 0xffff) ~ (v[7] >> 16)
|
||||
h[4] = (v[0] >> 16) ~ (v[1] << 16) ~ v[1] ~ (v[2] >> 16) ~ v[2] ~
|
||||
(v[3] << 16) ~ (v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ (v[5] >> 16) ~
|
||||
v[5] ~ (v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16)
|
||||
h[5] = (v[0] << 16) ~ (v[0] & 0xffff0000) ~ (v[1] << 16) ~ (v[1] >> 16) ~
|
||||
(v[1] & 0xffff0000) ~ (v[2] << 16) ~ v[2] ~ (v[3] >> 16) ~ v[3] ~
|
||||
(v[4] << 16) ~ (v[4] >> 16) ~ v[4] ~ (v[5] << 16) ~ (v[6] << 16) ~
|
||||
(v[6] >> 16) ~ v[6] ~ (v[7] << 16) ~ (v[7] >> 16) ~ (v[7] & 0xffff0000)
|
||||
h[6] = v[0] ~ v[2] ~ (v[2] >> 16) ~ v[3] ~ (v[3] << 16) ~ v[4] ~
|
||||
(v[4] >> 16) ~ (v[5] << 16) ~ (v[5] >> 16) ~ v[5] ~ (v[6] << 16) ~
|
||||
(v[6] >> 16) ~ v[6] ~ (v[7] << 16) ~ v[7]
|
||||
h[7] = v[0] ~ (v[0] >> 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~ (v[2] << 16) ~
|
||||
(v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ v[4] ~ (v[5] >> 16) ~ v[5] ~
|
||||
(v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16) ~ v[7]
|
||||
}
|
||||
@@ -0,0 +1,653 @@
|
||||
package groestl
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the GROESTL hashing algorithm, as defined in <http://www.groestl.info/Groestl.zip>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_224 :: 28
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_384 :: 48
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_224 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_224 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_224 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 224
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = 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) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_224(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_224(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_224]byte{}, false
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
hash_bytes_to_buffer_224,
|
||||
hash_string_to_buffer_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_256 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_256 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_256 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 256
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = 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) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_256(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_256(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_256]byte{}, false
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
hash_bytes_to_buffer_256,
|
||||
hash_string_to_buffer_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_384 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_384 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_384 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 384
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = 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) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_384(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_384(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_384]byte{}, false
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
hash_bytes_to_buffer_384,
|
||||
hash_string_to_buffer_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash[:])
|
||||
return hash
|
||||
}
|
||||
|
||||
// hash_string_to_buffer_512 will hash the given input and assign the
|
||||
// computed hash to the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
|
||||
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
|
||||
}
|
||||
|
||||
// hash_bytes_to_buffer_512 will hash the given input and write the
|
||||
// computed hash into the second parameter.
|
||||
// It requires that the destination buffer is at least as big as the digest size
|
||||
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
|
||||
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
update(&ctx, data)
|
||||
final(&ctx, hash)
|
||||
}
|
||||
|
||||
// hash_stream_512 will read the stream in chunks and compute a
|
||||
// hash from its contents
|
||||
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: Groestl_Context
|
||||
ctx.hashbitlen = 512
|
||||
init(&ctx)
|
||||
buf := make([]byte, 512)
|
||||
defer delete(buf)
|
||||
read := 1
|
||||
for read > 0 {
|
||||
read, _ = 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) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_512(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_512(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_512]byte{}, false
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^Groestl_Context) {
|
||||
assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
|
||||
if ctx.hashbitlen <= 256 {
|
||||
ctx.rounds = 10
|
||||
ctx.columns = 8
|
||||
ctx.statesize = 64
|
||||
} else {
|
||||
ctx.rounds = 14
|
||||
ctx.columns = 16
|
||||
ctx.statesize = 128
|
||||
}
|
||||
for i := 8 - size_of(i32); i < 8; i += 1 {
|
||||
ctx.chaining[i][ctx.columns - 1] = byte(ctx.hashbitlen >> (8 * (7 - uint(i))))
|
||||
}
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^Groestl_Context, data: []byte) {
|
||||
databitlen := len(data) * 8
|
||||
msglen := databitlen / 8
|
||||
rem := databitlen % 8
|
||||
|
||||
i: int
|
||||
assert(ctx.bits_in_last_byte == 0)
|
||||
|
||||
if ctx.buf_ptr != 0 {
|
||||
for i = 0; ctx.buf_ptr < ctx.statesize && i < msglen; i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1 {
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
}
|
||||
|
||||
if ctx.buf_ptr < ctx.statesize {
|
||||
if rem != 0 {
|
||||
ctx.bits_in_last_byte = rem
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.buf_ptr = 0
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
}
|
||||
|
||||
transform(ctx, data[i:], u32(msglen - i))
|
||||
i += ((msglen - i) / ctx.statesize) * ctx.statesize
|
||||
for i < msglen {
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1
|
||||
}
|
||||
|
||||
if rem != 0 {
|
||||
ctx.bits_in_last_byte = rem
|
||||
ctx.buffer[ctx.buf_ptr] = data[i]
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
}
|
||||
|
||||
final :: proc(ctx: ^Groestl_Context, hash: []byte) {
|
||||
hashbytelen := ctx.hashbitlen / 8
|
||||
|
||||
if ctx.bits_in_last_byte != 0 {
|
||||
ctx.buffer[ctx.buf_ptr - 1] &= ((1 << uint(ctx.bits_in_last_byte)) - 1) << (8 - uint(ctx.bits_in_last_byte))
|
||||
ctx.buffer[ctx.buf_ptr - 1] ~= 0x1 << (7 - uint(ctx.bits_in_last_byte))
|
||||
} else {
|
||||
ctx.buffer[ctx.buf_ptr] = 0x80
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
|
||||
if ctx.buf_ptr > ctx.statesize - 8 {
|
||||
for ctx.buf_ptr < ctx.statesize {
|
||||
ctx.buffer[ctx.buf_ptr] = 0
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
ctx.buf_ptr = 0
|
||||
}
|
||||
|
||||
for ctx.buf_ptr < ctx.statesize - 8 {
|
||||
ctx.buffer[ctx.buf_ptr] = 0
|
||||
ctx.buf_ptr += 1
|
||||
}
|
||||
|
||||
ctx.block_counter += 1
|
||||
ctx.buf_ptr = ctx.statesize
|
||||
|
||||
for ctx.buf_ptr > ctx.statesize - 8 {
|
||||
ctx.buf_ptr -= 1
|
||||
ctx.buffer[ctx.buf_ptr] = byte(ctx.block_counter)
|
||||
ctx.block_counter >>= 8
|
||||
}
|
||||
|
||||
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
|
||||
output_transformation(ctx)
|
||||
|
||||
for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 {
|
||||
hash[j] = ctx.chaining[i % 8][i / 8]
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GROESTL implementation
|
||||
*/
|
||||
|
||||
SBOX := [256]byte {
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
|
||||
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
|
||||
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
||||
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
|
||||
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
||||
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
|
||||
0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
||||
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
|
||||
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
||||
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
|
||||
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
||||
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
|
||||
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
||||
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
|
||||
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
||||
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
|
||||
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
|
||||
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
||||
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
|
||||
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
||||
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
|
||||
0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
||||
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
|
||||
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
||||
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
|
||||
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
||||
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
|
||||
0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
||||
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
|
||||
0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
|
||||
}
|
||||
|
||||
SHIFT := [2][2][8]int {
|
||||
{{0, 1, 2, 3, 4, 5, 6, 7}, {1, 3, 5, 7, 0, 2, 4, 6}},
|
||||
{{0, 1, 2, 3, 4, 5, 6, 11}, {1, 3, 5, 11, 0, 2, 4, 6}},
|
||||
}
|
||||
|
||||
Groestl_Context :: struct {
|
||||
chaining: [8][16]byte,
|
||||
block_counter: u64,
|
||||
hashbitlen: int,
|
||||
buffer: [128]byte,
|
||||
buf_ptr: int,
|
||||
bits_in_last_byte: int,
|
||||
columns: int,
|
||||
rounds: int,
|
||||
statesize: int,
|
||||
}
|
||||
|
||||
Groestl_Variant :: enum {
|
||||
P512 = 0,
|
||||
Q512 = 1,
|
||||
P1024 = 2,
|
||||
Q1024 = 3,
|
||||
}
|
||||
|
||||
MUL2 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return (b >> 7) != 0 ? (b << 1) ~ 0x1b : (b << 1)
|
||||
}
|
||||
|
||||
MUL3 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL2(b) ~ b
|
||||
}
|
||||
|
||||
MUL4 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL2(MUL2(b))
|
||||
}
|
||||
|
||||
MUL5 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL4(b) ~ b
|
||||
}
|
||||
|
||||
MUL6 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL4(b) ~ MUL2(b)
|
||||
}
|
||||
|
||||
MUL7 :: #force_inline proc "contextless"(b: byte) -> byte {
|
||||
return MUL4(b) ~ MUL2(b) ~ b
|
||||
}
|
||||
|
||||
sub_bytes :: #force_inline proc (x: [][16]byte, columns: int) {
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < columns; j += 1 {
|
||||
x[i][j] = SBOX[x[i][j]]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shift_bytes :: #force_inline proc (x: [][16]byte, columns: int, v: Groestl_Variant) {
|
||||
temp: [16]byte
|
||||
R := &SHIFT[int(v) / 2][int(v) & 1]
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < columns; j += 1 {
|
||||
temp[j] = x[i][(j + R[i]) % columns]
|
||||
}
|
||||
for j := 0; j < columns; j += 1 {
|
||||
x[i][j] = temp[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mix_bytes :: #force_inline proc (x: [][16]byte, columns: int) {
|
||||
temp: [8]byte
|
||||
|
||||
for i := 0; i < columns; i += 1 {
|
||||
for j := 0; j < 8; j += 1 {
|
||||
temp[j] = MUL2(x[(j + 0) % 8][i]) ~
|
||||
MUL2(x[(j + 1) % 8][i]) ~
|
||||
MUL3(x[(j + 2) % 8][i]) ~
|
||||
MUL4(x[(j + 3) % 8][i]) ~
|
||||
MUL5(x[(j + 4) % 8][i]) ~
|
||||
MUL3(x[(j + 5) % 8][i]) ~
|
||||
MUL5(x[(j + 6) % 8][i]) ~
|
||||
MUL7(x[(j + 7) % 8][i])
|
||||
}
|
||||
for j := 0; j < 8; j += 1 {
|
||||
x[j][i] = temp[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p :: #force_inline proc (ctx: ^Groestl_Context, x: [][16]byte) {
|
||||
v := ctx.columns == 8 ? Groestl_Variant.P512 : Groestl_Variant.P1024
|
||||
for i := 0; i < ctx.rounds; i += 1 {
|
||||
add_roundconstant(x, ctx.columns, byte(i), v)
|
||||
sub_bytes(x, ctx.columns)
|
||||
shift_bytes(x, ctx.columns, v)
|
||||
mix_bytes(x, ctx.columns)
|
||||
}
|
||||
}
|
||||
|
||||
q :: #force_inline proc (ctx: ^Groestl_Context, x: [][16]byte) {
|
||||
v := ctx.columns == 8 ? Groestl_Variant.Q512 : Groestl_Variant.Q1024
|
||||
for i := 0; i < ctx.rounds; i += 1 {
|
||||
add_roundconstant(x, ctx.columns, byte(i), v)
|
||||
sub_bytes(x, ctx.columns)
|
||||
shift_bytes(x, ctx.columns, v)
|
||||
mix_bytes(x, ctx.columns)
|
||||
}
|
||||
}
|
||||
|
||||
transform :: proc(ctx: ^Groestl_Context, input: []byte, msglen: u32) {
|
||||
tmp1, tmp2: [8][16]byte
|
||||
input, msglen := input, msglen
|
||||
|
||||
for msglen >= u32(ctx.statesize) {
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
tmp1[i][j] = ctx.chaining[i][j] ~ input[j * 8 + i]
|
||||
tmp2[i][j] = input[j * 8 + i]
|
||||
}
|
||||
}
|
||||
|
||||
p(ctx, tmp1[:])
|
||||
q(ctx, tmp2[:])
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
ctx.chaining[i][j] ~= tmp1[i][j] ~ tmp2[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
ctx.block_counter += 1
|
||||
msglen -= u32(ctx.statesize)
|
||||
input = input[ctx.statesize:]
|
||||
}
|
||||
}
|
||||
|
||||
output_transformation :: proc(ctx: ^Groestl_Context) {
|
||||
temp: [8][16]byte
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
temp[i][j] = ctx.chaining[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
p(ctx, temp[:])
|
||||
|
||||
for i := 0; i < 8; i += 1 {
|
||||
for j := 0; j < ctx.columns; j += 1 {
|
||||
ctx.chaining[i][j] ~= temp[i][j]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_roundconstant :: proc(x: [][16]byte, columns: int, round: byte, v: Groestl_Variant) {
|
||||
switch (i32(v) & 1) {
|
||||
case 0:
|
||||
for i := 0; i < columns; i += 1 {
|
||||
x[0][i] ~= byte(i << 4) ~ round
|
||||
}
|
||||
case 1:
|
||||
for i := 0; i < columns; i += 1 {
|
||||
for j := 0; j < 7; j += 1 {
|
||||
x[j][i] ~= 0xff
|
||||
}
|
||||
}
|
||||
for i := 0; i < columns; i += 1 {
|
||||
x[7][i] ~= byte(i << 4) ~ 0xff ~ round
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,584 @@
|
||||
package jh
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the JH hashing algorithm, as defined in <https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_224 :: 28
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_384 :: 48
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: 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) -> ([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) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_224(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_224(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_224]byte{}, false
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
hash_bytes_to_buffer_224,
|
||||
hash_string_to_buffer_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: 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) -> ([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) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_256(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_256(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_256]byte{}, false
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
hash_bytes_to_buffer_256,
|
||||
hash_string_to_buffer_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: 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) -> ([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) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_384(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_384(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_384]byte{}, false
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
hash_bytes_to_buffer_384,
|
||||
hash_string_to_buffer_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: 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) -> ([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) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_512(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_512(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_512]byte{}, false
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^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: ^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)
|
||||
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])
|
||||
}
|
||||
ctx.buffer_size = databitlen
|
||||
}
|
||||
}
|
||||
|
||||
final :: 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
|
||||
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))
|
||||
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
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
JH implementation
|
||||
*/
|
||||
|
||||
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,
|
||||
0xe, 0xa, 0x9, 0x5, 0x7, 0xd, 0x3, 0xe,
|
||||
0x3, 0xa, 0xd, 0xe, 0xc, 0x1, 0x7, 0x5,
|
||||
0x1, 0x2, 0x7, 0x7, 0x5, 0x0, 0x9, 0x9,
|
||||
0xd, 0xa, 0x2, 0xf, 0x5, 0x9, 0x0, 0xb,
|
||||
0x0, 0x6, 0x6, 0x7, 0x3, 0x2, 0x2, 0xa,
|
||||
}
|
||||
|
||||
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},
|
||||
}
|
||||
|
||||
Jh_Context :: struct {
|
||||
hashbitlen: int,
|
||||
databitlen: u64,
|
||||
buffer_size: u64,
|
||||
H: [128]byte,
|
||||
A: [256]byte,
|
||||
roundconstant: [64]byte,
|
||||
buffer: [64]byte,
|
||||
}
|
||||
|
||||
E8_finaldegroup :: proc(ctx: ^Jh_Context) {
|
||||
t0,t1,t2,t3: byte
|
||||
tem: [256]byte
|
||||
for i := 0; i < 128; i += 1 {
|
||||
tem[i] = ctx.A[i << 1]
|
||||
tem[i + 128] = ctx.A[(i << 1) + 1]
|
||||
}
|
||||
for i := 0; i < 128; i += 1 {
|
||||
ctx.H[i] = 0
|
||||
}
|
||||
for i := 0; i < 256; i += 1 {
|
||||
t0 = (tem[i] >> 3) & 1
|
||||
t1 = (tem[i] >> 2) & 1
|
||||
t2 = (tem[i] >> 1) & 1
|
||||
t3 = (tem[i] >> 0) & 1
|
||||
|
||||
ctx.H[uint(i) >> 3] |= t0 << (7 - (uint(i) & 7))
|
||||
ctx.H[(uint(i) + 256) >> 3] |= t1 << (7 - (uint(i) & 7))
|
||||
ctx.H[(uint(i) + 512) >> 3] |= t2 << (7 - (uint(i) & 7))
|
||||
ctx.H[(uint(i) + 768) >> 3] |= t3 << (7 - (uint(i) & 7))
|
||||
}
|
||||
}
|
||||
|
||||
update_roundconstant :: proc(ctx: ^Jh_Context) {
|
||||
tem: [64]byte
|
||||
t: byte
|
||||
for i := 0; i < 64; i += 1 {
|
||||
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
|
||||
tem[i] ~= ((tem[i + 1] << 1) ~ (tem[i + 1] >> 3) ~ ((tem[i + 1] >> 2) & 2)) & 0xf
|
||||
}
|
||||
for i := 0; i < 64; i += 4 {
|
||||
t = tem[i + 2]
|
||||
tem[i + 2] = tem[i + 3]
|
||||
tem[i + 3] = t
|
||||
}
|
||||
for i := 0; i < 32; i += 1 {
|
||||
ctx.roundconstant[i] = tem[i << 1]
|
||||
ctx.roundconstant[i + 32] = tem[(i << 1) + 1]
|
||||
}
|
||||
for i := 32; i < 64; i += 2 {
|
||||
t = ctx.roundconstant[i]
|
||||
ctx.roundconstant[i] = ctx.roundconstant[i + 1]
|
||||
ctx.roundconstant[i + 1] = t
|
||||
}
|
||||
}
|
||||
|
||||
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] = 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
|
||||
tem[i] ~= ((tem[i + 1] << 1) ~ (tem[i + 1] >> 3) ~ ((tem[i + 1] >> 2) & 2)) & 0xf
|
||||
}
|
||||
for i := 0; i < 256; i += 4 {
|
||||
t = tem[i + 2]
|
||||
tem[i+2] = tem[i + 3]
|
||||
tem[i+3] = t
|
||||
}
|
||||
for i := 0; i < 128; i += 1 {
|
||||
ctx.A[i] = tem[i << 1]
|
||||
ctx.A[i + 128] = tem[(i << 1) + 1]
|
||||
}
|
||||
for i := 128; i < 256; i += 2 {
|
||||
t = ctx.A[i]
|
||||
ctx.A[i] = ctx.A[i + 1]
|
||||
ctx.A[i + 1] = t
|
||||
}
|
||||
}
|
||||
|
||||
E8_initialgroup :: proc(ctx: ^Jh_Context) {
|
||||
t0, t1, t2, t3: byte
|
||||
tem: [256]byte
|
||||
for i := u32(0); i < 256; i += 1 {
|
||||
t0 = (ctx.H[i >> 3] >> (7 - (i & 7))) & 1
|
||||
t1 = (ctx.H[(i + 256) >> 3] >> (7 - (i & 7))) & 1
|
||||
t2 = (ctx.H[(i + 512) >> 3] >> (7 - (i & 7))) & 1
|
||||
t3 = (ctx.H[(i + 768) >> 3] >> (7 - (i & 7))) & 1
|
||||
tem[i] = (t0 << 3) | (t1 << 2) | (t2 << 1) | (t3 << 0)
|
||||
}
|
||||
for i := 0; i < 128; i += 1 {
|
||||
ctx.A[i << 1] = tem[i]
|
||||
ctx.A[(i << 1) + 1] = tem[i + 128]
|
||||
}
|
||||
}
|
||||
|
||||
E8 :: proc(ctx: ^Jh_Context) {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.roundconstant[i] = ROUNDCONSTANT_ZERO[i]
|
||||
}
|
||||
E8_initialgroup(ctx)
|
||||
for i := 0; i < 42; i += 1 {
|
||||
R8(ctx)
|
||||
update_roundconstant(ctx)
|
||||
}
|
||||
E8_finaldegroup(ctx)
|
||||
}
|
||||
|
||||
F8 :: proc(ctx: ^Jh_Context) {
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.H[i] ~= ctx.buffer[i]
|
||||
}
|
||||
E8(ctx)
|
||||
for i := 0; i < 64; i += 1 {
|
||||
ctx.H[i + 64] ~= ctx.buffer[i]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
package keccak
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Interface for the Keccak hashing algorithm.
|
||||
This is done because the padding in the SHA3 standard was changed by the NIST, resulting in a different output.
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../_sha3"
|
||||
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE_224 :: 28
|
||||
DIGEST_SIZE_256 :: 32
|
||||
DIGEST_SIZE_384 :: 48
|
||||
DIGEST_SIZE_512 :: 64
|
||||
|
||||
// hash_string_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
|
||||
return hash_bytes_224(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_224 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
|
||||
hash: [DIGEST_SIZE_224]byte
|
||||
ctx: _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) -> ([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) -> ([DIGEST_SIZE_224]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_224(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_224(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_224]byte{}, false
|
||||
}
|
||||
|
||||
hash_224 :: proc {
|
||||
hash_stream_224,
|
||||
hash_file_224,
|
||||
hash_bytes_224,
|
||||
hash_string_224,
|
||||
hash_bytes_to_buffer_224,
|
||||
hash_string_to_buffer_224,
|
||||
}
|
||||
|
||||
// hash_string_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
|
||||
return hash_bytes_256(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_256 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
|
||||
hash: [DIGEST_SIZE_256]byte
|
||||
ctx: _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) -> ([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) -> ([DIGEST_SIZE_256]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_256(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_256(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_256]byte{}, false
|
||||
}
|
||||
|
||||
hash_256 :: proc {
|
||||
hash_stream_256,
|
||||
hash_file_256,
|
||||
hash_bytes_256,
|
||||
hash_string_256,
|
||||
hash_bytes_to_buffer_256,
|
||||
hash_string_to_buffer_256,
|
||||
}
|
||||
|
||||
// hash_string_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
|
||||
return hash_bytes_384(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_384 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
|
||||
hash: [DIGEST_SIZE_384]byte
|
||||
ctx: _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) -> ([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) -> ([DIGEST_SIZE_384]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_384(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_384(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_384]byte{}, false
|
||||
}
|
||||
|
||||
hash_384 :: proc {
|
||||
hash_stream_384,
|
||||
hash_file_384,
|
||||
hash_bytes_384,
|
||||
hash_string_384,
|
||||
hash_bytes_to_buffer_384,
|
||||
hash_string_to_buffer_384,
|
||||
}
|
||||
|
||||
// hash_string_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
|
||||
return hash_bytes_512(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes_512 will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
|
||||
hash: [DIGEST_SIZE_512]byte
|
||||
ctx: _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) -> ([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) -> ([DIGEST_SIZE_512]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream_512(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes_512(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE_512]byte{}, false
|
||||
}
|
||||
|
||||
hash_512 :: proc {
|
||||
hash_stream_512,
|
||||
hash_file_512,
|
||||
hash_bytes_512,
|
||||
hash_string_512,
|
||||
hash_bytes_to_buffer_512,
|
||||
hash_string_to_buffer_512,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
Keccak_Context :: _sha3.Sha3_Context
|
||||
|
||||
init :: proc(ctx: ^_sha3.Sha3_Context) {
|
||||
ctx.is_keccak = true
|
||||
_sha3.init(ctx)
|
||||
}
|
||||
|
||||
update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
|
||||
_sha3.update(ctx, data)
|
||||
}
|
||||
|
||||
final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
|
||||
_sha3.final(ctx, hash)
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package md2
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the MD2 hashing algorithm, as defined in RFC 1319 <https://datatracker.ietf.org/doc/html/rfc1319>
|
||||
*/
|
||||
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 16
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: 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) -> ([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) -> ([DIGEST_SIZE]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE]byte{}, false
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
@(warning="Init is a no-op for MD2")
|
||||
init :: proc(ctx: ^Md2_Context) {
|
||||
// No action needed here
|
||||
}
|
||||
|
||||
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: ^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
|
||||
}
|
||||
transform(ctx, ctx.data[:])
|
||||
transform(ctx, ctx.checksum[:])
|
||||
for i := 0; i < DIGEST_SIZE; i += 1 {
|
||||
hash[i] = ctx.state[i]
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MD2 implementation
|
||||
*/
|
||||
|
||||
Md2_Context :: struct {
|
||||
data: [DIGEST_SIZE]byte,
|
||||
state: [DIGEST_SIZE * 3]byte,
|
||||
checksum: [DIGEST_SIZE]byte,
|
||||
datalen: int,
|
||||
}
|
||||
|
||||
PI_TABLE := [?]byte {
|
||||
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
|
||||
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, 76,
|
||||
130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, 138,
|
||||
23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251, 245, 142,
|
||||
187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63, 148, 194, 16,
|
||||
137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50, 39, 53, 62,
|
||||
204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165, 181, 209, 215,
|
||||
94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210, 150, 164, 125, 182,
|
||||
118, 252, 107, 226, 156, 116, 4, 241, 69, 157, 112, 89, 100, 113, 135,
|
||||
32, 134, 91, 207, 101, 230, 45, 168, 2, 27, 96, 37, 173, 174, 176,
|
||||
185, 246, 28, 70, 97, 105, 52, 64, 126, 15, 85, 71, 163, 35, 221,
|
||||
81, 175, 58, 195, 92, 249, 206, 186, 197, 234, 38, 44, 83, 13, 110,
|
||||
133, 40, 132, 9, 211, 223, 205, 244, 65, 129, 77, 82, 106, 220, 55,
|
||||
200, 108, 193, 171, 250, 36, 225, 123, 8, 12, 189, 177, 74, 120, 136,
|
||||
149, 139, 227, 99, 232, 109, 233, 203, 213, 254, 59, 0, 29, 57, 242,
|
||||
239, 183, 14, 102, 88, 208, 228, 166, 119, 114, 248, 235, 117, 75, 10,
|
||||
49, 68, 80, 180, 143, 237, 31, 26, 219, 153, 141, 51, 159, 17, 131,
|
||||
20,
|
||||
}
|
||||
|
||||
transform :: proc(ctx: ^Md2_Context, data: []byte) {
|
||||
j,k,t: byte
|
||||
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 < 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[DIGEST_SIZE - 1]
|
||||
for j = 0; j < DIGEST_SIZE; j += 1 {
|
||||
ctx.checksum[j] ~= PI_TABLE[data[j] ~ t]
|
||||
t = ctx.checksum[j]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package md4
|
||||
|
||||
/*
|
||||
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 MD4 hashing algorithm, as defined in RFC 1320 <https://datatracker.ietf.org/doc/html/rfc1320>
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 16
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: 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) -> ([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) -> ([DIGEST_SIZE]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE]byte{}, false
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^Md4_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MD4 implementation
|
||||
*/
|
||||
|
||||
BLOCK_SIZE :: 64
|
||||
|
||||
Md4_Context :: struct {
|
||||
data: [64]byte,
|
||||
state: [4]u32,
|
||||
bitlen: u64,
|
||||
datalen: u32,
|
||||
}
|
||||
|
||||
/*
|
||||
@note(zh): F, G and H, as mentioned in the RFC, have been inlined into FF, GG
|
||||
and HH respectively, instead of declaring them separately.
|
||||
*/
|
||||
|
||||
FF :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
|
||||
return util.ROTL32(a + ((b & c) | (~b & d)) + x, s)
|
||||
}
|
||||
|
||||
GG :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
|
||||
return util.ROTL32(a + ((b & c) | (b & d) | (c & d)) + x + 0x5a827999, s)
|
||||
}
|
||||
|
||||
HH :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
|
||||
return util.ROTL32(a + (b ~ c ~ d) + x + 0x6ed9eba1, s)
|
||||
}
|
||||
|
||||
transform :: proc(ctx: ^Md4_Context, data: []byte) {
|
||||
a, b, c, d, i, j: u32
|
||||
m: [DIGEST_SIZE]u32
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
a = ctx.state[0]
|
||||
b = ctx.state[1]
|
||||
c = ctx.state[2]
|
||||
d = ctx.state[3]
|
||||
|
||||
a = FF(a, b, c, d, m[0], 3)
|
||||
d = FF(d, a, b, c, m[1], 7)
|
||||
c = FF(c, d, a, b, m[2], 11)
|
||||
b = FF(b, c, d, a, m[3], 19)
|
||||
a = FF(a, b, c, d, m[4], 3)
|
||||
d = FF(d, a, b, c, m[5], 7)
|
||||
c = FF(c, d, a, b, m[6], 11)
|
||||
b = FF(b, c, d, a, m[7], 19)
|
||||
a = FF(a, b, c, d, m[8], 3)
|
||||
d = FF(d, a, b, c, m[9], 7)
|
||||
c = FF(c, d, a, b, m[10], 11)
|
||||
b = FF(b, c, d, a, m[11], 19)
|
||||
a = FF(a, b, c, d, m[12], 3)
|
||||
d = FF(d, a, b, c, m[13], 7)
|
||||
c = FF(c, d, a, b, m[14], 11)
|
||||
b = FF(b, c, d, a, m[15], 19)
|
||||
|
||||
a = GG(a, b, c, d, m[0], 3)
|
||||
d = GG(d, a, b, c, m[4], 5)
|
||||
c = GG(c, d, a, b, m[8], 9)
|
||||
b = GG(b, c, d, a, m[12], 13)
|
||||
a = GG(a, b, c, d, m[1], 3)
|
||||
d = GG(d, a, b, c, m[5], 5)
|
||||
c = GG(c, d, a, b, m[9], 9)
|
||||
b = GG(b, c, d, a, m[13], 13)
|
||||
a = GG(a, b, c, d, m[2], 3)
|
||||
d = GG(d, a, b, c, m[6], 5)
|
||||
c = GG(c, d, a, b, m[10], 9)
|
||||
b = GG(b, c, d, a, m[14], 13)
|
||||
a = GG(a, b, c, d, m[3], 3)
|
||||
d = GG(d, a, b, c, m[7], 5)
|
||||
c = GG(c, d, a, b, m[11], 9)
|
||||
b = GG(b, c, d, a, m[15], 13)
|
||||
|
||||
a = HH(a, b, c, d, m[0], 3)
|
||||
d = HH(d, a, b, c, m[8], 9)
|
||||
c = HH(c, d, a, b, m[4], 11)
|
||||
b = HH(b, c, d, a, m[12], 15)
|
||||
a = HH(a, b, c, d, m[2], 3)
|
||||
d = HH(d, a, b, c, m[10], 9)
|
||||
c = HH(c, d, a, b, m[6], 11)
|
||||
b = HH(b, c, d, a, m[14], 15)
|
||||
a = HH(a, b, c, d, m[1], 3)
|
||||
d = HH(d, a, b, c, m[9], 9)
|
||||
c = HH(c, d, a, b, m[5], 11)
|
||||
b = HH(b, c, d, a, m[13], 15)
|
||||
a = HH(a, b, c, d, m[3], 3)
|
||||
d = HH(d, a, b, c, m[11], 9)
|
||||
c = HH(c, d, a, b, m[7], 11)
|
||||
b = HH(b, c, d, a, m[15], 15)
|
||||
|
||||
ctx.state[0] += a
|
||||
ctx.state[1] += b
|
||||
ctx.state[2] += c
|
||||
ctx.state[3] += d
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
package md5
|
||||
|
||||
/*
|
||||
Copyright 2021 zhibog
|
||||
Made available under the BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
zhibog, dotbmp: Initial implementation.
|
||||
|
||||
Implementation of the MD5 hashing algorithm, as defined in RFC 1321 <https://datatracker.ietf.org/doc/html/rfc1321>
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
|
||||
import "../util"
|
||||
|
||||
/*
|
||||
High level API
|
||||
*/
|
||||
|
||||
DIGEST_SIZE :: 16
|
||||
|
||||
// hash_string will hash the given input and return the
|
||||
// computed hash
|
||||
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
|
||||
return hash_bytes(transmute([]byte)(data))
|
||||
}
|
||||
|
||||
// hash_bytes will hash the given input and return the
|
||||
// computed hash
|
||||
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
|
||||
hash: [DIGEST_SIZE]byte
|
||||
ctx: 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) -> ([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) -> ([DIGEST_SIZE]byte, bool) {
|
||||
if !load_at_once {
|
||||
return hash_stream(os.stream_from_handle(hd))
|
||||
} else {
|
||||
if buf, ok := os.read_entire_file(hd); ok {
|
||||
return hash_bytes(buf[:]), ok
|
||||
}
|
||||
}
|
||||
return [DIGEST_SIZE]byte{}, false
|
||||
}
|
||||
|
||||
hash :: proc {
|
||||
hash_stream,
|
||||
hash_file,
|
||||
hash_bytes,
|
||||
hash_string,
|
||||
hash_bytes_to_buffer,
|
||||
hash_string_to_buffer,
|
||||
}
|
||||
|
||||
/*
|
||||
Low level API
|
||||
*/
|
||||
|
||||
init :: proc(ctx: ^Md5_Context) {
|
||||
ctx.state[0] = 0x67452301
|
||||
ctx.state[1] = 0xefcdab89
|
||||
ctx.state[2] = 0x98badcfe
|
||||
ctx.state[3] = 0x10325476
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final :: 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
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
MD4 implementation
|
||||
*/
|
||||
|
||||
BLOCK_SIZE :: 64
|
||||
|
||||
Md5_Context :: struct {
|
||||
data: [BLOCK_SIZE]byte,
|
||||
state: [4]u32,
|
||||
bitlen: u64,
|
||||
datalen: u32,
|
||||
}
|
||||
|
||||
/*
|
||||
@note(zh): F, G, H and I, as mentioned in the RFC, have been inlined into FF, GG, HH
|
||||
and II respectively, instead of declaring them separately.
|
||||
*/
|
||||
|
||||
FF :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||
return b + util.ROTL32(a + ((b & c) | (~b & d)) + m + t, s)
|
||||
}
|
||||
|
||||
GG :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||
return b + util.ROTL32(a + ((b & d) | (c & ~d)) + m + t, s)
|
||||
}
|
||||
|
||||
HH :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||
return b + util.ROTL32(a + (b ~ c ~ d) + m + t, s)
|
||||
}
|
||||
|
||||
II :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u32 {
|
||||
return b + util.ROTL32(a + (c ~ (b | ~d)) + m + t, s)
|
||||
}
|
||||
|
||||
transform :: proc(ctx: ^Md5_Context, data: []byte) {
|
||||
i, j: u32
|
||||
m: [DIGEST_SIZE]u32
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
a := ctx.state[0]
|
||||
b := ctx.state[1]
|
||||
c := ctx.state[2]
|
||||
d := ctx.state[3]
|
||||
|
||||
a = FF(a, b, c, d, m[0], 7, 0xd76aa478)
|
||||
d = FF(d, a, b, c, m[1], 12, 0xe8c7b756)
|
||||
c = FF(c, d, a, b, m[2], 17, 0x242070db)
|
||||
b = FF(b, c, d, a, m[3], 22, 0xc1bdceee)
|
||||
a = FF(a, b, c, d, m[4], 7, 0xf57c0faf)
|
||||
d = FF(d, a, b, c, m[5], 12, 0x4787c62a)
|
||||
c = FF(c, d, a, b, m[6], 17, 0xa8304613)
|
||||
b = FF(b, c, d, a, m[7], 22, 0xfd469501)
|
||||
a = FF(a, b, c, d, m[8], 7, 0x698098d8)
|
||||
d = FF(d, a, b, c, m[9], 12, 0x8b44f7af)
|
||||
c = FF(c, d, a, b, m[10], 17, 0xffff5bb1)
|
||||
b = FF(b, c, d, a, m[11], 22, 0x895cd7be)
|
||||
a = FF(a, b, c, d, m[12], 7, 0x6b901122)
|
||||
d = FF(d, a, b, c, m[13], 12, 0xfd987193)
|
||||
c = FF(c, d, a, b, m[14], 17, 0xa679438e)
|
||||
b = FF(b, c, d, a, m[15], 22, 0x49b40821)
|
||||
|
||||
a = GG(a, b, c, d, m[1], 5, 0xf61e2562)
|
||||
d = GG(d, a, b, c, m[6], 9, 0xc040b340)
|
||||
c = GG(c, d, a, b, m[11], 14, 0x265e5a51)
|
||||
b = GG(b, c, d, a, m[0], 20, 0xe9b6c7aa)
|
||||
a = GG(a, b, c, d, m[5], 5, 0xd62f105d)
|
||||
d = GG(d, a, b, c, m[10], 9, 0x02441453)
|
||||
c = GG(c, d, a, b, m[15], 14, 0xd8a1e681)
|
||||
b = GG(b, c, d, a, m[4], 20, 0xe7d3fbc8)
|
||||
a = GG(a, b, c, d, m[9], 5, 0x21e1cde6)
|
||||
d = GG(d, a, b, c, m[14], 9, 0xc33707d6)
|
||||
c = GG(c, d, a, b, m[3], 14, 0xf4d50d87)
|
||||
b = GG(b, c, d, a, m[8], 20, 0x455a14ed)
|
||||
a = GG(a, b, c, d, m[13], 5, 0xa9e3e905)
|
||||
d = GG(d, a, b, c, m[2], 9, 0xfcefa3f8)
|
||||
c = GG(c, d, a, b, m[7], 14, 0x676f02d9)
|
||||
b = GG(b, c, d, a, m[12], 20, 0x8d2a4c8a)
|
||||
|
||||
a = HH(a, b, c, d, m[5], 4, 0xfffa3942)
|
||||
d = HH(d, a, b, c, m[8], 11, 0x8771f681)
|
||||
c = HH(c, d, a, b, m[11], 16, 0x6d9d6122)
|
||||
b = HH(b, c, d, a, m[14], 23, 0xfde5380c)
|
||||
a = HH(a, b, c, d, m[1], 4, 0xa4beea44)
|
||||
d = HH(d, a, b, c, m[4], 11, 0x4bdecfa9)
|
||||
c = HH(c, d, a, b, m[7], 16, 0xf6bb4b60)
|
||||
b = HH(b, c, d, a, m[10], 23, 0xbebfbc70)
|
||||
a = HH(a, b, c, d, m[13], 4, 0x289b7ec6)
|
||||
d = HH(d, a, b, c, m[0], 11, 0xeaa127fa)
|
||||
c = HH(c, d, a, b, m[3], 16, 0xd4ef3085)
|
||||
b = HH(b, c, d, a, m[6], 23, 0x04881d05)
|
||||
a = HH(a, b, c, d, m[9], 4, 0xd9d4d039)
|
||||
d = HH(d, a, b, c, m[12], 11, 0xe6db99e5)
|
||||
c = HH(c, d, a, b, m[15], 16, 0x1fa27cf8)
|
||||
b = HH(b, c, d, a, m[2], 23, 0xc4ac5665)
|
||||
|
||||
a = II(a, b, c, d, m[0], 6, 0xf4292244)
|
||||
d = II(d, a, b, c, m[7], 10, 0x432aff97)
|
||||
c = II(c, d, a, b, m[14], 15, 0xab9423a7)
|
||||
b = II(b, c, d, a, m[5], 21, 0xfc93a039)
|
||||
a = II(a, b, c, d, m[12], 6, 0x655b59c3)
|
||||
d = II(d, a, b, c, m[3], 10, 0x8f0ccc92)
|
||||
c = II(c, d, a, b, m[10], 15, 0xffeff47d)
|
||||
b = II(b, c, d, a, m[1], 21, 0x85845dd1)
|
||||
a = II(a, b, c, d, m[8], 6, 0x6fa87e4f)
|
||||
d = II(d, a, b, c, m[15], 10, 0xfe2ce6e0)
|
||||
c = II(c, d, a, b, m[6], 15, 0xa3014314)
|
||||
b = II(b, c, d, a, m[13], 21, 0x4e0811a1)
|
||||
a = II(a, b, c, d, m[4], 6, 0xf7537e82)
|
||||
d = II(d, a, b, c, m[11], 10, 0xbd3af235)
|
||||
c = II(c, d, a, b, m[2], 15, 0x2ad7d2bb)
|
||||
b = II(b, c, d, a, m[9], 21, 0xeb86d391)
|
||||
|
||||
ctx.state[0] += a
|
||||
ctx.state[1] += b
|
||||
ctx.state[2] += c
|
||||
ctx.state[3] += d
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user