Compare commits

...

462 Commits

Author SHA1 Message Date
gingerBill a04d849e30 core:container/lru 2022-02-01 15:24:37 +00:00
gingerBill 8c9505505a Add allocator parameter to rand.perm 2022-02-01 15:23:49 +00:00
gingerBill eac74631ec Correct debug information logic for procedure parameters 2022-02-01 14:11:19 +00:00
gingerBill 85706d559d Fix typo 2022-01-31 20:19:46 +00:00
gingerBill 67ba05cb7c Correct false positive check in check_unique_package_names 2022-01-31 19:33:02 +00:00
gingerBill 2f1aeaf757 Remove the unneeded $ 2022-01-31 15:56:40 +00:00
gingerBill 14a17fb36f Add slice.stable_sort* procedures 2022-01-31 15:55:52 +00:00
gingerBill 1a9ec776cb Unify logic for slice.sort* related procedures 2022-01-31 12:43:20 +00:00
gingerBill da1edac56d Enforce -strict-style in CI 2022-01-31 11:29:05 +00:00
gingerBill 44ec95a983 Fix all_main.odin 2022-01-30 21:55:55 +00:00
gingerBill 1502066303 Correct CI 2022-01-30 21:43:42 +00:00
gingerBill 35a826a0fd Update CI to do odin check examples/all 2022-01-30 21:38:34 +00:00
gingerBill ebb8ca7c26 Add round to linalg_glsl_math.odin 2022-01-30 21:35:05 +00:00
gingerBill 763de44853 Merge pull request #1461 from AquaGeneral/master
Added round to HLSL and GLSL, and isinf/isfinite + isnan to HLSL
2022-01-30 10:46:13 +00:00
Jesse Stiller 62cc752066 Added round to HLSL and GLSL, and isinf/isfinite + isnan to HLSL 2022-01-30 13:57:01 +10:00
gingerBill 965b962b29 Merge pull request #1459 from powerc9000/clay-macos-fixes
Fix mutex and conditions trying to be destroyed twice in unix
2022-01-28 00:24:29 +00:00
powerc9000 2f3c5336d9 Fix mutex and conditions trying to be destroyed twice in unix 2022-01-27 15:38:16 -07:00
gingerBill 3824937295 Remove debug code 2022-01-27 16:30:22 +00:00
gingerBill fc8ddcef5c Merge branch 'master' of https://github.com/odin-lang/Odin 2022-01-27 16:09:44 +00:00
gingerBill 3165b7cf95 Add rand.exp_float64 2022-01-27 16:09:33 +00:00
gingerBill 5eea23cf76 Fix typo 2022-01-27 16:09:05 +00:00
gingerBill 2aa783179e Update doc_format.odin 2022-01-27 16:08:58 +00:00
gingerBill 24e7356825 Add #no_type_assert and #type_assert to disable implicit type assertions with x.(T) 2022-01-27 16:08:47 +00:00
Jeroen van Rijn 2fcba25e50 Merge pull request #1458 from Kelimion/zlib_fix
Fix DEFLATE stored block handling.
2022-01-27 15:10:33 +01:00
Jeroen van Rijn 28bc274449 Fix DEFLATE stored block handling. 2022-01-27 14:58:45 +01:00
gingerBill 16786aac78 Correct int31_max etc 2022-01-27 12:33:34 +00:00
gingerBill 32b37f3429 Support built-in procedures for doc format 2022-01-27 00:08:05 +00:00
gingerBill 5808793cae Merge branch 'master' of https://github.com/odin-lang/Odin 2022-01-26 23:31:53 +00:00
gingerBill 7e11f3cc4b Update doc format to allow for aliases 2022-01-26 23:31:47 +00:00
gingerBill 714ab516c5 Merge pull request #1454 from jasonKercher/fix_segfault
avoid segfault in llvm_backend_general.cpp on map resize
2022-01-26 19:56:59 +00:00
CiD- 498f68c06b avoid segfault on map resize 2022-01-26 14:37:15 -05:00
gingerBill 070b450768 Add ODIN_ERROR_POS_STYLE constant and change runtime.print_caller_location based on that constant 2022-01-26 17:34:39 +00:00
gingerBill 74174eb4ae Remove spurious ) 2022-01-26 16:38:12 +00:00
gingerBill b190404b21 Fix double map dereference indexing 2022-01-26 16:37:16 +00:00
gingerBill 081a5a52a6 Add ODIN_ERROR_POS_STYLE environment variable
Allowing for two different error message styles:
default or odin
    path(line:column) message
unix
    path:line:column: message
2022-01-26 16:09:22 +00:00
gingerBill fb86c23dbd Keep -vet happy 2022-01-25 16:41:31 +00:00
gingerBill cb6a4ebf60 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-01-25 16:40:31 +00:00
gingerBill 1bf8328606 Strip unneeded semicolons 2022-01-25 16:40:25 +00:00
Jeroen van Rijn 6a7f39453b Merge pull request #1450 from Kelimion/bit_array
bit_array: Fix initial size.
2022-01-25 17:14:42 +01:00
Jeroen van Rijn 515fd2a228 bit_array: Fix initial size. 2022-01-25 17:08:32 +01:00
gingerBill dd3322ac1f Update all_main.odin to include all the crypto packages 2022-01-25 14:34:48 +00:00
gingerBill f16f1d932e Fix #1448 2022-01-25 14:24:15 +00:00
gingerBill a3e7b2baa1 Revert change 2022-01-25 12:42:45 +00:00
gingerBill fadf9b5309 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-01-25 12:39:12 +00:00
gingerBill f6a087775e Disable early return from check_proc_info 2022-01-25 12:39:06 +00:00
Jeroen van Rijn d5f7e181a0 Merge pull request #1449 from Kelimion/zlib_change
zlib: update Huffman builder.
2022-01-25 12:23:34 +01:00
Jeroen van Rijn 65f8722afc zlib: update Huffman builder. 2022-01-25 12:18:10 +01:00
gingerBill c0479f1564 Handle line comment better 2022-01-24 23:42:04 +00:00
gingerBill fe0b5bf4e2 Parse comments on enums fields 2022-01-24 23:28:59 +00:00
gingerBill f20105ddfe Update docs for packages fmt and io 2022-01-24 23:07:06 +00:00
gingerBill 42ab882db4 Remove debug code 2022-01-24 15:56:26 +00:00
gingerBill dcc9e61362 Correct string_append_token 2022-01-24 14:52:43 +00:00
gingerBill 2554c72bb2 Update CommentGroup parsing for struct types 2022-01-24 14:47:33 +00:00
gingerBill 49872e40dc Comment out calls 2022-01-24 14:46:56 +00:00
gingerBill 849fe01e70 Add lb_add_debug_local_variable call to procedure arguments 2022-01-24 14:13:24 +00:00
gingerBill d269dbcd40 Add comment for the internals of assert 2022-01-23 22:01:54 +00:00
gingerBill 18e639f59b Add strings.split_lines* procedures 2022-01-23 21:35:03 +00:00
gingerBill 6ad262c2df Migrate odin-html-docs to pkg.odin-lang.org repo 2022-01-23 16:52:08 +00:00
gingerBill 10b97a1b39 Update style.css 2022-01-23 16:36:37 +00:00
gingerBill 56b4e0a3c3 Fix #1267 2022-01-23 15:40:46 +00:00
gingerBill 27dbe84f79 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-01-23 09:53:56 +00:00
gingerBill 0711d4e5fe Correct bit_set printing 2022-01-23 09:53:50 +00:00
Jeroen van Rijn 1e46537959 Merge pull request #1443 from graphitemaster/patch-1
Fix fread definition
2022-01-23 09:39:43 +01:00
Dale Weiler a5e1693774 Fix fread definition 2022-01-23 03:12:59 -05:00
gingerBill 63771bc6e8 Merge pull request #1441 from Platin21/fix/multiple-llvm-versions
Makes it possible to use llvm 12 13 and 11 for building on macOS
2022-01-22 20:22:00 +00:00
Platin21 8516e2e7e3 Changed match to be arbitrary width
(cant do exact match as we never know how much subversion's llvm does)
2022-01-22 20:50:05 +01:00
gingerBill b3c3e41706 Correct directory comment printing 2022-01-22 19:49:46 +00:00
gingerBill 59f3a009fa Update header-lower.txt.html 2022-01-22 19:46:22 +00:00
Platin21 9bc5b84c4d Removes maybe unsupported versions 2022-01-22 20:40:04 +01:00
gingerBill f9265c14bf Update footer.txt.html 2022-01-22 19:37:35 +00:00
Platin21 9c1e1a63a2 Added pattern function so we can match whatever llvm versions we want
Right now we match every version that is 13 something for arm64 on macOS
And for x86 we allow any above 11 and including 11
2022-01-22 20:29:57 +01:00
gingerBill 4dc5839e3d Add header-lower.txt.html 2022-01-22 17:09:10 +00:00
gingerBill fdcb9deaff Generate core and vendor library collection; add package sidebar for the entire collection 2022-01-22 17:07:24 +00:00
gingerBill fe6539fad9 Add more to examples/all/all_vendor.odin 2022-01-22 17:03:55 +00:00
Platin21 0e06383620 Changed make file so that it allows for multiple LLVM versions instead of a single one on Darwin 2022-01-22 17:34:43 +01:00
gingerBill cfbc1a447b Correct inverse and inverse_transpose for 2x2 matrices 2022-01-20 13:18:37 +00:00
gingerBill 1b23dd2257 Add home page 2022-01-20 01:02:26 +00:00
gingerBill b612edba5a Improve detail styling 2022-01-20 00:36:44 +00:00
gingerBill d39c05b183 Simplify tag printing 2022-01-20 00:19:58 +00:00
gingerBill 3a3cb521ab Support tag printing 2022-01-20 00:19:24 +00:00
gingerBill 5b97ff0b48 General improves including comment formatting 2022-01-20 00:13:26 +00:00
gingerBill 2b918ada4b Add .Private information to doc-format 2022-01-19 17:15:10 +00:00
gingerBill b5754b6ed9 Print examples correctly 2022-01-19 16:35:50 +00:00
gingerBill 07ee23f817 Simplify dir tree generation 2022-01-19 16:28:01 +00:00
gingerBill ecdaac9921 Unify are_types_identical_unique_tuples 2022-01-19 15:14:15 +00:00
gingerBill 5ff82fc113 Correct tuple name checking for doc writing 2022-01-19 15:11:42 +00:00
gingerBill 28a816ef25 Allow for entity grouping in structs and procedure signatures with the Odin doc-format 2022-01-19 14:57:27 +00:00
gingerBill 6bdb210ad8 More improvements to the styling 2022-01-19 13:34:54 +00:00
gingerBill db08847f9a Improve rendering to match the main website's CSS 2022-01-19 13:20:38 +00:00
gingerBill 841c428273 Merge pull request #1433 from odin-lang/html-docs-printer
Initial Work on HTML Docs Printer
2022-01-18 11:03:41 +00:00
gingerBill 6b830f42b6 Improve stylization with collapsible directories; Fix name padding 2022-01-17 23:48:46 +00:00
gingerBill fb01dfe048 Improve docs_writer.cpp 2022-01-17 22:17:07 +00:00
gingerBill c7a9c8274f Improve type printing 2022-01-17 22:16:32 +00:00
gingerBill cafb6e5587 Correct //+private for odin doc 2022-01-17 21:33:20 +00:00
gingerBill e9ae6e20e8 Fix code, source code links, and add recursive make directory 2022-01-17 20:50:40 +00:00
gingerBill 2ca2dbcc92 Correct distinct printing 2022-01-17 19:23:24 +00:00
gingerBill 0d4642825f Correct package docs parsing 2022-01-17 19:07:25 +00:00
gingerBill 8eda756714 Add printing for constants, variables, types, and procedure groups 2022-01-17 19:01:16 +00:00
gingerBill c85ac955f7 Simplify docs to hide the copyright 2022-01-17 19:00:47 +00:00
gingerBill 97922406fe Improve printing for record types 2022-01-17 18:23:30 +00:00
gingerBill 76ccce2942 Begin work on a html doc printer 2022-01-17 17:57:55 +00:00
gingerBill 686dbb4421 Correct odin doc comment printing 2022-01-17 14:43:42 +00:00
gingerBill cd6898439e Comment out link_section on procedures 2022-01-17 12:17:13 +00:00
gingerBill 95620aaf2a Update examples/all 2022-01-17 11:48:15 +00:00
gingerBill 1d293749c2 Move core:path to core:path/slashpath
This is to reduce the confusion that occurs between that package and the `core:path/filepath` package
2022-01-17 11:38:15 +00:00
gingerBill 2d35a5c1af Merge pull request #1431 from AquaGeneral/master
Extraneous parameters in hlsl/glsl.saturate removed
2022-01-17 10:52:19 +00:00
Jesse Stiller d4ea02a877 Extraneous parameters in hlsl/glsl.saturate removed
This is a breaking change to anyone who used glsl/hlsl.saturate functions prior, but the y and z parameters never were used and do not conform to how the saturate function works in HLSL:  https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-saturate

Note however GLSL does not contain a saturate function, but removing it does not accomplish anything good IMO.
2022-01-17 12:19:06 +10:00
gingerBill 2a325b3da0 Update ODIN_ENDIAN usage 2022-01-16 12:25:39 +00:00
gingerBill f0529535e0 ODIN_ENDIAN changed to an enum constant; ODIN_ENUM_STRING is the new string version of the old constant 2022-01-15 17:53:18 +00:00
gingerBill 3f59c45740 Remove main creation in llvm_backend.cpp and have it done purely in the runtime package (partial bootstrapping) 2022-01-15 17:42:10 +00:00
gingerBill 29ebe0c3c9 Rename architecture 386 to i386 2022-01-15 17:40:00 +00:00
gingerBill 6c48670819 Make ODIN_BUILD_MODE a enum type 2022-01-15 17:34:35 +00:00
gingerBill 51dcbc80c3 Add LLVMAddMergedLoadStoreMotionPass on -debug -opt:0 2022-01-15 16:26:14 +00:00
gingerBill 9ecbadd457 Simplify procedure parameters callee logic 2022-01-15 16:16:11 +00:00
gingerBill 79f32d7b71 Remove unused lbDefer kind 2022-01-15 16:03:37 +00:00
gingerBill 7501cc2f17 Remove dead code 2022-01-15 16:01:23 +00:00
gingerBill a390ef41f8 Fix swizzle logic within lb_build_assign_stmt_array 2022-01-15 15:55:01 +00:00
gingerBill bb9c2f7aad Merge branch 'master' of https://github.com/odin-lang/Odin 2022-01-15 15:38:15 +00:00
gingerBill 6aa80ee8e4 Correct _start as an entry point 2022-01-15 15:38:09 +00:00
Jeroen van Rijn 0741bc37cc Merge pull request #1427 from oskarnp/macos-linker-fix
Fix invalid linker flags passed to clang on macOS
2022-01-14 17:05:28 +01:00
oskarnp c6ed3fa4b5 Fix invalid linker flags passed to clang on macOS 2022-01-14 10:48:41 -05:00
Jeroen van Rijn e277102947 Merge pull request #1426 from jasonKercher/fix_queue
fix push_back and pop_front
2022-01-14 16:45:54 +01:00
CiD- 6cf5371d7d fix push_back and pop_front 2022-01-14 10:17:49 -05:00
gingerBill e15f714660 Define wasm _start entry point in Odin code 2022-01-13 15:18:47 +00:00
gingerBill 4f77151ebc Merge pull request #1389 from ap29600/slice_scanner
Add slice/scanner proc
2022-01-13 14:10:17 +00:00
gingerBill 9a46463078 Merge pull request #1399 from kleeon/master
Fixed wrong function name in vendor/OpenGL/README.md
2022-01-13 12:06:53 +00:00
gingerBill a0816bb581 Merge pull request #1424 from jasonKercher/os_linux
os_linux additions + libc to syscalls
2022-01-13 12:06:40 +00:00
gingerBill b33ca6651e Rename proc_* to entry_* 2022-01-13 12:05:22 +00:00
gingerBill 315a08f33f Add main to proc_unix.odin 2022-01-13 12:04:42 +00:00
Jeroen van Rijn 50668fa7a6 Merge pull request #1425 from graphitemaster/more_linux_shared_library_fixes
Fixes for shared library initialization and finalization
2022-01-13 06:36:19 +01:00
Dale Weiler ee260986a9 more fixes 2022-01-13 00:19:04 -05:00
gingerBill c9bc759624 Correct calling convention 2022-01-12 23:04:31 +00:00
gingerBill 80f175cdb0 Add empty main dynamic builds for *nix systems 2022-01-12 20:40:34 +00:00
gingerBill 8f03811842 Fix typo 2022-01-12 20:30:34 +00:00
gingerBill 3def94505e Add dynamic to error message for -build-mode 2022-01-12 20:28:11 +00:00
gingerBill e30f16b1f3 Correct -init for *nix 2022-01-12 20:17:30 +00:00
gingerBill 7df93ea504 Initialize runtime.args__ through main 2022-01-12 20:16:46 +00:00
gingerBill 6209b02bf9 Add intrinsics._entry_point call to _odin_entry_point 2022-01-12 20:16:04 +00:00
gingerBill 75b7f2b9fe Correct -init for *nix to be a different procedure 2022-01-12 20:13:38 +00:00
gingerBill f1521aa980 Add proc_windows.odin for custom entry points 2022-01-12 20:10:23 +00:00
gingerBill fb0a3ab7c1 Correct linkage for entry point procedures on Windows 2022-01-12 20:07:17 +00:00
CiD- 8eaafd5242 check correct errno in _readlink 2022-01-12 14:51:49 -05:00
CiD- 774951e8c0 os_linux additions + libc to syscalls 2022-01-12 14:36:18 -05:00
gingerBill 5ec93677a0 Correct look for entry point in llvm backend (Windows only currently) 2022-01-12 19:27:49 +00:00
gingerBill 7e4067c44c Begin work to move entry point code to Odin itself rather than in C++ side 2022-01-12 19:19:43 +00:00
gingerBill f2f6c3c67d Merge branch 'master' of https://github.com/odin-lang/Odin 2022-01-11 10:56:13 +00:00
gingerBill 847b05013f Disable DEFAULT_TO_THREADED_CHECKER until race condition is found 2022-01-11 10:56:07 +00:00
gingerBill d308473075 Merge pull request #1421 from graphitemaster/linux_shared_library_fixes
Linux shared library fixes
2022-01-10 23:18:04 +00:00
Dale Weiler 4334dbe69a disable this warning 2022-01-10 18:00:38 -05:00
Dale Weiler 8f91e9307c shared library fixes 2022-01-10 17:57:33 -05:00
gingerBill 32ec1162bf Use more {} ctor 2022-01-10 14:52:47 +00:00
gingerBill 7cc265e14c Add mutex guards for signature scopes 2022-01-10 14:50:28 +00:00
gingerBill 6f3e450c50 Move error handling code to a separate file 2022-01-10 14:03:36 +00:00
gingerBill cb1080d56c Fix check_procedure_bodies to allow multiple threads caused by a typo 2022-01-10 13:31:34 +00:00
gingerBill 80bd1eb615 Fix polymorphic matrix element with a minor hack 2022-01-10 12:19:49 +00:00
gingerBill fb53402914 Merge pull request #1402 from jasonKercher/syscalls
add more Linux syscalls
2022-01-10 11:50:45 +00:00
gingerBill 731853ce78 Merge pull request #1416 from Naboris/utf16-typo
fix typo in utf16 decode_to_utf8
2022-01-10 11:50:04 +00:00
gingerBill f0260e9771 Merge pull request #1420 from odin-lang/linalg-to-use-matrix-type
Update matrix types to be the native Odin `matrix` types
2022-01-10 11:49:49 +00:00
gingerBill af612bc7e9 Update matrix types to be the native Odin matrix types 2022-01-10 11:32:27 +00:00
Jeroen van Rijn d76dd95c0b Merge pull request #1418 from wbogocki/patch-1
Fix link to Odin blog
2022-01-09 16:10:50 +01:00
Wojciech Bogócki 1cff72ad62 Fix link to Odin blog 2022-01-09 22:43:12 +08:00
Naboris 773cfac449 fix typo 2022-01-08 09:49:21 +01:00
Jeroen van Rijn b02f2953ac Merge pull request #1410 from Kelimion/sort_map
Fix unused imports.
2022-01-07 06:47:54 +01:00
Jeroen van Rijn 566a750899 Fix unused imports. 2022-01-07 06:12:00 +01:00
Jeroen van Rijn 1d1d684cbc Merge pull request #1407 from Platin21/feature/fix-supervisor-call
Fix Syscall Intrinsic on ARM64 MacOS
2022-01-05 17:02:24 +01:00
Platin21 7a14acaa01 Fixes syscall intrinsic on macOS they use a slightly different section + register for the id 2022-01-05 16:49:58 +01:00
gingerBill 057174497a Merge pull request #1406 from terickson001/schar
add schar to core:c and core:c/libc
2022-01-05 00:50:24 +00:00
Tyler Erickson 8c9597b24b add schar to core:c and core:c/libc 2022-01-04 16:45:16 -08:00
gingerBill 72862ce30d Fix minor typo in c/frontend/preprocess 2022-01-04 11:48:18 +00:00
gingerBill d0f4cb1de4 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-01-04 11:47:05 +00:00
gingerBill 17613185e7 Support struct field tags in odin doc format 2022-01-04 11:44:34 +00:00
Jeroen van Rijn 00ff1fbca2 Merge pull request #1403 from Platin21/feature/fix-open-syscall
Fixes open system call (Thanks TIM!)
2022-01-03 20:48:09 +01:00
gingerBill f15bb0b424 Fix quaternion casting 2022-01-03 19:45:27 +00:00
gingerBill f818d0feb1 Fix #1344 2022-01-03 19:43:22 +00:00
Platin21 8ff6f95571 Removes the default create flag 2022-01-03 20:40:56 +01:00
Platin21 68e5f57e27 Fixes open system call (Thanks TIM!) 2022-01-03 20:34:57 +01:00
CiD- 38e5e13b3f add more Linux syscalls 2022-01-03 09:24:39 -05:00
gingerBill defc1672c3 Revert fix #1344 2022-01-03 13:48:12 +00:00
gingerBill 12f459b5fb Fix #1344 2022-01-03 13:12:39 +00:00
gingerBill e6b8f7e77a Fix #1398 2022-01-03 12:54:31 +00:00
gingerBill 236b08cb49 Fix #1356 2022-01-03 12:51:32 +00:00
gingerBill e4f28de3de Fix #1311 2022-01-03 12:14:01 +00:00
gingerBill 6543491148 Clean up code for queue (no logic changed) 2022-01-02 15:31:47 +00:00
gingerBill 3cbf9c3719 Fix #1381 2022-01-02 14:45:39 +00:00
gingerBill 50188f0308 Add sort.map_entries_by_key sort.map_entries_by_value 2022-01-01 17:13:11 +00:00
gingerBill a60b9735a2 Add core:container/queue 2022-01-01 15:46:22 +00:00
gingerBill a032a2ef32 Remove the hidden NUL byte past the end from bytes.clone 2022-01-01 15:33:19 +00:00
gingerBill f364ac60c2 Remove the hidden NUL byte past the end from strings.clone 2022-01-01 15:31:51 +00:00
gingerBill 43763ddfda Correct _shift_down logic 2022-01-01 13:44:37 +00:00
gingerBill 70ed280c5a Fix typo in priority_queue.odin and add default_swap_proc 2022-01-01 13:11:53 +00:00
gingerBill 0d7cb02386 Fix conversion from float to quaternion 2021-12-31 23:20:14 +00:00
gingerBill bdf66bb1e1 Correct abs type behaviour for quaternions 2021-12-31 22:54:12 +00:00
Jeroen van Rijn 9b5cfe2677 Merge pull request #1401 from zhibog/extend-crypto-api
Extended crypto Hash API by variants that write the result into a destinat…
2021-12-31 13:27:56 +01:00
zhibog 42033ea808 Extended crypto API by variants that write the result into a destination buffer, instead of returning it 2021-12-31 13:16:11 +01:00
gingerBill c7ff296bef Change the implementation of Priority_Queue to have a better interface that allows for a less and swap procedure 2021-12-30 13:42:10 +00:00
kleeon 750ee4ecdb Fixed wrong function name in README.md 2021-12-30 15:49:07 +03:00
gingerBill ed742846cb Correct lb_emit_ptr_offset bug caused by LLVMConstGEP assuming a signed index 2021-12-29 15:01:56 +00:00
gingerBill ed8b20da78 Add core:container/priority_queue 2021-12-29 14:38:39 +00:00
gingerBill c987b84292 Move bash.djbx33a to hash.odin 2021-12-29 12:24:47 +00:00
gingerBill a9b17b5a37 Add hash.djbx33a 2021-12-29 12:01:07 +00:00
gingerBill a66f859fb4 Minor improvements to core:container/small_array 2021-12-29 11:58:27 +00:00
gingerBill c46e7eda1d Add core:container/small_array 2021-12-29 11:26:22 +00:00
Andrea Piseri 92e70b9a58 use multipointers instead of simple pointers 2021-12-28 16:22:34 +01:00
Andrea Piseri 822da9d12d Merge branch 'master' into slice_scanner 2021-12-28 16:12:15 +01:00
Jeroen van Rijn b0817d136b Merge pull request #1354 from Kelimion/bit_vector
[core:container/bit_array] Create new package.
2021-12-28 15:51:45 +01:00
Jeroen van Rijn 53e30e4621 [core:container/bit_vector] Create new package.
A dynamic bit array, optionally allowing negative indices.
2021-12-28 15:38:12 +01:00
gingerBill dbf42d2469 make slice.as_ptr return [^]E 2021-12-28 14:16:27 +00:00
gingerBill 36c61aeacf Merge pull request #1350 from thePHTest/json-typo
Fix 'unmarsal' typo in core/encoding/json/unmarshal.odin
2021-12-28 14:11:27 +00:00
gingerBill 289b0422bd Merge pull request #1372 from ryuukk/patch-1
[WASM] Added missing zoffset parameters to some gl functions
2021-12-28 14:11:11 +00:00
gingerBill 78359f0c16 Merge pull request #1379 from weshardee/master
___$startup_runtime for MacOS
2021-12-28 14:09:42 +00:00
gingerBill 3f8c6a6745 Merge pull request #1396 from Platin21/feature/fix-matrix-return
Fixes Matrix/Float return on Arm64
2021-12-28 14:07:13 +00:00
gingerBill 5d653a9d8e Merge branch 'master' of https://github.com/odin-lang/Odin 2021-12-28 14:05:18 +00:00
gingerBill 7f61a90ea1 Remove core:container contents 2021-12-28 14:05:09 +00:00
Platin21 982ec1e58b Merge remote-tracking branch 'origin/master' into feature/fix-matrix-return 2021-12-27 22:11:08 +01:00
Platin21 86f831ddd1 This adds code which checks how big the return is and if it is to big returns the value via sret 2021-12-27 22:10:52 +01:00
Jeroen van Rijn 6f370fdbf2 Merge pull request #1394 from Tetralux/parse-allman-for
core:odin/parser: Fix parsing of Allman style braces in for loops
2021-12-25 20:24:30 +01:00
Tetralux a60667e900 core:odin/parser: Fix parsing of Allman style braces in for loops 2021-12-25 19:18:29 +00:00
Jeroen van Rijn 6889cb6fe2 Merge pull request #1393 from Tetralux/odin-parse-no-nil
core:odin/parser: Parse #no_nil on unions
2021-12-25 20:12:01 +01:00
Tetralux 9b2fe56d14 Parse #no_nil on unions 2021-12-25 18:58:08 +00:00
Andrea Piseri 5d80e24224 Add slice/scanner proc 2021-12-23 12:49:40 +01:00
Jeroen van Rijn eec61c3f6f Merge pull request #1388 from Yawning/feature/linux-aarch64
src: Add preliminary support for Linux AArch64
2021-12-23 04:17:42 +01:00
Yawning Angel dce120258f src: Add preliminary support for Linux AArch64
Tested via `tests/core`, on a Raspberry Pi 4 running the latest
64-bit Raspberry Pi OS image (LLVM 11).
2021-12-23 02:46:32 +00:00
gingerBill 5752a374ab Merge pull request #1386 from Platin21/feature/fix-arm64
Removes unneeded lookups / Adds sret to call site which fixes the mac…
2021-12-23 01:06:39 +00:00
Platin21 8dbeed8a9f Removes unneeded lookups / Adds sret to call site which fixes the mac bug 2021-12-23 01:59:31 +01:00
gingerBill 84d774c7b4 Merge pull request #1382 from Tetralux/rename-to-dynamic
Rename slice.to_dynamic to slice.clone_to_dynamic
2021-12-21 10:26:36 +00:00
Tetralux e2b36c4004 Rename slice.to_dynamic to slice.clone_to_dynamic 2021-12-21 02:17:24 +00:00
gingerBill 8453a6cbdb Merge pull request #1380 from Platin21/feature/llvm-version-check
Adds version check for Apple Silicon for LLVM Version
2021-12-19 21:15:54 +00:00
Platin21 3e465c7e84 Changes to required llvm version 13 as both 12 and 11 don't work correctly on macOS Apple Silicon 2021-12-19 21:51:51 +01:00
Wes Hardee 92ce7defb1 Merge branch 'master' of https://github.com/weshardee/Odin 2021-12-18 12:43:33 -06:00
Wes Hardee a48317deee use '___$startup_runtime' for MacOS
MacOS needs 3 underscores unlike the 2 needed by Linux.
2021-12-18 12:43:24 -06:00
gingerBill 0548db4230 Disallow @(static) and @(thread_local) within defer statements 2021-12-17 11:06:17 +00:00
gingerBill aba6d2e52c Merge pull request #1374 from indiscible/fix-mathprod
fix math.prod
2021-12-16 18:34:10 +00:00
gilles 4ebdb6740e fix math.prod
accumulator was not initialized to one
2021-12-16 18:55:51 +01:00
ryuukk d0240b8981 [WASM] Added missing zoffset parameters to some gl functions 2021-12-15 06:12:26 +01:00
gingerBill 4423bc0706 Fix typo 2021-12-12 01:10:40 +00:00
gingerBill 8c72813b85 Merge pull request #1364 from RehkitzDev/fix-webgl-glue
Fix storeInt call in webgl glue code
2021-12-11 17:52:45 +00:00
gingerBill 08a081ed45 Improve debug symbol retention with -debug -opt:0 2021-12-11 17:42:58 +00:00
Rehkitzdev b7c78da1fb Fix storeInt call in webgl glue code 2021-12-11 18:38:32 +01:00
Jeroen van Rijn 3257454209 Merge pull request #1363 from Kelimion/big_math
[math/big] Rename `internal_int_shl_digit` to `_private_int_shl_leg`.
2021-12-11 15:31:43 +01:00
Jeroen van Rijn 938744b276 [math/big] Rename internal_int_shl_digit to _private_int_shl_leg.
Same for the SHR variant. These are pure implementation details to shift by a leg/word at a time.
Prevent accidental usage.
2021-12-11 15:22:24 +01:00
gingerBill 84b84d9f7d Fix rat_set_f64 2021-12-11 12:47:05 +00:00
gingerBill 85f8c8df91 Fix fields_proc in strings and bytes 2021-12-11 12:04:34 +00:00
gingerBill c889591333 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-12-11 12:02:33 +00:00
gingerBill e2f53ee107 Fix #1362 strings.index_any 2021-12-11 12:02:23 +00:00
Jeroen van Rijn c771ea9794 Merge pull request #1358 from Kelimion/big_math_fix
[math/big] Return 0, .Integer_Underflow if T = unsigned and bigint is negative.
2021-12-09 16:41:37 +01:00
gingerBill 94bad4d786 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-12-09 15:36:12 +00:00
gingerBill 1d7c9cf872 Make strconv more robust 2021-12-09 15:35:00 +00:00
gingerBill 1e17d5d86f Add utility procedures to get low values 2021-12-09 15:34:35 +00:00
gingerBill 1e9b30666f Minor style change 2021-12-09 15:34:17 +00:00
Jeroen van Rijn b2b79b86f0 [math/big] Return 0, .Integer_Underflow if trying to get a negative number to an unsigned int. 2021-12-09 16:31:54 +01:00
Jeroen van Rijn 3d85013aba Merge pull request #1357 from Kelimion/big_math_fix
[math/big] Fix int_set and int_get.
2021-12-09 16:22:09 +01:00
Jeroen van Rijn c94098c2ab [math/big] Fix int_set and int_get. 2021-12-09 16:14:04 +01:00
gingerBill 9d4fe90356 Fix bugs in big.Rat caused by typos 2021-12-07 17:35:41 +00:00
Phil H a7138b22a5 Fix 'unmarsal' typo 2021-12-05 19:04:14 -08:00
gingerBill 6ce5608003 Correct odin doc default parameter value init_string generation 2021-12-03 11:46:54 +00:00
Jeroen van Rijn db42a2db47 Merge pull request #1347 from DanielGavin/parser-fix
Do not save comments when peeking tokens.
2021-12-02 23:05:24 +01:00
DanielGavin cecca96f3d Merge branch 'odin-lang:master' into parser-fix 2021-12-02 22:45:14 +01:00
Daniel Gavin f1a126e162 Do not save the comment when peeking. 2021-12-02 22:44:47 +01:00
gingerBill 9f0a30e36e Merge pull request #1337 from DanielGavin/parser-fix
Add Matrix_Type as literal type on "core:odin"
2021-11-28 10:38:37 +00:00
Daniel Gavin 517c8ff1dd Include Matrix_Type to the is_literal_type switch statement. 2021-11-28 02:14:25 +01:00
gingerBill 2b07afaf70 Add lb_build_addr on or_return and or_else for sanity's sake 2021-11-27 16:03:03 +00:00
gingerBill 6616882708 Correct reading from a console on Windows
e.g. `os.read(os.stdin, buf[:])`
2021-11-27 14:59:35 +00:00
gingerBill c9c197ba08 Add os.read_at_least and os_read_full utility procedures. 2021-11-27 14:57:49 +00:00
gingerBill 7876660d8c Add new utf16 procedures: decode, decode_to_utf8 2021-11-27 14:57:20 +00:00
gingerBill db9326f31d Merge pull request #1332 from odin-lang/nasm-support
NASM Support
2021-11-26 23:06:33 +00:00
gingerBill 27106dd9ae Allow .asm, .s, and .S as valid assembly file extensions 2021-11-26 22:25:07 +00:00
gingerBill 33dc12a61a Add supported check for .asm files 2021-11-26 14:46:03 +00:00
gingerBill ffd7ca57f1 Move nasm.exe to windows/nasm.exe, etc 2021-11-26 14:40:39 +00:00
gingerBill 44897b5eac Merge pull request #1334 from jockus/allow-enum-any-int
Allow enums to pass #any_int checks
2021-11-25 11:31:04 +00:00
Joakim Hentula 8255481204 Allow enums to pass #any_int checks 2021-11-25 11:20:40 +00:00
gingerBill 1e453cf1d7 Merge pull request #1296 from kevinsjoberg/do-not-filter-tests-when-empty
Do not filter test procedures when filter is empty
2021-11-25 09:13:31 +00:00
gingerBill c34a331696 Add -extra-assembler-flags 2021-11-24 22:20:18 +00:00
gingerBill 07ec93bfeb Add procs_windows_amd64.asm for use with -no-crt 2021-11-24 18:32:27 +00:00
gingerBill 994ee5a559 Allow for multiple .asm files 2021-11-24 17:57:31 +00:00
gingerBill 50057b0696 Add basic support for foreign import "foo.asm" on Windows with nasm.exe 2021-11-24 16:56:42 +00:00
gingerBill 00597127dd Add missing field skip_missing 2021-11-24 16:39:29 +00:00
gingerBill 70d4bc8573 Add nasm binaries 2021-11-24 16:36:34 +00:00
gingerBill bc775afccb Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-24 16:31:47 +00:00
gingerBill 504ea7deeb Fix update_untyped_expr_type for ternary if expressions with an untyped type 2021-11-24 16:31:37 +00:00
gingerBill 5e2280a787 Fix set_file_path_string and thread_safe_set_ast_file_from_id 2021-11-24 16:20:01 +00:00
gingerBill 84e03421d3 Merge pull request #1312 from DYSEQTA/master
Improve compiler help output with regard to command specific help.
2021-11-24 15:49:49 +00:00
DYSEQTA 0a87ffe0e6 Merge branch 'odin-lang:master' into master 2021-11-24 12:07:14 +11:00
DYSEQTA e5f961b48f Removed '--help' from help string as per request. 2021-11-24 11:10:40 +11:00
gingerBill 5db505c42f Merge pull request #1277 from Yawning/feature/modern-crypto
core/crypto: Add some "modern" primitives
2021-11-23 17:54:03 +00:00
gingerBill 275241f9b4 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-23 11:43:38 +00:00
gingerBill 9246e89c4a Fix #1328 2021-11-23 11:43:32 +00:00
gingerBill b56964e465 Merge pull request #1315 from SrMordred/patch-2
GetMouseDelta
2021-11-23 11:30:54 +00:00
gingerBill 2e89585c8c Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-23 10:59:50 +00:00
gingerBill e230b7110c Merge pull request #1327 from graphitemaster/fix-path-join-leak
fix memory leak in path.join
2021-11-22 15:34:30 +00:00
Dale Weiler a55f0cfb63 fix memory leak in path.join 2021-11-22 10:25:54 -05:00
gingerBill de435c9318 Remove unneeded semicolons from vendor:OpenGL 2021-11-21 14:52:40 +00:00
gingerBill f40f12d480 Minor cleanup to math constants 2021-11-21 14:06:32 +00:00
gingerBill 8a2c829e07 Patch odin doc binary format 2021-11-21 14:06:15 +00:00
gingerBill 42b9ce636f Remove #force_inline from all wrappers 2021-11-21 13:59:28 +00:00
gingerBill ca6951d05e Add MessageDecompose; Update the static library 2021-11-20 20:20:12 +00:00
gingerBill 446f1f6183 Correct foreign imports for portmidi on Windows 2021-11-20 19:27:34 +00:00
gingerBill d424c84bf9 Merge pull request #1322 from Gaunsessa/master
Add darwin support for glfw and re-add ln for js.
2021-11-20 12:22:38 +00:00
Gus 56d2bbc5b9 Added back ln for js 2021-11-20 20:03:54 +11:00
Gus 2c7bf87998 Added darwin support 2021-11-20 20:02:21 +11:00
gingerBill daebaa8b50 Fix #1319 2021-11-19 15:43:13 +00:00
gingerBill 9320a31f4d Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-19 12:26:19 +00:00
gingerBill 3e04b45106 Allow cast from float to complex 2021-11-19 12:26:10 +00:00
gingerBill acd5878d66 Merge pull request #1316 from Skytrias/master
add `builtin.` in slice.swap_between
2021-11-18 23:48:43 +00:00
Michael Kutowski 4439d59105 add builtin. 2021-11-19 00:24:56 +01:00
gingerBill 12c1291805 Add optional seed parameters to all hashes 2021-11-18 16:14:33 +00:00
Patric Dexheimer 61bc963e92 GetMouseDelta 2021-11-17 19:03:01 -03:00
gingerBill ae59f214ee @(tag=<string>) - dummy attribute for tooling 2021-11-17 21:32:33 +00:00
Yawning Angel 6bafa21bee crypto: Add rand_bytes
This adds `rand_bytes(dst: []byte)` which fills the destination buffer
with entropy from the cryptographic random number generator.  This takes
the "simple is best" approach and just directly returns the OS CSPRNG
output instead of doing anything fancy (a la OpenBSD's arc4random).
2021-11-17 14:00:00 +00:00
Yawning Angel 61c581baeb core/sys/unix: Add syscalls_linux.odin
Linux is in the unfortunate situation where the system call number is
architecture specific.  This consolidates the system call number
definitions in a single location, adds some wrappers, and hopefully
fixes the existing non-portable invocations of the syscall intrinsic.
2021-11-17 14:00:00 +00:00
Yawning Angel 6c4c9aef61 core/crypto: Add chacha20poly1305
This package implements the chacha20poly1305 AEAD construct as specified
in RFC 8439.
2021-11-17 13:59:53 +00:00
Yawning Angel 7bed317636 core/crypto: Add chacha20
This package implements the ChaCha20 stream cipher as specified in
RFC 8439, and the somewhat non-standard XChaCha20 variant that supports
a 192-bit nonce.

While an IETF draft for XChaCha20 standardization exists,
implementations that pre-date the draft use a 64-bit counter, instead of
the IETF-style 32-bit one.  This implementation opts for the latter as
compatibility with libsodium is more important than compatibility with
an expired IETF draft.
2021-11-17 13:59:53 +00:00
Yawning Angel 4647081f49 core/crypto/poly1305: Triple performance on amd64 with -o:speed 2021-11-17 13:59:53 +00:00
Yawning Angel 64db286582 core/crypto: Add poly1305
This package implements the Poly1305 MAC algorithm as specified in RFC
8439, using routines taked from fiat-crypto and poly1305-donna.
2021-11-17 13:59:53 +00:00
Yawning Angel 1a7a6a9116 core/crypto: Add x25519
This package implements the X25519 key agreement scheme as specified in
RFC 7748, using routines taken from fiat-crypto and Monocypher.
2021-11-17 13:59:53 +00:00
Yawning Angel d1e76ee4f2 core/crypto: Add constant-time memory comparison routines
Using a constant-time comparison is required when comparing things like
MACs, password digests, and etc to avoid exposing sensitive data via
trivial timing attacks.

These routines could also live under core:mem, but they are somewhat
specialized, and are likely only useful for cryptographic applications.
2021-11-17 13:59:53 +00:00
gingerBill 9be0d18e5d Correct x in ptr logic 2021-11-17 11:02:11 +00:00
gingerBill e877525073 Keep -vet happy for -no-crt and wasm targets 2021-11-17 10:40:55 +00:00
gingerBill f09638318f Add support for darwin to core:c/libc 2021-11-16 21:19:08 +00:00
gingerBill bb7703fcec Improve ptr_map_hash_key 2021-11-16 16:08:20 +00:00
gingerBill 1b28226a67 Add math.lgamma based off FreeBSD's /usr/src/lib/msun/src/e_lgamma_r.c 2021-11-16 15:32:32 +00:00
gingerBill 2b546a598c Add math.signbit; Add math.gamma based on http://netlib.sandia.gov/cephes/cprob/gamma.c 2021-11-16 15:23:19 +00:00
gingerBill b530ca9a5e Add math.nextafter 2021-11-16 15:12:01 +00:00
gingerBill d232796149 Fix typo 2021-11-16 15:09:47 +00:00
gingerBill e721f26a76 Implement ln based off FreeBSD's /usr/src/lib/msun/src/e_log.c 2021-11-16 15:05:04 +00:00
gingerBill 91408cb21f Implement atanh based on FreeBSD's /usr/src/lib/msun/src/e_atanh.c 2021-11-16 14:58:59 +00:00
gingerBill eb8b0d7a03 Add log1p, erf, erfc, ilogb logb (implemented based of FreeBSD's) 2021-11-16 14:54:57 +00:00
gingerBill 880af47ae7 Rename math_js.odin to math_basic_js.odin 2021-11-16 14:26:04 +00:00
gingerBill 91949b0992 Implement math.sqrt with intrinsics.sqrt 2021-11-16 14:11:20 +00:00
gingerBill 6a101e69a2 Implement ldexp and frexp in native Odin 2021-11-16 14:04:49 +00:00
cybermancer 1823b0cead Improve compiler help output with regard to command specific help. 2021-11-16 15:15:21 +11:00
gingerBill 1ec0b79345 Allow both -help and --help if passed as init_filename 2021-11-15 22:10:31 +00:00
gingerBill e814a3693f Improve usage of file_id 2021-11-15 17:26:01 +00:00
gingerBill f55fc4cd08 Add complex32 and quaternion64 for the 16-bit float types to fmt 2021-11-15 17:25:29 +00:00
gingerBill f47311f2f6 Remove scope field from Ast 2021-11-14 15:22:40 +00:00
gingerBill 3f038428a7 Begin minimizing Ast size 2021-11-14 15:12:37 +00:00
gingerBill b9701340b8 Add linalg.matrix4_look_at_from_fru 2021-11-13 19:15:37 +00:00
gingerBill 82110bf487 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-13 19:07:27 +00:00
gingerBill a75dc9d86d Fix minor issue with unmarshal for booleans 2021-11-13 19:07:16 +00:00
gingerBill bfa23f1352 Merge pull request #1308 from Yawning/fix/amd64-syscalls
src: Fix the syscall intrinsic code generation for Linux and Windows
2021-11-13 18:54:12 +00:00
Yawning Angel c430a82721 src: Fix the syscall intrinsic code generation for Linux and Windows
The old assembly generated for the syscall intrinsic did not specify
clobber constraints.  This adds RCX and R11 (that are clobbered by
the instruction itself), and memory (that is clobbered by some
system calls) to the assembly constraints.

Note: This is still incorrect on FreeBSD, which clobbers more registers
and uses the carry flag instead of -errno in rax to indicate an error.
2021-11-13 09:53:20 +00:00
Jeroen van Rijn cc316a473e Merge pull request #1299 from Kelimion/vendor-glfw-test
[vendor:glfw] Add test.
2021-11-10 19:24:55 +01:00
Jeroen van Rijn c213274607 [vendor:glfw] Add test. 2021-11-10 19:15:10 +01:00
Jeroen van Rijn c4a2580dfd Merge pull request #1290 from wjlroe/fix-glfw-on-windows
Fix path to static GLFW lib on Windows
2021-11-10 18:41:30 +01:00
Jeroen van Rijn 8a547b5922 Merge pull request #1298 from CarwynNelson/ws32-add-socket
Add socket() function to windows ws32 bindings
2021-11-10 17:23:59 +01:00
Carwyn Nelson c67c0789eb Add socket() function to windows ws32 bindings
It looks like this was missing from the winsock bindings. Odin contains
WSASocketW which I assume would also work for obtaining a socket, but
socket() is distinct and is what I was using, so I assume others will
want it too.
2021-11-10 15:55:50 +00:00
Jeroen van Rijn cefe312ba1 Merge pull request #1297 from CarwynNelson/patch-1
Fix the windows binding for getaddrinfo
2021-11-10 16:22:56 +01:00
Carwyn Nelson d8b1523161 Fix the windows binding for getaddrinfo
getaddrinfo should take a double pointer to ADDRINFOA instead of a single pointer. If you call the binding in its current state you will not get back a valid ADDRINFOA struct.

I have also changed the `node` and `service` params to be cstring to avoid having to do `transmute(u8) value`.
2021-11-10 15:15:40 +00:00
Kevin Sjöberg 61b02adc50 Do not filter test procedures when filter is empty
If `build_context.test_names` is empty, we do not need to perform any
filtering.
2021-11-10 15:49:23 +01:00
Jeroen van Rijn 989ddbd688 Merge pull request #1295 from zhibog/master
Add tests to Linux and Mac and add vendor tests
2021-11-10 15:34:29 +01:00
zhibog 96b670af49 Fix package name again 2021-11-10 15:31:29 +01:00
zhibog 359e02bad7 Fix botan lib name for apt 2021-11-10 15:26:26 +01:00
zhibog 8aadcacc0b Add tests to Linux and Mac and add vendor tests 2021-11-10 15:22:12 +01:00
Jeroen van Rijn 615efc7c86 Merge pull request #1294 from Kelimion/fix_dir_walk
Fix os.walk for UNC paths.
2021-11-10 15:09:22 +01:00
Jeroen van Rijn dd88104a81 Fix os.walk for UNC paths. 2021-11-10 14:59:54 +01:00
gingerBill 5cb23725ae Merge pull request #1289 from Kelimion/timings-export
Add functionality to export build timings.
2021-11-10 12:06:36 +00:00
Jeroen van Rijn 8c5c45a04c [timings-export] Style fixes. 2021-11-10 12:23:46 +01:00
gingerBill 4a552e6326 Merge pull request #1286 from DanielGavin/parser-fix
Add Any_Int as allowed flag in field signatures.
2021-11-10 10:59:53 +00:00
gingerBill 1f0758708f Merge pull request #1293 from kevinsjoberg/fix-test-filtering
Postpone checking test procedures
2021-11-10 10:52:01 +00:00
Kevin Sjöberg b8dec4268d Postpone checking test procedures
The dependency set need to be generated before we check the testing
procedures. Otherwise `checker->info.testing_procedures` will be empty
and thus no filtering is taking place.
2021-11-10 10:26:17 +01:00
gingerBill fc920a630f Merge pull request #1288 from odin-lang/target-js_wasm32
Target `js_wasm32` with `vendor:wasm/WebGL`
2021-11-09 23:15:42 +00:00
Jeroen van Rijn ffeac8895d Merge pull request #1291 from zhibog/master
Add Botan crypto lib as a vendor library
2021-11-09 23:59:14 +01:00
zhibog cef9632607 Add Botan crypto lib as a vendor library 2021-11-09 23:49:17 +01:00
gingerBill 76054dddb7 Revert build.bat 2021-11-09 22:11:18 +00:00
Jeroen van Rijn 9dc8753a14 [timings-export] Improve help messages
Also make `clang` happy as concerns the build settings switch/case.
2021-11-09 22:52:26 +01:00
William Roe a805d9a721 Fix path to static GLFW lib on Windows 2021-11-09 20:10:34 +00:00
Jeroen van Rijn 6c306f7633 Fix Linux warnings. 2021-11-09 20:31:22 +01:00
Jeroen van Rijn 05a86d5296 [timings-export] Implement JSON + CSV timngs export. 2021-11-09 19:57:55 +01:00
Jeroen van Rijn 9422fd311f [timings-export] Add -export-timings:format + -export-timings-file:filename. 2021-11-09 19:51:27 +01:00
gingerBill 80360f3f51 Add vendor packages for the js_wasm32 target 2021-11-09 18:26:42 +00:00
gingerBill 321d93bff1 Merge branch 'master' into target-js_wasm32 2021-11-09 18:06:19 +00:00
gingerBill 600d19c51b General catch-all for llvm debug types 2021-11-09 18:04:31 +00:00
gingerBill ed933bca19 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-09 16:36:01 +00:00
gingerBill a9ea590d24 Add dummy time_freestanding.odin 2021-11-09 16:35:50 +00:00
Jeroen van Rijn 275d39b59b Merge pull request #1287 from zhibog/master
Removed context switching system from the crypto library to simplify …
2021-11-09 16:57:59 +01:00
zhibog c24454ae70 Removed context switching system from the crypto library to simplify the code 2021-11-09 16:50:13 +01:00
DanielGavin fbc38c78eb Merge branch 'odin-lang:master' into parser-fix 2021-11-09 14:30:34 +01:00
Daniel Gavin b0db90de96 Add Any_Int as allowed flag in field signatures. 2021-11-09 14:29:53 +01:00
Jeroen van Rijn eb96f9677e Merge pull request #1285 from Kelimion/vet
[core:os/os2] Keep -vet happy.
2021-11-09 14:12:04 +01:00
Jeroen van Rijn 0a3b75c5f5 [core:os/os2] Keep -vet happy. 2021-11-09 14:06:14 +01:00
gingerBill 50562440bf Correct wasm-ld path for non-Windows platforms 2021-11-09 08:09:56 +00:00
gingerBill ce90c3c9ee Merge pull request #1284 from odin-lang/vendor-raylib-4.0
raylib 4.0
2021-11-09 08:05:48 +00:00
gingerBill d4bdcd55e1 Add Modified README.md 2021-11-08 16:25:51 +00:00
gingerBill 3f90faf0c9 Update vendor:raylib version 4.0 2021-11-08 15:57:55 +00:00
gingerBill 3d35c5ceb1 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-08 12:16:04 +00:00
gingerBill a674e842d0 Improve matrix indices to offset logic 2021-11-08 12:15:57 +00:00
gingerBill 23f0fbc376 Improve matrix->matrix casting implementation 2021-11-08 11:40:41 +00:00
gingerBill c63f4d68c8 Add math_js.odin specific calls (that just wrap the f64 procedures) 2021-11-07 20:06:05 +00:00
gingerBill 518460af66 Begin work in semi-standardized js_wasm32 target 2021-11-07 19:56:01 +00:00
gingerBill 39f652de47 Merge pull request #1280 from zhibog/master
Fix order of operations to make it correct and work with -o:speed flag
2021-11-07 18:08:33 +00:00
zhibog 483afe462b Fix order of operations to make it correct and work with -o:speed flaf 2021-11-07 18:53:30 +01:00
gingerBill 1296fabe2c Fix typos 2021-11-07 16:20:04 +00:00
gingerBill dc2edd3e79 Improve support for freestanding_wasm32 2021-11-07 16:19:27 +00:00
gingerBill e9c903f1ea Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-07 14:16:13 +00:00
gingerBill 83be954efd Minor spelling change 2021-11-07 14:16:05 +00:00
Jeroen van Rijn f84bdee1ba Merge pull request #1279 from DanielGavin/fix-json
Add json encoding test + fix enum not being set on success.
2021-11-07 14:47:35 +01:00
Daniel Gavin 5b074ceee5 Add json encoding test + fix enum not being set on success. 2021-11-07 14:35:52 +01:00
gingerBill 40eed29527 Remove LLVMAddDeadStoreEliminationPass pass 2021-11-06 18:11:29 +00:00
gingerBill 3d3785a7f1 Remove many LLVM optimization passes which were causes UB due to them assuming C-like behaviour incompatible with Odin 2021-11-06 17:23:33 +00:00
gingerBill 5df15b5724 Completely ignore LLVM_ADD_CONSTANT_VALUE_PASS LLVM >= 12 2021-11-06 16:29:53 +00:00
gingerBill ee259e4229 Merge pull request #1273 from odin-lang/compiler-map-improvements
Compiler Map Improvements
2021-11-05 18:12:40 +00:00
gingerBill 36985f8da0 Simplification to ptr_map_hash_key 2021-11-05 18:04:18 +00:00
gingerBill eb0faf9602 Unify hash logic for PtrSet 2021-11-05 17:58:11 +00:00
gingerBill 899cc71990 Improve ptr_map_hash_key 2021-11-05 17:55:09 +00:00
gingerBill 7be18b4a80 Be more correct with MapIndex usage 2021-11-05 17:36:00 +00:00
gingerBill 0c9bb9d920 Clean up logic 2021-11-05 17:32:17 +00:00
gingerBill 26e3daf5ad Unify MapFindResult types 2021-11-05 17:24:19 +00:00
gingerBill 0af69f8cda Remove map.cpp code 2021-11-05 17:16:37 +00:00
gingerBill 86e26c9a44 Remove dead code 2021-11-05 17:13:26 +00:00
gingerBill 541beb615b Move more things to PtrMap 2021-11-05 17:13:07 +00:00
gingerBill 6646348e1a Increase usage of PtrMap 2021-11-05 17:03:02 +00:00
gingerBill c38d6dc959 Remove HashKey usage for PtrMap calls 2021-11-05 16:46:09 +00:00
gingerBill 924faa58b4 Correct map_remove(PtrMap) 2021-11-05 16:45:27 +00:00
gingerBill 6be104e521 Make llvm backend code use PtrMap; remove dead code 2021-11-05 16:43:53 +00:00
gingerBill e95204908a Add PtrMap, begin working change Map to PtrMap where possible 2021-11-05 16:34:37 +00:00
gingerBill e963fc4d6a Change map index types to u32 from isize 2021-11-05 12:51:28 +00:00
gingerBill 1a75a71403 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-05 12:44:08 +00:00
gingerBill 439fc86740 Improve performance of the compiler hash table types and unify behaviour 2021-11-05 12:42:19 +00:00
gingerBill 0010e882a7 Make PtrSet match Map 2021-11-05 12:11:50 +00:00
gingerBill a022f18015 Reorganize code 2021-11-05 12:11:33 +00:00
Jeroen van Rijn cee9561259 Merge pull request #1271 from hdooley/master
don't try to use __cpuid() on arm64
2021-11-05 10:45:28 +01:00
Henry Dooley 3d0cd6f0dc don't try to use __cpuid() on arm64 2021-11-04 18:54:15 -07:00
gingerBill adb5928767 Change to RUNTIME_LINKAGE definition 2021-11-04 20:21:51 +00:00
gingerBill 23c74bc67b Update all_main.odin to include core:math/linalg/hlsl 2021-11-04 19:30:46 +00:00
gingerBill a22120fe94 Reorganize code 2021-11-04 17:38:58 +00:00
gingerBill ae25eaf10c Correct foreign import library usage 2021-11-04 17:25:37 +00:00
gingerBill adcfca966e Use Rtl*Memory procedures with -no-crt on Windows 2021-11-04 17:24:28 +00:00
gingerBill d8e34bd9b7 Add core:math/linalg/hlsl 2021-11-04 17:08:59 +00:00
gingerBill 68046d0c08 Allow casting between matrix types of different element types 2021-11-04 16:50:59 +00:00
gingerBill bc2bf1caeb Add #load_hash(<filepath>, <string-hash-kind>) 2021-11-04 16:29:41 +00:00
gingerBill d551144841 Add inverse for dmatN types 2021-11-04 16:09:19 +00:00
gingerBill 84540d7aa2 Add smoothstep 2021-11-04 15:57:27 +00:00
gingerBill 57eedfc4f4 Fix lb_emit_array_epi for matrix types 2021-11-04 15:01:31 +00:00
gingerBill 2718ade2bc Add core:math/linalg/glsl to all_main.odin 2021-11-04 14:56:16 +00:00
gingerBill 95f36d4fa5 Minor reorganization 2021-11-04 14:54:55 +00:00
gingerBill 3accf4048e Add f64 variants of all types and procedures 2021-11-04 14:52:03 +00:00
gingerBill eb05879148 Add more comments 2021-11-04 14:25:34 +00:00
gingerBill a882118c56 Add comments 2021-11-04 14:20:47 +00:00
gingerBill 57d15ac6e7 Remove unneeded suffixes 2021-11-04 14:11:34 +00:00
gingerBill e3cfdf6982 Remove build tag 2021-11-04 14:11:04 +00:00
gingerBill 017fe10762 core:math/linalg/glsl - GLSL-like mathematics types and operations 2021-11-04 14:09:12 +00:00
gingerBill 7bb7a741c6 Make math procedure contextless; Add asinh, acosh, atanh 2021-11-04 14:07:05 +00:00
gingerBill 14351c5bf2 Simplify logic for procs.odin 2021-11-04 13:56:38 +00:00
gingerBill 7ef3c87dbb Change RUNTIME_LINKAGE requirements 2021-11-04 13:52:53 +00:00
gingerBill b2a2aa15c2 Add ODIN_BUILD_MODE 2021-11-04 12:49:39 +00:00
gingerBill 1ec2f8d537 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-11-04 12:40:55 +00:00
gingerBill 6ded538546 @(linkage=<string>) for procedures and variables; @(require) for procedures; package runtime linkage improvements; Subsequence improvements to lb_run_remove_unused_function_pass 2021-11-04 12:40:50 +00:00
Jeroen van Rijn 0d1bc05419 Update issue templates 2021-11-04 12:37:24 +01:00
Jeroen van Rijn db2d7a4fdb Update issue templates 2021-11-04 12:36:48 +01:00
gingerBill 3fa7dabaa8 Correctly support -default-to-nil-allocator for all platforms 2021-11-04 11:03:21 +00:00
gingerBill 1980f32bd6 Correct demo.odin 2021-11-04 00:50:48 +00:00
gingerBill 9ab71ca0da Add ODIN_NO_CRT global constant 2021-11-04 00:50:28 +00:00
gingerBill 3d06dddb72 Allow casting from floats to quaternions 2021-11-03 12:45:19 +00:00
gingerBill 9896205a06 Make runtime builtin matrix procedures contextless 2021-11-03 12:44:34 +00:00
gingerBill 8a626ef564 Minor comments about matrix printing 2021-11-03 11:34:47 +00:00
gingerBill 8429943569 Represent matrices in fmt as expected 2021-11-03 11:27:21 +00:00
gingerBill edd12d505d Correct fmt for matrices 2021-11-03 11:20:04 +00:00
gingerBill 69f978f22b Correct lb_emit_matrix_flatten 2021-11-03 11:07:35 +00:00
gingerBill 229c98309e Correct assertion usage 2021-11-03 11:02:47 +00:00
Jeroen van Rijn c2665462e5 Merge pull request #1270 from Kelimion/fix_1268
Fix #1268.
2021-11-03 11:41:32 +01:00
Jeroen van Rijn 73648bb2d8 Fix #1268.
Error message for enumerated arrays going out of bounds was not yet updated for the Enum change.
2021-11-03 11:36:24 +01:00
Jeroen van Rijn ba0daaa706 Merge pull request #1269 from Kelimion/enum_array_bug
Fix error message.
2021-11-03 11:06:04 +01:00
Jeroen van Rijn dcc5697a48 Fix error message. 2021-11-03 11:01:18 +01:00
314 changed files with 32323 additions and 16291 deletions
+2 -2
View File
@@ -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
+47 -13
View File
@@ -6,8 +6,8 @@ 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 version
@@ -17,26 +17,34 @@ jobs:
run: ./odin report
timeout-minutes: 1
- name: Odin check
run: ./odin check examples/demo/demo.odin -vet
run: ./odin check examples/demo -vet
timeout-minutes: 10
- name: Odin run
run: ./odin run examples/demo/demo.odin
run: ./odin run examples/demo
timeout-minutes: 10
- name: Odin run -debug
run: ./odin run examples/demo/demo.odin -debug
run: ./odin run examples/demo -debug
timeout-minutes: 10
- name: Odin check examples/all
run: ./odin check examples/all -strict-style
timeout-minutes: 10
- name: Core library tests
run: |
cd tests/core
make
timeout-minutes: 10
- name: Vendor library tests
run: |
cd tests/vendor
make
timeout-minutes: 10
build_macOS:
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Download LLVM and setup PATH
- name: Download LLVM, botan and setup PATH
run: |
brew install llvm@11
brew install llvm@11 botan
echo "/usr/local/opt/llvm@11/bin" >> $GITHUB_PATH
TMP_PATH=$(xcrun --show-sdk-path)/user/include
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
@@ -49,13 +57,26 @@ jobs:
run: ./odin report
timeout-minutes: 1
- name: Odin check
run: ./odin check examples/demo/demo.odin -vet
run: ./odin check examples/demo -vet
timeout-minutes: 10
- name: Odin run
run: ./odin run examples/demo/demo.odin
run: ./odin run examples/demo
timeout-minutes: 10
- name: Odin run -debug
run: ./odin run examples/demo/demo.odin -debug
run: ./odin run examples/demo -debug
timeout-minutes: 10
- name: Odin check examples/all
run: ./odin check examples/all -strict-style
timeout-minutes: 10
- name: Core library tests
run: |
cd tests/core
make
timeout-minutes: 10
- name: Vendor library tests
run: |
cd tests/vendor
make
timeout-minutes: 10
build_windows:
runs-on: windows-latest
@@ -76,19 +97,25 @@ jobs:
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/demo/demo.odin -vet
odin check examples/demo -vet
timeout-minutes: 10
- name: Odin run
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo/demo.odin
odin run examples/demo
timeout-minutes: 10
- name: Odin run -debug
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo/demo.odin -debug
odin run examples/demo -debug
timeout-minutes: 10
- name: Odin check examples/all
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/all -strict-style
timeout-minutes: 10
- name: Core library tests
shell: cmd
@@ -97,6 +124,13 @@ jobs:
cd tests\core
call build.bat
timeout-minutes: 10
- name: Vendor library tests
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\vendor
call build.bat
timeout-minutes: 10
- name: core:math/big tests
shell: cmd
run: |
+26 -4
View File
@@ -8,12 +8,34 @@ CC=clang
OS=$(shell uname)
ifeq ($(OS), Darwin)
LLVM_CONFIG=llvm-config
ifneq ($(shell llvm-config --version | grep '^11\.'),)
ARCH=$(shell uname -m)
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)))"
ifneq ($(shell llvm-config --version | grep -E $(LLVM_VERSION_PATTERN)),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
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
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
+1 -1
View File
@@ -84,7 +84,7 @@ 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.
+29
View File
@@ -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.
+1 -1
View File
@@ -79,4 +79,4 @@ if %release_mode% EQU 0 odin run examples/demo
del *.obj > NUL 2> NUL
:end_of_build
:end_of_build
+1 -1
View File
@@ -2,7 +2,7 @@
package builtin
nil :: nil;
false :: 0!==0;
false :: 0!=0;
true :: 0==0;
ODIN_OS :: ODIN_OS;
+2 -3
View File
@@ -5,9 +5,8 @@ import "core:unicode"
import "core:unicode/utf8"
clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
c := make([]byte, len(s)+1, allocator, loc)
c := make([]byte, len(s), allocator, loc)
copy(c, s)
c[len(s)] = 0
return c[:len(s)]
}
@@ -1143,7 +1142,7 @@ fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.alloc
}
if start >= 0 {
append(&subslices, s[start : end])
append(&subslices, s[start : len(s)])
}
return subslices[:]
+3 -1
View File
@@ -3,6 +3,8 @@ package c
import builtin "core:builtin"
char :: builtin.u8 // assuming -funsigned-char
schar :: builtin.i8
short :: builtin.i16
int :: builtin.i32
long :: builtin.i32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.i64
@@ -46,7 +48,7 @@ int_least64_t :: builtin.i64
uint_least64_t :: builtin.u64
// Same on Windows, Linux, and FreeBSD
when ODIN_ARCH == "386" || ODIN_ARCH == "amd64" {
when ODIN_ARCH == "i386" || ODIN_ARCH == "amd64" {
int_fast8_t :: builtin.i8
uint_fast8_t :: builtin.u8
int_fast16_t :: builtin.i32
+1 -1
View File
@@ -956,7 +956,7 @@ substitute_token :: proc(cpp: ^Preprocessor, tok: ^Token, args: ^Macro_Arg) -> ^
continue
}
if tok.lit == "__VA__OPT__" && tok.next.lit == "(" {
if tok.lit == "__VA_OPT__" && tok.next.lit == "(" {
opt_arg := read_macro_arg_one(cpp, &tok, tok.next.next, true)
if has_varargs(args) {
for t := opt_arg.tok; t.kind != .EOF; t = t.next {
+2
View File
@@ -4,6 +4,8 @@ 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"
}
+2
View File
@@ -2,6 +2,8 @@ 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"
}
+16
View File
@@ -4,6 +4,8 @@ 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"
}
@@ -38,6 +40,20 @@ when ODIN_OS == "windows" {
ERANGE :: 34
}
when ODIN_OS == "darwin" {
@(private="file")
@(default_calling_convention="c")
foreign libc {
@(link_name="__error")
_get_errno :: proc() -> ^int ---
}
// Unknown
EDOM :: 33
EILSEQ :: 92
ERANGE :: 34
}
// Odin has no way to make an identifier "errno" behave as a function call to
// read the value, or to produce an lvalue such that you can assign a different
// error value to errno. To work around this, just expose it as a function like
+2
View File
@@ -6,6 +6,8 @@ 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"
}
+2 -1
View File
@@ -4,10 +4,11 @@ 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"
}
when ODIN_OS == "windows" {
@(default_calling_convention="c")
foreign libc {
+16 -1
View File
@@ -4,6 +4,8 @@ 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"
}
@@ -32,7 +34,20 @@ when ODIN_OS == "windows" {
SIGTERM :: 15
}
when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
SIG_ERR :: rawptr(~uintptr(0))
SIG_DFL :: rawptr(uintptr(0))
SIG_IGN :: rawptr(uintptr(1))
SIGABRT :: 6
SIGFPE :: 8
SIGILL :: 4
SIGINT :: 2
SIGSEGV :: 11
SIGTERM :: 15
}
when ODIN_OS == "darwin" {
SIG_ERR :: rawptr(~uintptr(0))
SIG_DFL :: rawptr(uintptr(0))
SIG_IGN :: rawptr(uintptr(1))
+34 -2
View File
@@ -2,6 +2,8 @@ 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"
}
@@ -67,7 +69,7 @@ when ODIN_OS == "linux" {
SEEK_CUR :: 1
SEEK_END :: 2
TMP_MAX :: 10000
TMP_MAX :: 308915776
foreign libc {
stderr: ^FILE
@@ -76,6 +78,36 @@ when ODIN_OS == "linux" {
}
}
when ODIN_OS == "darwin" {
fpos_t :: distinct i64
_IOFBF :: 0
_IOLBF :: 1
_IONBF :: 2
BUFSIZ :: 1024
EOF :: int(-1)
FOPEN_MAX :: 20
FILENAME_MAX :: 1024
L_tmpnam :: 1024
SEEK_SET :: 0
SEEK_CUR :: 1
SEEK_END :: 2
TMP_MAX :: 308915776
foreign libc {
@(link_name="__stderrp") stderr: ^FILE
@(link_name="__stdinp") stdin: ^FILE
@(link_name="__stdoutp") stdout: ^FILE
}
}
@(default_calling_convention="c")
foreign libc {
// 7.21.4 Operations on files
@@ -117,7 +149,7 @@ foreign libc {
putchar :: proc() -> int ---
puts :: proc(s: cstring) -> int ---
ungetc :: proc(c: int, stream: ^FILE) -> int ---
fread :: proc(ptr: rawptr, size: size_t, stream: ^FILE) -> size_t ---
fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
fwrite :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
// 7.21.9 File positioning functions
+19 -1
View File
@@ -4,6 +4,8 @@ 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"
}
@@ -33,7 +35,23 @@ when ODIN_OS == "linux" {
}
MB_CUR_MAX :: #force_inline proc() -> size_t {
return __ctype_get_mb_cur_max()
return size_t(__ctype_get_mb_cur_max())
}
}
when ODIN_OS == "darwin" {
RAND_MAX :: 0x7fffffff
// GLIBC and MUSL only
@(private="file")
@(default_calling_convention="c")
foreign libc {
___mb_cur_max :: proc() -> int ---
}
MB_CUR_MAX :: #force_inline proc() -> size_t {
return size_t(___mb_cur_max())
}
}
+2
View File
@@ -6,6 +6,8 @@ import "core:runtime"
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else when ODIN_OS == "darwin" {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
}
+5
View File
@@ -136,3 +136,8 @@ when ODIN_OS == "linux" {
tss_set :: proc(key: tss_t, val: rawptr) -> int ---
}
}
when ODIN_OS == "darwin" {
// TODO: find out what this is meant to be!
}
+5 -3
View File
@@ -4,6 +4,8 @@ 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"
}
@@ -43,7 +45,7 @@ when ODIN_OS == "windows" {
}
}
when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
@(default_calling_convention="c")
foreign libc {
// 7.27.2 Time manipulation functions
@@ -75,7 +77,7 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
tm :: struct {
tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst: int,
_: long,
_: rawptr,
tm_gmtoff: long,
tm_zone: rawptr,
}
}
+2
View File
@@ -3,6 +3,8 @@ package libc
import "core:c"
char :: c.char // assuming -funsigned-char
schar :: c.schar
short :: c.short
int :: c.int
long :: c.long
+2
View File
@@ -4,6 +4,8 @@ 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"
}
+2
View File
@@ -4,6 +4,8 @@ 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"
}
+8 -1
View File
@@ -4,6 +4,8 @@ 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"
}
@@ -14,10 +16,15 @@ when ODIN_OS == "windows" {
}
when ODIN_OS == "linux" {
wctrans_t :: distinct rawptr
wctrans_t :: distinct intptr_t
wctype_t :: distinct ulong
}
when ODIN_OS == "darwin" {
wctrans_t :: distinct int
wctype_t :: distinct u32
}
@(default_calling_convention="c")
foreign libc {
// 7.30.2.1 Wide character classification functions
+3
View File
@@ -5,6 +5,9 @@
List of contributors:
Jeroen van Rijn: Initial implementation, optimization.
*/
// package compress is a collection of utilities to aid with other compression packages
package compress
import "core:io"
+13 -10
View File
@@ -111,9 +111,9 @@ ZFAST_MASK :: ((1 << ZFAST_BITS) - 1)
*/
Huffman_Table :: struct {
fast: [1 << ZFAST_BITS]u16,
firstcode: [16]u16,
firstcode: [17]u16,
maxcode: [17]int,
firstsymbol: [16]u16,
firstsymbol: [17]u16,
size: [288]u8,
value: [288]u16,
}
@@ -244,7 +244,7 @@ allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_T
@(optimization_mode="speed")
build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
sizes: [HUFFMAN_MAX_BITS+1]int
next_code: [HUFFMAN_MAX_BITS]int
next_code: [HUFFMAN_MAX_BITS+1]int
k := int(0)
@@ -256,14 +256,14 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
}
sizes[0] = 0
for i in 1..<(HUFFMAN_MAX_BITS+1) {
for i in 1 ..< HUFFMAN_MAX_BITS {
if sizes[i] > (1 << uint(i)) {
return E_Deflate.Huffman_Bad_Sizes
}
}
code := int(0)
for i in 1..<HUFFMAN_MAX_BITS {
for i in 1 ..= HUFFMAN_MAX_BITS {
next_code[i] = code
z.firstcode[i] = u16(code)
z.firstsymbol[i] = u16(k)
@@ -538,19 +538,20 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
final = compress.read_bits_lsb(z, 1)
type = compress.read_bits_lsb(z, 2)
// fmt.printf("Final: %v | Type: %v\n", final, type);
// fmt.printf("Final: %v | Type: %v\n", final, type)
switch type {
case 0:
// fmt.printf("Method 0: STORED\n")
// Uncompressed block
// Discard bits until next byte boundary
compress.discard_to_next_byte_lsb(z)
uncompressed_len := i16(compress.read_bits_lsb(z, 16))
length_check := i16(compress.read_bits_lsb(z, 16))
uncompressed_len := u16(compress.read_bits_lsb(z, 16))
length_check := u16(compress.read_bits_lsb(z, 16))
// fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check);
// fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check)
if ~uncompressed_len != length_check {
@@ -567,10 +568,12 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
write_byte(z, u8(lit))
uncompressed_len -= 1
}
assert(uncompressed_len == 0)
case 3:
return E_Deflate.BType_3
case:
// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
// fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type)
if type == 1 {
// Use fixed code lengths.
build_huffman(z_repeat, Z_FIXED_LENGTH[:]) or_return
-216
View File
@@ -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)
}
+124
View File
@@ -0,0 +1,124 @@
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
Bit_Array :: struct {
bits: [dynamic]u64,
bias: int,
}
/*
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.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,
}
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
}
+52
View File
@@ -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")
}
*/
-80
View File
@@ -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)
}
+183
View File
@@ -0,0 +1,183 @@
package container_lru
import "core:intrinsics"
import "core:mem"
_ :: intrinsics
_ :: mem
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: mem.Allocator,
on_remove: proc(key: Key, value: Value, user_data: rawptr),
on_remove_user_data: rawptr,
call_on_remove_on_destroy: bool,
}
// 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 Cache
destroy :: proc(c: ^$C/Cache($Key, $Value)) {
for _, node in c.entries {
if c.call_on_remove_on_destroy && c.on_remove != nil {
c.on_remove(node.key, node.value, c.on_remove_user_data)
}
free(node, c.node_allocator)
}
clear(&c.entries)
delete(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) -> mem.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)
if c.on_remove != nil {
c.on_remove(node.key, node.value, c.on_remove_user_data)
}
free(node, c.node_allocator)
}
@(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
}
-377
View File
@@ -1,377 +0,0 @@
package container
import "core:intrinsics"
_ :: intrinsics
Map :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
hash: Array(int),
entries: Array(Map_Entry(Key, Value)),
}
Map_Entry :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
hash: uintptr,
next: int,
key: Key,
value: Value,
}
/*
map_init :: proc{
map_init_none,
map_init_cap,
}
map_delete
map_has
map_get
map_get_default
map_get_ptr
map_set
map_remove
map_reserve
map_clear
// Multi Map
multi_map_find_first
multi_map_find_next
multi_map_count
multi_map_get :: proc{
multi_map_get_array,
multi_map_get_slice,
};
multi_map_get_as_slice
multi_map_insert
multi_map_remove
multi_map_remove_all
*/
map_init :: proc{map_init_none, map_init_cap}
map_init_none :: proc(m: ^$M/Map($Key, $Value), allocator := context.allocator) {
m.hash.allocator = allocator
m.entries.allocator = allocator
}
map_init_cap :: proc(m: ^$M/Map($Key, $Value), cap: int, allocator := context.allocator) {
m.hash.allocator = allocator
m.entries.allocator = allocator
map_reserve(m, cap)
}
map_delete :: proc(m: $M/Map($Key, $Value)) {
array_delete(m.hash)
array_delete(m.entries)
}
map_has :: proc(m: $M/Map($Key, $Value), key: Key) -> bool {
return _map_find_or_fail(m, key) >= 0
}
map_get :: proc(m: $M/Map($Key, $Value), key: Key) -> (res: Value, ok: bool) #optional_ok {
i := _map_find_or_fail(m, key)
if i < 0 {
return {}, false
}
return array_get(m.entries, i).value, true
}
map_get_default :: proc(m: $M/Map($Key, $Value), key: Key, default: Value) -> (res: Value, ok: bool) #optional_ok {
i := _map_find_or_fail(m, key)
if i < 0 {
return default, false
}
return array_get(m.entries, i).value, true
}
map_get_ptr :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Value {
i := _map_find_or_fail(m, key)
if i < 0 {
return nil
}
return array_get_ptr(m.entries, i).value
}
map_set :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
if array_len(m.hash) == 0 {
_map_grow(m)
}
i := _map_find_or_make(m, key)
array_get_ptr(m.entries, i).value = value
if _map_full(m^) {
_map_grow(m)
}
}
map_remove :: proc(m: ^$M/Map($Key, $Value), key: Key) {
fr := _map_find_key(m^, key)
if fr.entry_index >= 0 {
_map_erase(m, fr)
}
}
map_reserve :: proc(m: ^$M/Map($Key, $Value), new_size: int) {
nm: M
map_init(&nm, m.hash.allocator)
array_resize(&nm.hash, new_size)
array_reserve(&nm.entries, array_len(m.entries))
for i in 0..<new_size {
array_set(&nm.hash, i, -1)
}
for i in 0..<array_len(m.entries) {
e := array_get(m.entries, i)
multi_map_insert(&nm, e.key, e.value)
}
map_delete(m^)
m^ = nm
}
map_clear :: proc(m: ^$M/Map($Key, $Value)) {
array_clear(&m.hash)
array_clear(&m.entries)
}
multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Map_Entry(Key, Value) {
i := _map_find_or_fail(m, key)
if i < 0 {
return nil
}
return array_get_ptr(m.entries, i)
}
multi_map_find_next :: proc(m: $M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> ^Map_Entry(Key, Value) {
i := e.next
for i >= 0 {
it := array_get_ptr(m.entries, i)
if it.hash == e.hash && it.key == e.key {
return it
}
i = it.next
}
return nil
}
multi_map_count :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
n := 0
e := multi_map_find_first(m, key)
for e != nil {
n += 1
e = multi_map_find_next(m, e)
}
return n
}
multi_map_get :: proc{multi_map_get_array, multi_map_get_slice}
multi_map_get_array :: proc(m: $M/Map($Key, $Value), key: Key, items: ^Array(Value)) {
if items == nil {
return
}
e := multi_map_find_first(m, key)
for e != nil {
array_append(items, e.value)
e = multi_map_find_next(m, e)
}
}
multi_map_get_slice :: proc(m: $M/Map($Key, $Value), key: Key, items: []Value) {
e := multi_map_find_first(m, key)
i := 0
for e != nil && i < len(items) {
items[i] = e.value
i += 1
e = multi_map_find_next(m, e)
}
}
multi_map_get_as_slice :: proc(m: $M/Map($Key, $Value), key: Key) -> []Value {
items: Array(Value)
array_init(&items, 0)
e := multi_map_find_first(m, key)
for e != nil {
array_append(&items, e.value)
e = multi_map_find_next(m, e)
}
return array_slice(items)
}
multi_map_insert :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
if array_len(m.hash) == 0 {
_map_grow(m)
}
i := _map_make(m, key)
array_get_ptr(m.entries, i).value = value
if _map_full(m^) {
_map_grow(m)
}
}
multi_map_remove :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) {
fr := _map_find_entry(m, e)
if fr.entry_index >= 0 {
_map_erase(m, fr)
}
}
multi_map_remove_all :: proc(m: ^$M/Map($Key, $Value), key: Key) {
for map_exist(m^, key) {
map_remove(m, key)
}
}
/// Internal
Map_Find_Result :: struct {
hash_index: int,
entry_prev: int,
entry_index: int,
}
_map_add_entry :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int where intrinsics.type_is_valid_map_key(Key) {
hasher := intrinsics.type_hasher_proc(Key)
e: Map_Entry(Key, Value)
e.key = key
e.hash = hasher(&e.key, 0)
e.next = -1
idx := array_len(m.entries)
array_push(&m.entries, e)
return idx
}
_map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) {
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next)
} else {
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next
}
if fr.entry_index == array_len(m.entries)-1 {
array_pop_back(&m.entries)
return
}
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1))
last := _map_find_key(m^, array_get(m.entries, fr.entry_index).key)
if last.entry_prev < 0 {
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index
} else {
array_set(&m.hash, last.hash_index, fr.entry_index)
}
}
_map_find_key :: proc(m: $M/Map($Key, $Value), key: Key) -> Map_Find_Result where intrinsics.type_is_valid_map_key(Key) {
fr: Map_Find_Result
fr.hash_index = -1
fr.entry_prev = -1
fr.entry_index = -1
if array_len(m.hash) == 0 {
return fr
}
hasher := intrinsics.type_hasher_proc(Key)
key := key
hash := hasher(&key, 0)
fr.hash_index = int(hash % uintptr(array_len(m.hash)))
fr.entry_index = array_get(m.hash, fr.hash_index)
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index)
if it.hash == hash && it.key == key {
return fr
}
fr.entry_prev = fr.entry_index
fr.entry_index = it.next
}
return fr
}
_map_find_entry :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> Map_Find_Result {
fr: Map_Find_Result
fr.hash_index = -1
fr.entry_prev = -1
fr.entry_index = -1
if array_len(m.hash) == 0 {
return fr
}
fr.hash_index = int(e.hash % uintptr(array_len(m.hash)))
fr.entry_index = array_get(m.hash, fr.hash_index)
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index)
if it == e {
return fr
}
fr.entry_prev = fr.entry_index
fr.entry_index = it.next
}
return fr
}
_map_find_or_fail :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
return _map_find_key(m, key).entry_index
}
_map_find_or_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
fr := _map_find_key(m^, key)
if fr.entry_index >= 0 {
return fr.entry_index
}
i := _map_add_entry(m, key)
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, i)
} else {
array_get_ptr(m.entries, fr.entry_prev).next = i
}
return i
}
_map_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
fr := _map_find_key(m^, key)
i := _map_add_entry(m, key)
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, i)
} else {
array_get_ptr(m.entries, fr.entry_prev).next = i
}
array_get_ptr(m.entries, i).next = fr.entry_index
return i
}
_map_full :: proc(m: $M/Map($Key, $Value)) -> bool {
// TODO(bill): Determine good max load factor
return array_len(m.entries) >= (array_len(m.hash) / 4)*3
}
_map_grow :: proc(m: ^$M/Map($Key, $Value)) {
new_size := array_len(m.entries) * 4 + 7 // TODO(bill): Determine good grow rate
map_reserve(m, new_size)
}
-121
View File
@@ -1,121 +0,0 @@
package container
Priority_Queue :: struct($T: typeid) {
data: Array(T),
len: int,
priority: proc(item: T) -> int,
}
priority_queue_init_none :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, allocator := context.allocator) {
queue_init_len(q, f, 0, allocator)
}
priority_queue_init_len :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, allocator := context.allocator) {
queue_init_len_cap(q, f, 0, 16, allocator)
}
priority_queue_init_len_cap :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, cap: int, allocator := context.allocator) {
array_init(&q.data, len, cap, allocator)
q.len = len
q.priority = f
}
priority_queue_init :: proc{priority_queue_init_none, priority_queue_init_len, priority_queue_init_len_cap}
priority_queue_delete :: proc(q: $Q/Priority_Queue($T)) {
array_delete(q.data)
}
priority_queue_clear :: proc(q: ^$Q/Priority_Queue($T)) {
q.len = 0
}
priority_queue_len :: proc(q: $Q/Priority_Queue($T)) -> int {
return q.len
}
priority_queue_cap :: proc(q: $Q/Priority_Queue($T)) -> int {
return array_cap(q.data)
}
priority_queue_space :: proc(q: $Q/Priority_Queue($T)) -> int {
return array_len(q.data) - q.len
}
priority_queue_reserve :: proc(q: ^$Q/Priority_Queue($T), capacity: int) {
if capacity > q.len {
array_resize(&q.data, new_capacity)
}
}
priority_queue_resize :: proc(q: ^$Q/Priority_Queue($T), length: int) {
if length > q.len {
array_resize(&q.data, new_capacity)
}
q.len = length
}
_priority_queue_grow :: proc(q: ^$Q/Priority_Queue($T), min_capacity: int = 0) {
new_capacity := max(array_len(q.data)*2 + 8, min_capacity)
array_resize(&q.data, new_capacity)
}
priority_queue_push :: proc(q: ^$Q/Priority_Queue($T), item: T) {
if array_len(q.data) - q.len == 0 {
_priority_queue_grow(q)
}
s := array_slice(q.data)
s[q.len] = item
i := q.len
for i > 0 {
p := (i - 1) / 2
if q.priority(s[p]) <= q.priority(item) {
break
}
s[i] = s[p]
i = p
}
q.len += 1
if q.len > 0 {
s[i] = item
}
}
priority_queue_pop :: proc(q: ^$Q/Priority_Queue($T)) -> T {
assert(q.len > 0)
s := array_slice(q.data)
min := s[0]
root := s[q.len-1]
q.len -= 1
i := 0
for i * 2 + 1 < q.len {
a := i * 2 + 1
b := i * 2 + 2
c := b < q.len && q.priority(s[b]) < q.priority(s[a]) ? b : a
if q.priority(s[c]) >= q.priority(root) {
break
}
s[i] = s[c]
i = c
}
if q.len > 0 {
s[i] = root
}
return min
}
priority_queue_peek :: proc(q: ^$Q/Priority_Queue($T)) -> T {
assert(q.len > 0)
s := array_slice(q.data)
return s[0]
}
@@ -0,0 +1,143 @@
package container_priority_queue
import "core:builtin"
Priority_Queue :: struct($T: typeid) {
queue: [dynamic]T,
less: proc(a, b: T) -> bool,
swap: proc(q: []T, i, j: int),
}
DEFAULT_CAPACITY :: 16
default_swap_proc :: proc($T: typeid) -> proc(q: []T, i, j: int) {
return proc(q: []T, i, j: int) {
q[i], q[j] = q[j], q[i]
}
}
init :: proc(pq: ^$Q/Priority_Queue($T), less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int), capacity := DEFAULT_CAPACITY, allocator := context.allocator) {
if pq.queue.allocator.procedure == nil {
pq.queue.allocator = allocator
}
reserve(pq, capacity)
pq.less = less
pq.swap = swap
}
init_from_dynamic_array :: proc(pq: ^$Q/Priority_Queue($T), queue: [dynamic]T, less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int)) {
pq.queue = queue
pq.less = less
pq.swap = swap
n := builtin.len(pq.queue)
for i := n/2 - 1; i >= 0; i -= 1 {
_shift_down(pq, i, n)
}
}
destroy :: proc(pq: ^$Q/Priority_Queue($T)) {
clear(pq)
delete(pq.queue)
}
reserve :: proc(pq: ^$Q/Priority_Queue($T), capacity: int) {
builtin.reserve(&pq.queue, capacity)
}
clear :: proc(pq: ^$Q/Priority_Queue($T)) {
builtin.clear(&pq.queue)
}
len :: proc(pq: $Q/Priority_Queue($T)) -> int {
return builtin.len(pq.queue)
}
cap :: proc(pq: $Q/Priority_Queue($T)) -> int {
return builtin.cap(pq.queue)
}
_shift_down :: proc(pq: ^$Q/Priority_Queue($T), i0, n: int) -> bool {
// O(n log n)
if 0 > i0 || i0 > n {
return false
}
i := i0
queue := pq.queue[:]
for {
j1 := 2*i + 1
if j1 < 0 || j1 >= n {
break
}
j := j1
if j2 := j1+1; j2 < n && pq.less(queue[j2], queue[j1]) {
j = j2
}
if !pq.less(queue[j], queue[i]) {
break
}
pq.swap(queue, i, j)
i = j
}
return i > i0
}
_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
j := j
queue := pq.queue[:]
n := builtin.len(queue)
for 0 <= j {
i := (j-1)/2
if i == j || !pq.less(queue[j], queue[i]) {
break
}
pq.swap(queue, i, j)
j = i
}
}
// NOTE(bill): When an element at index 'i' has changed its value, this will fix the
// the heap ordering. This is using a basic "heapsort" with shift up and a shift down parts.
fix :: proc(pq: ^$Q/Priority_Queue($T), i: int) {
if !_shift_down(pq, i, builtin.len(pq.queue)) {
_shift_up(pq, i)
}
}
push :: proc(pq: ^$Q/Priority_Queue($T), value: T) {
append(&pq.queue, value)
_shift_up(pq, builtin.len(pq.queue)-1)
}
pop :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value: T) {
assert(condition=builtin.len(pq.queue)>0, loc=loc)
n := builtin.len(pq.queue)-1
pq.swap(pq.queue[:], 0, n)
_shift_down(pq, 0, n)
return builtin.pop(&pq.queue)
}
pop_safe :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value: T, ok: bool) {
if builtin.len(pq.queue) > 0 {
n := builtin.len(pq.queue)-1
pq.swap(pq.queue[:], 0, n)
_shift_down(pq, 0, n)
return builtin.pop_safe(&pq.queue)
}
return
}
remove :: proc(pq: ^$Q/Priority_Queue($T), i: int) -> (value: T, ok: bool) {
n := builtin.len(pq.queue)
if 0 <= i && i < n {
if n != i {
pq.swap(pq.queue[:], i, n)
_shift_down(pq, i, n)
_shift_up(pq, i)
}
value, ok = builtin.pop_safe(&pq.queue)
}
return
}
-175
View File
@@ -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)
}
+209
View File
@@ -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
}
-74
View File
@@ -1,74 +0,0 @@
package container
Ring :: struct($T: typeid) {
next, prev: ^Ring(T),
value: T,
}
ring_init :: proc(r: ^$R/Ring) -> ^R {
r.prev, r.next = r, r
return r
}
ring_next :: proc(r: ^$R/Ring) -> ^R {
if r.next == nil {
return ring_init(r)
}
return r.next
}
ring_prev :: proc(r: ^$R/Ring) -> ^R {
if r.prev == nil {
return ring_init(r)
}
return r.prev
}
ring_move :: proc(r: ^$R/Ring, n: int) -> ^R {
r := r
if r.next == nil {
return ring_init(r)
}
switch {
case n < 0:
for _ in n..<0 {
r = r.prev
}
case n > 0:
for _ in 0..<n {
r = r.next
}
}
return r
}
ring_link :: proc(r, s: ^$R/Ring) -> ^R {
n := ring_next(r)
if s != nil {
p := ring_prev(s)
r.next = s
s.prev = r
n.prev = p
p.next = n
}
return n
}
ring_unlink :: proc(r: ^$R/Ring, n: int) -> ^R {
if n <= 0 {
return nil
}
return ring_link(r, ring_move(r, n+1))
}
ring_len :: proc(r: ^$R/Ring) -> int {
n := 0
if r != nil {
n = 1
for p := ring_next(r); p != r; p = p.next {
n += 1
}
}
return n
}
-240
View File
@@ -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)
}
-95
View File
@@ -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}
+117
View File
@@ -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}
+42 -44
View File
@@ -2,48 +2,45 @@
A crypto library for the Odin language
## Supported
This library offers various algorithms available in either native Odin or via bindings to the [Botan](https://botan.randombit.net/) crypto library.
This library offers various algorithms implemented in Odin.
Please see the chart below for the options.
**Note:** All crypto hash algorithms, offered by [Botan\'s FFI](https://botan.randombit.net/handbook/api_ref/hash.html), have been added.
## Hashing algorithms
| Algorithm | Odin | Botan |
|:-------------------------------------------------------------------------------------------------------------|:-----------------|:---------------------|
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | &#10004;&#65039; | |
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | &#10004;&#65039; | &#10004;&#65039; |
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | &#10004;&#65039; | |
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | &#10004;&#65039; | &#10004;&#65039; |
| [Grøstl](http://www.groestl.info/Groestl.zip) | &#10004;&#65039; | |
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | &#10004;&#65039; | |
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | &#10004;&#65039; | |
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; | &#10004;&#65039; |
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | &#10004;&#65039; | |
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | &#10004;&#65039; | &#10004;&#65039; |
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | &#10004;&#65039; | &#10004;&#65039; |
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | &#10004;&#65039; | &#10004;&#65039;\* |
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | &#10004;&#65039; | &#10004;&#65039; |
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | &#10004;&#65039; | &#10004;&#65039; |
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; | &#10004;&#65039; |
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; | &#10004;&#65039; |
| [Skein](https://www.schneier.com/academic/skein/) | | &#10004;&#65039;\*\* |
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | &#10004;&#65039; | &#10004;&#65039; |
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | &#10004;&#65039; | &#10004;&#65039; |
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | &#10004;&#65039; | &#10004;&#65039; |
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | &#10004;&#65039; | |
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | &#10004;&#65039; | &#10004;&#65039; |
\* Only `RIPEMD-160`
\*\* Only `SKEIN-512`
| Algorithm | |
|:-------------------------------------------------------------------------------------------------------------|:-----------------|
| [BLAKE](https://web.archive.org/web/20190915215948/https://131002.net/blake) | &#10004;&#65039; |
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | &#10004;&#65039; |
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | &#10004;&#65039; |
| [GOST](https://datatracker.ietf.org/doc/html/rfc5831) | &#10004;&#65039; |
| [Grøstl](http://www.groestl.info/Groestl.zip) | &#10004;&#65039; |
| [HAVAL](https://web.archive.org/web/20150111210116/http://labs.calyptix.com/haval.php) | &#10004;&#65039; |
| [JH](https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html) | &#10004;&#65039; |
| [Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; |
| [MD2](https://datatracker.ietf.org/doc/html/rfc1319) | &#10004;&#65039; |
| [MD4](https://datatracker.ietf.org/doc/html/rfc1320) | &#10004;&#65039; |
| [MD5](https://datatracker.ietf.org/doc/html/rfc1321) | &#10004;&#65039; |
| [RIPEMD](https://homes.esat.kuleuven.be/~bosselae/ripemd160.html) | &#10004;&#65039; |
| [SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | &#10004;&#65039; |
| [SHA-2](https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf) | &#10004;&#65039; |
| [SHA-3](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; |
| [SHAKE](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; |
| [SM3](https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02) | &#10004;&#65039; |
| [Streebog](https://datatracker.ietf.org/doc/html/rfc6986) | &#10004;&#65039; |
| [Tiger](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | &#10004;&#65039; |
| [Tiger2](https://www.cs.technion.ac.il/~biham/Reports/Tiger/) | &#10004;&#65039; |
| [Whirlpool](https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html) | &#10004;&#65039; |
#### High level API
Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_<size>`\*\*\*.
Included in these groups are four procedures.
Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_<size>`\*.
Included in these groups are six procedures.
* `hash_string` - Hash a given string and return the computed hash. Just calls `hash_bytes` internally
* `hash_bytes` - Hash a given byte slice and return the computed hash
* `hash_string_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. Just calls `hash_bytes_to_buffer` internally
* `hash_bytes_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. The destination buffer has to be at least as big as the digest size of the hash
* `hash_stream` - Takes a stream from io.Stream and returns the computed hash from it
* `hash_file` - Takes a file handle and returns the computed hash from it. A second optional boolean parameter controls if the file is streamed (this is the default) or read at once (set to true)
\*\*\* On some algorithms there is another part to the name, since they might offer control about additional parameters.
\* On some algorithms there is another part to the name, since they might offer control about additional parameters.
For instance, `HAVAL` offers different sizes as well as three different round amounts.
Computing a 256-bit hash with 3 rounds is therefore achieved by calling `haval.hash_256_3(...)`.
@@ -51,13 +48,6 @@ Computing a 256-bit hash with 3 rounds is therefore achieved by calling `haval.h
The above mentioned procedures internally call three procedures: `init`, `update` and `final`.
You may also directly call them, if you wish.
#### Context system
The library uses a context system internally to be able to switch between Odin / Botan implementations freely.
When an Odin implementation is available, it is the default.
You may change what is used during runtime by calling `foo.use_botan()` or `foo.use_odin()`.
It is also possible to set this during compile time via `USE_BOTAN_LIB=true`.
Internally a vtable is used to set the appropriate procedures when switching. This works for all the procedures mentioned in the APIs above.
#### Example
```odin
package crypto_example
@@ -67,12 +57,20 @@ import "core:crypto/md4"
main :: proc() {
input := "foo"
// Compute the hash via Odin implementation
// Compute the hash, using the high level API
computed_hash := md4.hash(input)
// Switch to Botan
md4.use_botan()
// Compute the hash via Botan bindings
computed_hash_botan := md4.hash(input)
// Variant that takes a destination buffer, instead of returning the computed hash
hash := make([]byte, md4.DIGEST_SIZE) // @note: Destination buffer has to be at least as big as the digest size of the hash
md4.hash(input, hash[:])
// Compute the hash, using the low level API
ctx: md4.Md4_Context
computed_hash_low: [16]byte
md4.init(&ctx)
md4.update(&ctx, transmute([]byte)input)
md4.final(&ctx, computed_hash_low[:])
}
```
For example uses of all available algorithms, please see the tests within `tests/core/crypto`.
+22 -14
View File
@@ -6,7 +6,6 @@ package _blake2
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the BLAKE2 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/rfc7693> and <https://www.blake2.net/>
*/
@@ -76,7 +75,7 @@ BLAKE2B_IV := [8]u64 {
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
}
init_odin :: proc(ctx: ^$T) {
init :: proc(ctx: ^$T) {
when T == Blake2s_Context {
block_size :: BLAKE2S_BLOCK_SIZE
} else when T == Blake2b_Context {
@@ -139,17 +138,17 @@ init_odin :: proc(ctx: ^$T) {
}
if len(ctx.cfg.key) > 0 {
copy(ctx.padded_key[:], ctx.cfg.key)
update_odin(ctx, ctx.padded_key[:])
update(ctx, ctx.padded_key[:])
ctx.is_keyed = true
}
copy(ctx.ih[:], ctx.h[:])
copy(ctx.h[:], ctx.ih[:])
if ctx.is_keyed {
update_odin(ctx, ctx.padded_key[:])
update(ctx, ctx.padded_key[:])
}
}
update_odin :: proc(ctx: ^$T, p: []byte) {
update :: proc "contextless" (ctx: ^$T, p: []byte) {
p := p
when T == Blake2s_Context {
block_size :: BLAKE2S_BLOCK_SIZE
@@ -161,7 +160,7 @@ update_odin :: proc(ctx: ^$T, p: []byte) {
if len(p) > left {
copy(ctx.x[ctx.nx:], p[:left])
p = p[left:]
blake2_blocks(ctx, ctx.x[:])
blocks(ctx, ctx.x[:])
ctx.nx = 0
}
if len(p) > block_size {
@@ -169,13 +168,22 @@ update_odin :: proc(ctx: ^$T, p: []byte) {
if n == len(p) {
n -= block_size
}
blake2_blocks(ctx, p[:n])
blocks(ctx, p[:n])
p = p[n:]
}
ctx.nx += copy(ctx.x[ctx.nx:], p)
}
blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
final :: proc "contextless" (ctx: ^$T, hash: []byte) {
when T == Blake2s_Context {
blake2s_final(ctx, hash)
}
when T == Blake2b_Context {
blake2b_final(ctx, hash)
}
}
blake2s_final :: proc "contextless" (ctx: ^Blake2s_Context, hash: []byte) {
if ctx.is_keyed {
for i := 0; i < len(ctx.padded_key); i += 1 {
ctx.padded_key[i] = 0
@@ -193,7 +201,7 @@ blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
ctx.f[1] = 0xffffffff
}
blake2_blocks(ctx, ctx.x[:])
blocks(ctx, ctx.x[:])
j := 0
for s, _ in ctx.h[:(ctx.size - 1) / 4 + 1] {
@@ -205,7 +213,7 @@ blake2s_final_odin :: proc(ctx: $T, hash: []byte) {
}
}
blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
blake2b_final :: proc "contextless" (ctx: ^Blake2b_Context, hash: []byte) {
if ctx.is_keyed {
for i := 0; i < len(ctx.padded_key); i += 1 {
ctx.padded_key[i] = 0
@@ -223,7 +231,7 @@ blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
ctx.f[1] = 0xffffffffffffffff
}
blake2_blocks(ctx, ctx.x[:])
blocks(ctx, ctx.x[:])
j := 0
for s, _ in ctx.h[:(ctx.size - 1) / 8 + 1] {
@@ -239,7 +247,7 @@ blake2b_final_odin :: proc(ctx: $T, hash: []byte) {
}
}
blake2_blocks :: proc(ctx: ^$T, p: []byte) {
blocks :: proc "contextless" (ctx: ^$T, p: []byte) {
when T == Blake2s_Context {
blake2s_blocks(ctx, p)
}
@@ -248,7 +256,7 @@ blake2_blocks :: proc(ctx: ^$T, p: []byte) {
}
}
blake2s_blocks :: #force_inline proc "contextless"(ctx: ^Blake2s_Context, p: []byte) {
blake2s_blocks :: #force_inline proc "contextless" (ctx: ^Blake2s_Context, p: []byte) {
h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
p := p
for len(p) >= BLAKE2S_BLOCK_SIZE {
@@ -1404,7 +1412,7 @@ blake2s_blocks :: #force_inline proc "contextless"(ctx: ^Blake2s_Context, p: []b
ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7] = h0, h1, h2, h3, h4, h5, h6, h7
}
blake2b_blocks :: #force_inline proc "contextless"(ctx: ^Blake2b_Context, p: []byte) {
blake2b_blocks :: #force_inline proc "contextless" (ctx: ^Blake2b_Context, p: []byte) {
h0, h1, h2, h3, h4, h5, h6, h7 := ctx.h[0], ctx.h[1], ctx.h[2], ctx.h[3], ctx.h[4], ctx.h[5], ctx.h[6], ctx.h[7]
p := p
for len(p) >= BLAKE2B_BLOCK_SIZE {
-79
View File
@@ -1,79 +0,0 @@
package _ctx
/*
Copyright 2021 zhibog
Made available under the BSD-3 license.
List of contributors:
zhibog: Initial creation and testing of the bindings.
Implementation of the context, used internally by the crypto library.
*/
import "core:io"
import "core:os"
Hash_Size :: enum {
_16,
_20,
_24,
_28,
_32,
_40,
_48,
_64,
_128,
}
Hash_Context :: struct {
botan_hash_algo: cstring,
external_ctx: any,
internal_ctx: any,
hash_size: Hash_Size,
hash_size_val: int,
is_using_odin: bool,
using vtbl: ^Hash_Context_Vtable,
}
Hash_Context_Vtable :: struct {
hash_bytes_16 : proc (ctx: ^Hash_Context, input: []byte) -> [16]byte,
hash_bytes_20 : proc (ctx: ^Hash_Context, input: []byte) -> [20]byte,
hash_bytes_24 : proc (ctx: ^Hash_Context, input: []byte) -> [24]byte,
hash_bytes_28 : proc (ctx: ^Hash_Context, input: []byte) -> [28]byte,
hash_bytes_32 : proc (ctx: ^Hash_Context, input: []byte) -> [32]byte,
hash_bytes_40 : proc (ctx: ^Hash_Context, input: []byte) -> [40]byte,
hash_bytes_48 : proc (ctx: ^Hash_Context, input: []byte) -> [48]byte,
hash_bytes_64 : proc (ctx: ^Hash_Context, input: []byte) -> [64]byte,
hash_bytes_128 : proc (ctx: ^Hash_Context, input: []byte) -> [128]byte,
hash_file_16 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool),
hash_file_20 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool),
hash_file_24 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool),
hash_file_28 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool),
hash_file_32 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool),
hash_file_40 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([40]byte, bool),
hash_file_48 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool),
hash_file_64 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool),
hash_file_128 : proc (ctx: ^Hash_Context, hd: os.Handle, load_at_once := false) -> ([128]byte, bool),
hash_stream_16 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([16]byte, bool),
hash_stream_20 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([20]byte, bool),
hash_stream_24 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([24]byte, bool),
hash_stream_28 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([28]byte, bool),
hash_stream_32 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([32]byte, bool),
hash_stream_40 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([40]byte, bool),
hash_stream_48 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([48]byte, bool),
hash_stream_64 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([64]byte, bool),
hash_stream_128 : proc (ctx: ^Hash_Context, s: io.Stream) -> ([128]byte, bool),
hash_bytes_slice : proc (ctx: ^Hash_Context, input: []byte, out_size: int, allocator := context.allocator) -> []byte,
hash_file_slice : proc (ctx: ^Hash_Context, hd: os.Handle, out_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool),
hash_stream_slice : proc (ctx: ^Hash_Context, s: io.Stream, out_size: int, allocator := context.allocator) -> ([]byte, bool),
init : proc (ctx: ^Hash_Context),
update : proc (ctx: ^Hash_Context, data: []byte),
final : proc (ctx: ^Hash_Context, hash: []byte),
}
_init_vtable :: #force_inline proc() -> ^Hash_Context {
ctx := new(Hash_Context)
vtbl := new(Hash_Context_Vtable)
ctx.vtbl = vtbl
return ctx
}
+35
View File
@@ -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
+24
View File
@@ -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
}
+7 -8
View File
@@ -6,7 +6,6 @@ package _sha3
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the Keccak hashing algorithm, standardized as SHA3 in <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf>
To use the original Keccak padding, set the is_keccak bool to true, otherwise it will use SHA3 padding.
@@ -53,7 +52,7 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
t: u64 = ---
bc: [5]u64 = ---
when ODIN_ENDIAN != "little" {
when ODIN_ENDIAN != .Little {
v: uintptr = ---
for i = 0; i < 25; i += 1 {
v := uintptr(&st[i])
@@ -99,7 +98,7 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
st[0] ~= keccakf_rndc[r]
}
when ODIN_ENDIAN != "little" {
when ODIN_ENDIAN != .Little {
for i = 0; i < 25; i += 1 {
v = uintptr(&st[i])
t = st[i]
@@ -115,14 +114,14 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
}
}
init_odin :: proc "contextless" (c: ^Sha3_Context) {
init :: proc "contextless" (c: ^Sha3_Context) {
for i := 0; i < 25; i += 1 {
c.st.q[i] = 0
}
c.rsiz = 200 - 2 * c.mdlen
}
update_odin :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
update :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
j := c.pt
for i := 0; i < len(data); i += 1 {
c.st.b[j] ~= data[i]
@@ -135,7 +134,7 @@ update_odin :: proc "contextless" (c: ^Sha3_Context, data: []byte) {
c.pt = j
}
final_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
final :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
if c.is_keccak {
c.st.b[c.pt] ~= 0x01
} else {
@@ -149,14 +148,14 @@ final_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
}
}
shake_xof_odin :: proc "contextless" (c: ^Sha3_Context) {
shake_xof :: proc "contextless" (c: ^Sha3_Context) {
c.st.b[c.pt] ~= 0x1F
c.st.b[c.rsiz - 1] ~= 0x80
keccakf(&c.st.q)
c.pt = 0
}
shake_out_odin :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
shake_out :: proc "contextless" (c: ^Sha3_Context, hash: []byte) {
j := c.pt
for i := 0; i < len(hash); i += 1 {
if j >= c.rsiz {
+10 -11
View File
@@ -6,7 +6,6 @@ package _tiger
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the Tiger hashing algorithm, as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
*/
@@ -291,7 +290,7 @@ Tiger_Context :: struct {
ver: int,
}
round :: #force_inline proc "contextless"(a, b, c, x, mul: u64) -> (u64, u64, u64) {
round :: #force_inline proc "contextless" (a, b, c, x, mul: u64) -> (u64, u64, u64) {
a, b, c := a, b, c
c ~= x
a -= T1[c & 0xff] ~ T2[(c >> 16) & 0xff] ~ T3[(c >> 32) & 0xff] ~ T4[(c >> 48) & 0xff]
@@ -300,7 +299,7 @@ round :: #force_inline proc "contextless"(a, b, c, x, mul: u64) -> (u64, u64, u6
return a, b, c
}
pass :: #force_inline proc "contextless"(a, b, c: u64, d: []u64, mul: u64) -> (x, y, z: u64) {
pass :: #force_inline proc "contextless" (a, b, c: u64, d: []u64, mul: u64) -> (x, y, z: u64) {
x, y, z = round(a, b, c, d[0], mul)
y, z, x = round(y, z, x, d[1], mul)
z, x, y = round(z, x, y, d[2], mul)
@@ -312,7 +311,7 @@ pass :: #force_inline proc "contextless"(a, b, c: u64, d: []u64, mul: u64) -> (x
return
}
key_schedule :: #force_inline proc "contextless"(x: []u64) {
key_schedule :: #force_inline proc "contextless" (x: []u64) {
x[0] -= x[7] ~ 0xa5a5a5a5a5a5a5a5
x[1] ~= x[0]
x[2] += x[1]
@@ -331,7 +330,7 @@ key_schedule :: #force_inline proc "contextless"(x: []u64) {
x[7] -= x[6] ~ 0x0123456789abcdef
}
compress :: #force_inline proc "contextless"(ctx: ^Tiger_Context, data: []byte) {
compress :: #force_inline proc "contextless" (ctx: ^Tiger_Context, data: []byte) {
a := ctx.a
b := ctx.b
c := ctx.c
@@ -346,13 +345,13 @@ compress :: #force_inline proc "contextless"(ctx: ^Tiger_Context, data: []byte)
ctx.c += c
}
init_odin :: proc(ctx: ^Tiger_Context) {
init :: proc "contextless" (ctx: ^Tiger_Context) {
ctx.a = 0x0123456789abcdef
ctx.b = 0xfedcba9876543210
ctx.c = 0xf096a5b4c3b2e187
}
update_odin :: proc(ctx: ^Tiger_Context, input: []byte) {
update :: proc(ctx: ^Tiger_Context, input: []byte) {
p := make([]byte, len(input))
copy(p, input)
@@ -380,7 +379,7 @@ update_odin :: proc(ctx: ^Tiger_Context, input: []byte) {
}
}
final_odin :: proc(ctx: ^Tiger_Context, hash: []byte) {
final :: proc(ctx: ^Tiger_Context, hash: []byte) {
length := ctx.length
tmp: [64]byte
if ctx.ver == 1 {
@@ -391,16 +390,16 @@ final_odin :: proc(ctx: ^Tiger_Context, hash: []byte) {
size := length & 0x3f
if size < 56 {
update_odin(ctx, tmp[:56 - size])
update(ctx, tmp[:56 - size])
} else {
update_odin(ctx, tmp[:64 + 56 - size])
update(ctx, tmp[:64 + 56 - size])
}
length <<= 3
for i := uint(0); i < 8; i += 1 {
tmp[i] = byte(length >> (8 * i))
}
update_odin(ctx, tmp[:8])
update(ctx, tmp[:8])
for i := uint(0); i < 8; i += 1 {
tmp[i] = byte(ctx.a >> (8 * i))
File diff suppressed because it is too large Load Diff
+72 -131
View File
@@ -6,7 +6,6 @@ package blake2b
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Interface for the BLAKE2B hashing algorithm.
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
@@ -15,78 +14,89 @@ package blake2b
import "core:os"
import "core:io"
import "../botan"
import "../_ctx"
import "../_blake2"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_64 = hash_bytes_odin
ctx.hash_file_64 = hash_file_odin
ctx.hash_stream_64 = hash_stream_odin
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_BLAKE2B)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE :: 64
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [64]byte {
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [64]byte {
_create_blake2b_ctx()
return _hash_impl->hash_bytes_64(data)
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: _blake2.Blake2b_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
ctx.cfg = cfg
_blake2.init(&ctx)
_blake2.update(&ctx, data)
_blake2.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
ctx: _blake2.Blake2b_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
ctx.cfg = cfg
_blake2.init(&ctx)
_blake2.update(&ctx, data)
_blake2.final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_blake2b_ctx()
return _hash_impl->hash_stream_64(s)
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: _blake2.Blake2b_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
ctx.cfg = cfg
_blake2.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_blake2.update(&ctx, buf[:read])
}
}
_blake2.final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_blake2b_ctx()
return _hash_impl->hash_file_64(hd, load_at_once)
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -94,93 +104,24 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
Blake2b_Context :: _blake2.Blake2b_Context
init :: proc(ctx: ^_blake2.Blake2b_Context) {
_blake2.init(ctx)
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
update :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, data: []byte) {
_blake2.update(ctx, data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
hash: [64]byte
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
_blake2.init_odin(&c)
_blake2.update_odin(&c, data)
_blake2.blake2b_final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
hash: [64]byte
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
_blake2.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_blake2.update_odin(&c, buf[:read])
}
}
_blake2.blake2b_final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
@(private)
_create_blake2b_ctx :: #force_inline proc() {
ctx: _blake2.Blake2b_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
ctx.cfg = cfg
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._64
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_blake2b_ctx()
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
_blake2.init_odin(&c)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
_blake2.update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(_blake2.Blake2b_Context); ok {
_blake2.blake2b_final_odin(&c, hash)
}
final :: proc "contextless" (ctx: ^_blake2.Blake2b_Context, hash: []byte) {
_blake2.final(ctx, hash)
}
+72 -131
View File
@@ -6,7 +6,6 @@ package blake2s
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Interface for the BLAKE2S hashing algorithm.
BLAKE2B and BLAKE2B share the implementation in the _blake2 package.
@@ -15,78 +14,89 @@ package blake2s
import "core:os"
import "core:io"
import "../_ctx"
import "../_blake2"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_32 = hash_bytes_odin
ctx.hash_file_32 = hash_file_odin
ctx.hash_stream_32 = hash_stream_odin
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan does nothing, since Blake2s is not available in Botan
@(warning="Blake2s is not provided by the Botan API. Odin implementation will be used")
use_botan :: #force_inline proc() {
use_odin()
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE :: 32
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [32]byte {
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [32]byte {
_create_blake2s_ctx()
return _hash_impl->hash_bytes_32(data)
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: _blake2.Blake2s_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
ctx.cfg = cfg
_blake2.init(&ctx)
_blake2.update(&ctx, data)
_blake2.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
ctx: _blake2.Blake2s_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
ctx.cfg = cfg
_blake2.init(&ctx)
_blake2.update(&ctx, data)
_blake2.final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_blake2s_ctx()
return _hash_impl->hash_stream_32(s)
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: _blake2.Blake2s_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
ctx.cfg = cfg
_blake2.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_blake2.update(&ctx, buf[:read])
}
}
_blake2.final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_blake2s_ctx()
return _hash_impl->hash_file_32(hd, load_at_once)
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -94,93 +104,24 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
Blake2s_Context :: _blake2.Blake2b_Context
init :: proc(ctx: ^_blake2.Blake2s_Context) {
_blake2.init(ctx)
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
update :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, data: []byte) {
_blake2.update(ctx, data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
_blake2.init_odin(&c)
_blake2.update_odin(&c, data)
_blake2.blake2s_final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
_blake2.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_blake2.update_odin(&c, buf[:read])
}
}
_blake2.blake2s_final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
@(private)
_create_blake2s_ctx :: #force_inline proc() {
ctx: _blake2.Blake2s_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
ctx.cfg = cfg
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._32
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_blake2s_ctx()
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
_blake2.init_odin(&c)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
_blake2.update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(_blake2.Blake2s_Context); ok {
_blake2.blake2s_final_odin(&c, hash)
}
final :: proc "contextless" (ctx: ^_blake2.Blake2s_Context, hash: []byte) {
_blake2.final(ctx, hash)
}
-498
View File
@@ -1,498 +0,0 @@
package botan
/*
Copyright 2021 zhibog
Made available under the BSD-3 license.
List of contributors:
zhibog: Initial creation and testing of the bindings.
Implementation of the context for the Botan side.
*/
import "core:os"
import "core:io"
import "core:fmt"
import "core:strings"
import "../_ctx"
hash_bytes_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
hash: [16]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._16, 16), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
hash: [20]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._20, 20), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte {
hash: [24]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._24, 24), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._28, 28), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._32, 32), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
hash: [48]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._48, 48), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
hash: [64]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._64, 64), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [128]byte {
hash: [128]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._128, 128), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash
}
hash_bytes_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
hash := make([]byte, bit_size, allocator)
c: hash_t
hash_init(&c, _check_ctx(ctx, nil, bit_size), 0)
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
hash_final(c, &hash[0])
hash_destroy(c)
return hash[:]
}
hash_file_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_16(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_16(ctx, buf[:]), ok
}
}
return [16]byte{}, false
}
hash_file_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
if !load_at_once {
return hash_stream_20(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_20(ctx, buf[:]), ok
}
}
return [20]byte{}, false
}
hash_file_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
if !load_at_once {
return hash_stream_24(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_24(ctx, buf[:]), ok
}
}
return [24]byte{}, false
}
hash_file_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
if !load_at_once {
return hash_stream_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_28(ctx, buf[:]), ok
}
}
return [28]byte{}, false
}
hash_file_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
hash_file_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_48(ctx, buf[:]), ok
}
}
return [48]byte{}, false
}
hash_file_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_64(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
hash_file_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([128]byte, bool) {
if !load_at_once {
return hash_stream_128(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(ctx, buf[:]), ok
}
}
return [128]byte{}, false
}
hash_file_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_slice(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_slice(ctx, buf[:], bit_size, allocator), ok
}
}
return nil, false
}
hash_stream_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([16]byte, bool) {
hash: [16]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._16, 16), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([20]byte, bool) {
hash: [20]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._20, 20), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([24]byte, bool) {
hash: [24]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._24, 24), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._28, 28), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._32, 32), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([48]byte, bool) {
hash: [48]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._48, 48), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([64]byte, bool) {
hash: [64]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._64, 64), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_128 :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream) -> ([128]byte, bool) {
hash: [128]byte
c: hash_t
hash_init(&c, _check_ctx(ctx, _ctx.Hash_Size._128, 128), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash, true
}
hash_stream_slice :: #force_inline proc(ctx: ^_ctx.Hash_Context, s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
hash := make([]byte, bit_size, allocator)
c: hash_t
hash_init(&c, _check_ctx(ctx, nil, bit_size), 0)
buf := make([]byte, 512)
defer delete(buf)
i := 1
for i > 0 {
i, _ = s->impl_read(buf)
if i > 0 {
hash_update(c, len(buf) == 0 ? nil : &buf[0], uint(i))
}
}
hash_final(c, &hash[0])
hash_destroy(c)
return hash[:], true
}
init :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
c: hash_t
hash_init(&c, ctx.botan_hash_algo, 0)
ctx.external_ctx = c
}
update :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.external_ctx.(hash_t); ok {
hash_update(c, len(data) == 0 ? nil : &data[0], uint(len(data)))
}
}
final :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.external_ctx.(hash_t); ok {
hash_final(c, &hash[0])
hash_destroy(c)
}
}
assign_hash_vtable :: proc(ctx: ^_ctx.Hash_Context, hash_algo: cstring) {
ctx.init = init
ctx.update = update
ctx.final = final
ctx.botan_hash_algo = hash_algo
switch hash_algo {
case HASH_MD4, HASH_MD5:
ctx.hash_bytes_16 = hash_bytes_16
ctx.hash_file_16 = hash_file_16
ctx.hash_stream_16 = hash_stream_16
case HASH_SHA1, HASH_RIPEMD_160:
ctx.hash_bytes_20 = hash_bytes_20
ctx.hash_file_20 = hash_file_20
ctx.hash_stream_20 = hash_stream_20
case HASH_SHA2, HASH_SHA3:
ctx.hash_bytes_28 = hash_bytes_28
ctx.hash_file_28 = hash_file_28
ctx.hash_stream_28 = hash_stream_28
ctx.hash_bytes_32 = hash_bytes_32
ctx.hash_file_32 = hash_file_32
ctx.hash_stream_32 = hash_stream_32
ctx.hash_bytes_48 = hash_bytes_48
ctx.hash_file_48 = hash_file_48
ctx.hash_stream_48 = hash_stream_48
ctx.hash_bytes_64 = hash_bytes_64
ctx.hash_file_64 = hash_file_64
ctx.hash_stream_64 = hash_stream_64
case HASH_GOST, HASH_WHIRLPOOL, HASH_SM3:
ctx.hash_bytes_32 = hash_bytes_32
ctx.hash_file_32 = hash_file_32
ctx.hash_stream_32 = hash_stream_32
case HASH_STREEBOG:
ctx.hash_bytes_32 = hash_bytes_32
ctx.hash_file_32 = hash_file_32
ctx.hash_stream_32 = hash_stream_32
ctx.hash_bytes_64 = hash_bytes_64
ctx.hash_file_64 = hash_file_64
ctx.hash_stream_64 = hash_stream_64
case HASH_BLAKE2B:
ctx.hash_bytes_64 = hash_bytes_64
ctx.hash_file_64 = hash_file_64
ctx.hash_stream_64 = hash_stream_64
case HASH_TIGER:
ctx.hash_bytes_16 = hash_bytes_16
ctx.hash_file_16 = hash_file_16
ctx.hash_stream_16 = hash_stream_16
ctx.hash_bytes_20 = hash_bytes_20
ctx.hash_file_20 = hash_file_20
ctx.hash_stream_20 = hash_stream_20
ctx.hash_bytes_24 = hash_bytes_24
ctx.hash_file_24 = hash_file_24
ctx.hash_stream_24 = hash_stream_24
case HASH_SKEIN_512:
ctx.hash_bytes_slice = hash_bytes_slice
ctx.hash_file_slice = hash_file_slice
ctx.hash_stream_slice = hash_stream_slice
}
}
_check_ctx :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash_size: _ctx.Hash_Size, hash_size_val: int) -> cstring {
ctx.hash_size = hash_size
ctx.hash_size_val = hash_size_val
switch ctx.botan_hash_algo {
case HASH_SHA2:
#partial switch hash_size {
case ._28: return HASH_SHA_224
case ._32: return HASH_SHA_256
case ._48: return HASH_SHA_384
case ._64: return HASH_SHA_512
}
case HASH_SHA3:
#partial switch hash_size {
case ._28: return HASH_SHA3_224
case ._32: return HASH_SHA3_256
case ._48: return HASH_SHA3_384
case ._64: return HASH_SHA3_512
}
case HASH_KECCAK:
#partial switch hash_size {
case ._28: return HASH_KECCAK_224
case ._32: return HASH_KECCAK_256
case ._48: return HASH_KECCAK_384
case ._64: return HASH_KECCAK_512
}
case HASH_STREEBOG:
#partial switch hash_size {
case ._32: return HASH_STREEBOG_256
case ._64: return HASH_STREEBOG_512
}
case HASH_TIGER:
#partial switch hash_size {
case ._16: return HASH_TIGER_128
case ._20: return HASH_TIGER_160
case ._24: return HASH_TIGER_192
}
case HASH_SKEIN_512:
return strings.unsafe_string_to_cstring(fmt.tprintf("Skein-512(%d)", hash_size_val * 8))
case: return ctx.botan_hash_algo
}
return nil
}
+581
View File
@@ -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
}
+52
View File
@@ -0,0 +1,52 @@
package crypto
import "core:mem"
// compare_constant_time returns 1 iff a and b are equal, 0 otherwise.
//
// The execution time of this routine is constant regardless of the contents
// of the slices being compared, as long as the length of the slices is equal.
// If the length of the two slices is different, it will early-return 0.
compare_constant_time :: proc "contextless" (a, b: []byte) -> int {
// If the length of the slices is different, early return.
//
// This leaks the fact that the slices have a different length,
// but the routine is primarily intended for comparing things
// like MACS and password digests.
n := len(a)
if n != len(b) {
return 0
}
return compare_byte_ptrs_constant_time(raw_data(a), raw_data(b), n)
}
// compare_byte_ptrs_constant_time returns 1 iff the bytes pointed to by
// a and b are equal, 0 otherwise.
//
// The execution time of this routine is constant regardless of the
// contents of the memory being compared.
compare_byte_ptrs_constant_time :: proc "contextless" (a, b: ^byte, n: int) -> int {
x := mem.slice_ptr(a, n)
y := mem.slice_ptr(b, n)
v: byte
for i in 0..<n {
v |= x[i] ~ y[i]
}
// After the loop, v == 0 iff a == b. The subtraction will underflow
// iff v == 0, setting the sign-bit, which gets returned.
return int((u32(v)-1) >> 31)
}
// rand_bytes fills the dst buffer with cryptographic entropy taken from
// the system entropy source. This routine will block if the system entropy
// source is not ready yet. All system entropy source failures are treated
// as catastrophic, resulting in a panic.
rand_bytes :: proc (dst: []byte) {
// zero-fill the buffer first
mem.zero_explicit(raw_data(dst), len(dst))
_rand_bytes(dst)
}
+139 -214
View File
@@ -6,7 +6,6 @@ package gost
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the GOST hashing algorithm, as defined in RFC 5831 <https://datatracker.ietf.org/doc/html/rfc5831>
*/
@@ -15,71 +14,77 @@ import "core:mem"
import "core:os"
import "core:io"
import "../botan"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
_assign_hash_vtable(ctx)
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_32 = hash_bytes_odin
ctx.hash_file_32 = hash_file_odin
ctx.hash_stream_32 = hash_stream_odin
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan does nothing, since MD2 is not available in Botan
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_GOST)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE :: 32
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [32]byte {
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [32]byte {
_create_gost_ctx()
return _hash_impl->hash_bytes_32(data)
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Gost_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
ctx: Gost_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_gost_ctx()
return _hash_impl->hash_stream_32(s)
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Gost_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_gost_ctx()
return _hash_impl->hash_file_32(hd, load_at_once)
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -87,91 +92,85 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
init :: proc "contextless" (ctx: ^Gost_Context) {
sbox: [8][16]u32 = {
{ 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15 },
{ 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8 },
{ 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13 },
{ 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3 },
{ 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5 },
{ 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3 },
{ 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11 },
{ 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 },
}
return hash
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
i := 0
for a := 0; a < 16; a += 1 {
ax := sbox[1][a] << 15
bx := sbox[3][a] << 23
cx := sbox[5][a]
cx = (cx >> 1) | (cx << 31)
dx := sbox[7][a] << 7
for b := 0; b < 16; b, i = b + 1, i + 1 {
SBOX_1[i] = ax | (sbox[0][b] << 11)
SBOX_2[i] = bx | (sbox[2][b] << 19)
SBOX_3[i] = cx | (sbox[4][b] << 27)
SBOX_4[i] = dx | (sbox[6][b] << 3)
}
}
return [32]byte{}, false
}
@(private)
_create_gost_ctx :: #force_inline proc() {
ctx: Gost_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._32
}
update :: proc(ctx: ^Gost_Context, data: []byte) {
length := byte(len(data))
j: byte
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_gost_ctx()
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
init_odin(&c)
i := ctx.partial_bytes
for i < 32 && j < length {
ctx.partial[i] = data[j]
i, j = i + 1, j + 1
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
update_odin(&c, data)
if i < 32 {
ctx.partial_bytes = i
return
}
bytes(ctx, ctx.partial[:], 256)
for (j + 32) < length {
bytes(ctx, data[j:], 256)
j += 32
}
i = 0
for j < length {
ctx.partial[i] = data[j]
i, j = i + 1, j + 1
}
ctx.partial_bytes = i
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Gost_Context); ok {
final_odin(&c, hash)
final :: proc(ctx: ^Gost_Context, hash: []byte) {
if ctx.partial_bytes > 0 {
mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
}
compress(ctx.hash[:], ctx.len[:])
compress(ctx.hash[:], ctx.sum[:])
for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 {
hash[j] = byte(ctx.hash[i])
hash[j + 1] = byte(ctx.hash[i] >> 8)
hash[j + 2] = byte(ctx.hash[i] >> 16)
hash[j + 3] = byte(ctx.hash[i] >> 24)
}
}
@@ -187,12 +186,12 @@ Gost_Context :: struct {
partial_bytes: byte,
}
SBOX_1 : [256]u32
SBOX_2 : [256]u32
SBOX_3 : [256]u32
SBOX_4 : [256]u32
SBOX_1: [256]u32
SBOX_2: [256]u32
SBOX_3: [256]u32
SBOX_4: [256]u32
GOST_ENCRYPT_ROUND :: #force_inline proc "contextless"(l, r, t, k1, k2: u32) -> (u32, u32, u32) {
ENCRYPT_ROUND :: #force_inline proc "contextless" (l, r, t, k1, k2: u32) -> (u32, u32, u32) {
l, r, t := l, r, t
t = (k1) + r
l ~= SBOX_1[t & 0xff] ~ SBOX_2[(t >> 8) & 0xff] ~ SBOX_3[(t >> 16) & 0xff] ~ SBOX_4[t >> 24]
@@ -201,30 +200,30 @@ GOST_ENCRYPT_ROUND :: #force_inline proc "contextless"(l, r, t, k1, k2: u32) ->
return l, r, t
}
GOST_ENCRYPT :: #force_inline proc "contextless"(a, b, c: u32, key: []u32) -> (l, r, t: u32) {
l, r, t = GOST_ENCRYPT_ROUND(a, b, c, key[0], key[1])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[0], key[1])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[0], key[1])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[2], key[3])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[4], key[5])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[6], key[7])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[7], key[6])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[5], key[4])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[3], key[2])
l, r, t = GOST_ENCRYPT_ROUND(l, r, t, key[1], key[0])
ENCRYPT :: #force_inline proc "contextless" (a, b, c: u32, key: []u32) -> (l, r, t: u32) {
l, r, t = ENCRYPT_ROUND(a, b, c, key[0], key[1])
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
l, r, t = ENCRYPT_ROUND(l, r, t, key[0], key[1])
l, r, t = ENCRYPT_ROUND(l, r, t, key[2], key[3])
l, r, t = ENCRYPT_ROUND(l, r, t, key[4], key[5])
l, r, t = ENCRYPT_ROUND(l, r, t, key[6], key[7])
l, r, t = ENCRYPT_ROUND(l, r, t, key[7], key[6])
l, r, t = ENCRYPT_ROUND(l, r, t, key[5], key[4])
l, r, t = ENCRYPT_ROUND(l, r, t, key[3], key[2])
l, r, t = ENCRYPT_ROUND(l, r, t, key[1], key[0])
t = r
r = l
l = t
return
}
gost_bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
a, c: u32
m: [8]u32
@@ -237,14 +236,14 @@ gost_bytes :: proc(ctx: ^Gost_Context, buf: []byte, bits: u32) {
c = c < a ? 1 : 0
}
gost_compress(ctx.hash[:], m[:])
compress(ctx.hash[:], m[:])
ctx.len[0] += bits
if ctx.len[0] < bits {
ctx.len[1] += 1
}
}
gost_compress :: proc(h, m: []u32) {
compress :: proc(h, m: []u32) {
key, u, v, w, s: [8]u32
copy(u[:], h)
@@ -272,7 +271,7 @@ gost_compress :: proc(h, m: []u32) {
r := h[i]
l := h[i + 1]
t: u32
l, r, t = GOST_ENCRYPT(l, r, 0, key[:])
l, r, t = ENCRYPT(l, r, 0, key[:])
s[i] = r
s[i + 1] = l
@@ -380,78 +379,4 @@ gost_compress :: proc(h, m: []u32) {
h[7] = v[0] ~ (v[0] >> 16) ~ (v[1] << 16) ~ (v[1] >> 16) ~ (v[2] << 16) ~
(v[3] >> 16) ~ v[3] ~ (v[4] << 16) ~ v[4] ~ (v[5] >> 16) ~ v[5] ~
(v[6] << 16) ~ (v[6] >> 16) ~ (v[7] << 16) ~ v[7]
}
init_odin :: proc(ctx: ^Gost_Context) {
sbox: [8][16]u32 = {
{ 10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15 },
{ 5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8 },
{ 7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13 },
{ 4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3 },
{ 7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5 },
{ 7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3 },
{ 13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11 },
{ 1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12 },
}
i := 0
for a := 0; a < 16; a += 1 {
ax := sbox[1][a] << 15
bx := sbox[3][a] << 23
cx := sbox[5][a]
cx = (cx >> 1) | (cx << 31)
dx := sbox[7][a] << 7
for b := 0; b < 16; b, i = b + 1, i + 1 {
SBOX_1[i] = ax | (sbox[0][b] << 11)
SBOX_2[i] = bx | (sbox[2][b] << 19)
SBOX_3[i] = cx | (sbox[4][b] << 27)
SBOX_4[i] = dx | (sbox[6][b] << 3)
}
}
}
update_odin :: proc(ctx: ^Gost_Context, data: []byte) {
length := byte(len(data))
j: byte
i := ctx.partial_bytes
for i < 32 && j < length {
ctx.partial[i] = data[j]
i, j = i + 1, j + 1
}
if i < 32 {
ctx.partial_bytes = i
return
}
gost_bytes(ctx, ctx.partial[:], 256)
for (j + 32) < length {
gost_bytes(ctx, data[j:], 256)
j += 32
}
i = 0
for j < length {
ctx.partial[i] = data[j]
i, j = i + 1, j + 1
}
ctx.partial_bytes = i
}
final_odin :: proc(ctx: ^Gost_Context, hash: []byte) {
if ctx.partial_bytes > 0 {
mem.set(&ctx.partial[ctx.partial_bytes], 0, 32 - int(ctx.partial_bytes))
gost_bytes(ctx, ctx.partial[:], u32(ctx.partial_bytes) << 3)
}
gost_compress(ctx.hash[:], ctx.len[:])
gost_compress(ctx.hash[:], ctx.sum[:])
for i, j := 0, 0; i < 8; i, j = i + 1, j + 4 {
hash[j] = byte(ctx.hash[i])
hash[j + 1] = byte(ctx.hash[i] >> 8)
hash[j + 2] = byte(ctx.hash[i] >> 16)
hash[j + 3] = byte(ctx.hash[i] >> 24)
}
}
+301 -378
View File
@@ -6,7 +6,6 @@ package groestl
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the GROESTL hashing algorithm, as defined in <http://www.groestl.info/Groestl.zip>
*/
@@ -14,99 +13,83 @@ package groestl
import "core:os"
import "core:io"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_28 = hash_bytes_odin_28
ctx.hash_file_28 = hash_file_odin_28
ctx.hash_stream_28 = hash_stream_odin_28
ctx.hash_bytes_32 = hash_bytes_odin_32
ctx.hash_file_32 = hash_file_odin_32
ctx.hash_stream_32 = hash_stream_odin_32
ctx.hash_bytes_48 = hash_bytes_odin_48
ctx.hash_file_48 = hash_file_odin_48
ctx.hash_stream_48 = hash_stream_odin_48
ctx.hash_bytes_64 = hash_bytes_odin_64
ctx.hash_file_64 = hash_file_odin_64
ctx.hash_stream_64 = hash_stream_odin_64
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan does nothing, since GROESTL is not available in Botan
@(warning="GROESTL is not provided by the Botan API. Odin implementation will be used")
use_botan :: #force_inline proc() {
use_odin()
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
@(private)
_create_groestl_ctx :: #force_inline proc(size: _ctx.Hash_Size) {
ctx: Groestl_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = size
#partial switch size {
case ._28: ctx.hashbitlen = 224
case ._32: ctx.hashbitlen = 256
case ._48: ctx.hashbitlen = 384
case ._64: ctx.hashbitlen = 512
}
}
/*
High level API
*/
DIGEST_SIZE_224 :: 28
DIGEST_SIZE_256 :: 32
DIGEST_SIZE_384 :: 48
DIGEST_SIZE_512 :: 64
// hash_string_224 will hash the given input and return the
// computed hash
hash_string_224 :: proc(data: string) -> [28]byte {
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// hash_bytes_224 will hash the given input and return the
// computed hash
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
_create_groestl_ctx(._28)
return _hash_impl->hash_bytes_28(data)
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
hash: [DIGEST_SIZE_224]byte
ctx: Groestl_Context
ctx.hashbitlen = 224
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_224 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_224 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
ctx: Groestl_Context
ctx.hashbitlen = 224
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
_create_groestl_ctx(._28)
return _hash_impl->hash_stream_28(s)
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
hash: [DIGEST_SIZE_224]byte
ctx: Groestl_Context
ctx.hashbitlen = 224
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_224 will read the file provided by the given handle
// and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
_create_groestl_ctx(._28)
return _hash_impl->hash_file_28(hd, load_at_once)
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
@@ -114,33 +97,78 @@ hash_224 :: proc {
hash_file_224,
hash_bytes_224,
hash_string_224,
hash_bytes_to_buffer_224,
hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [32]byte {
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_groestl_ctx(._32)
return _hash_impl->hash_bytes_32(data)
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: Groestl_Context
ctx.hashbitlen = 256
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
ctx: Groestl_Context
ctx.hashbitlen = 256
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_groestl_ctx(._32)
return _hash_impl->hash_stream_32(s)
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: Groestl_Context
ctx.hashbitlen = 256
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_groestl_ctx(._32)
return _hash_impl->hash_file_32(hd, load_at_once)
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -148,33 +176,78 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
hash_string_384 :: proc(data: string) -> [48]byte {
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
_create_groestl_ctx(._48)
return _hash_impl->hash_bytes_48(data)
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
hash: [DIGEST_SIZE_384]byte
ctx: Groestl_Context
ctx.hashbitlen = 384
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_384 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_384 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
ctx: Groestl_Context
ctx.hashbitlen = 384
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
_create_groestl_ctx(._48)
return _hash_impl->hash_stream_48(s)
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
hash: [DIGEST_SIZE_384]byte
ctx: Groestl_Context
ctx.hashbitlen = 384
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_384 will read the file provided by the given handle
// and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
_create_groestl_ctx(._48)
return _hash_impl->hash_file_48(hd, load_at_once)
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
@@ -182,33 +255,78 @@ hash_384 :: proc {
hash_file_384,
hash_bytes_384,
hash_string_384,
hash_bytes_to_buffer_384,
hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
hash_string_512 :: proc(data: string) -> [64]byte {
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_groestl_ctx(._64)
return _hash_impl->hash_bytes_64(data)
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
hash: [DIGEST_SIZE_512]byte
ctx: Groestl_Context
ctx.hashbitlen = 512
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_512 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_512 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
ctx: Groestl_Context
ctx.hashbitlen = 512
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_groestl_ctx(._64)
return _hash_impl->hash_stream_64(s)
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
hash: [DIGEST_SIZE_512]byte
ctx: Groestl_Context
ctx.hashbitlen = 512
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_512 will read the file provided by the given handle
// and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_groestl_ctx(._64)
return _hash_impl->hash_file_64(hd, load_at_once)
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -216,207 +334,109 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
hash_bytes_to_buffer_512,
hash_string_to_buffer_512,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
init :: proc(ctx: ^Groestl_Context) {
assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
if ctx.hashbitlen <= 256 {
ctx.rounds = 10
ctx.columns = 8
ctx.statesize = 64
} else {
return hash, false
ctx.rounds = 14
ctx.columns = 16
ctx.statesize = 128
}
for i := 8 - size_of(i32); i < 8; i += 1 {
ctx.chaining[i][ctx.columns - 1] = byte(ctx.hashbitlen >> (8 * (7 - uint(i))))
}
}
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
if !load_at_once {
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_28(ctx, buf[:]), ok
update :: proc(ctx: ^Groestl_Context, data: []byte) {
databitlen := len(data) * 8
msglen := databitlen / 8
rem := databitlen % 8
i: int
assert(ctx.bits_in_last_byte == 0)
if ctx.buf_ptr != 0 {
for i = 0; ctx.buf_ptr < ctx.statesize && i < msglen; i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1 {
ctx.buffer[ctx.buf_ptr] = data[i]
}
}
return [28]byte{}, false
}
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
if ctx.buf_ptr < ctx.statesize {
if rem != 0 {
ctx.bits_in_last_byte = rem
ctx.buffer[ctx.buf_ptr] = data[i]
ctx.buf_ptr += 1
}
return
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
ctx.buf_ptr = 0
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
}
transform(ctx, data[i:], u32(msglen - i))
i += ((msglen - i) / ctx.statesize) * ctx.statesize
for i < msglen {
ctx.buffer[ctx.buf_ptr] = data[i]
i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1
}
if rem != 0 {
ctx.bits_in_last_byte = rem
ctx.buffer[ctx.buf_ptr] = data[i]
ctx.buf_ptr += 1
}
}
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
final :: proc(ctx: ^Groestl_Context, hash: []byte) {
hashbytelen := ctx.hashbitlen / 8
if ctx.bits_in_last_byte != 0 {
ctx.buffer[ctx.buf_ptr - 1] &= ((1 << uint(ctx.bits_in_last_byte)) - 1) << (8 - uint(ctx.bits_in_last_byte))
ctx.buffer[ctx.buf_ptr - 1] ~= 0x1 << (7 - uint(ctx.bits_in_last_byte))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
ctx.buffer[ctx.buf_ptr] = 0x80
ctx.buf_ptr += 1
}
if ctx.buf_ptr > ctx.statesize - 8 {
for ctx.buf_ptr < ctx.statesize {
ctx.buffer[ctx.buf_ptr] = 0
ctx.buf_ptr += 1
}
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
ctx.buf_ptr = 0
}
return [32]byte{}, false
}
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
hash: [48]byte
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
for ctx.buf_ptr < ctx.statesize - 8 {
ctx.buffer[ctx.buf_ptr] = 0
ctx.buf_ptr += 1
}
return hash
}
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
hash: [48]byte
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
ctx.block_counter += 1
ctx.buf_ptr = ctx.statesize
for ctx.buf_ptr > ctx.statesize - 8 {
ctx.buf_ptr -= 1
ctx.buffer[ctx.buf_ptr] = byte(ctx.block_counter)
ctx.block_counter >>= 8
}
}
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_48(ctx, buf[:]), ok
}
}
return [48]byte{}, false
}
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
output_transformation(ctx)
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_groestl_ctx(ctx.hash_size)
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
init_odin(&c)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Groestl_Context); ok {
final_odin(&c, hash)
for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 {
hash[j] = ctx.chaining[i % 8][i / 8]
}
}
@@ -631,100 +651,3 @@ add_roundconstant :: proc(x: [][16]byte, columns: int, round: byte, v: Groestl_V
}
}
}
init_odin :: proc(ctx: ^Groestl_Context) {
if ctx.hashbitlen <= 256 {
ctx.rounds = 10
ctx.columns = 8
ctx.statesize = 64
} else {
ctx.rounds = 14
ctx.columns = 16
ctx.statesize = 128
}
for i := 8 - size_of(i32); i < 8; i += 1 {
ctx.chaining[i][ctx.columns - 1] = byte(ctx.hashbitlen >> (8 * (7 - uint(i))))
}
}
update_odin :: proc(ctx: ^Groestl_Context, data: []byte) {
databitlen := len(data) * 8
msglen := databitlen / 8
rem := databitlen % 8
i: int
assert(ctx.bits_in_last_byte == 0)
if ctx.buf_ptr != 0 {
for i = 0; ctx.buf_ptr < ctx.statesize && i < msglen; i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1 {
ctx.buffer[ctx.buf_ptr] = data[i]
}
if ctx.buf_ptr < ctx.statesize {
if rem != 0 {
ctx.bits_in_last_byte = rem
ctx.buffer[ctx.buf_ptr] = data[i]
ctx.buf_ptr += 1
}
return
}
ctx.buf_ptr = 0
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
}
transform(ctx, data[i:], u32(msglen - i))
i += ((msglen - i) / ctx.statesize) * ctx.statesize
for i < msglen {
ctx.buffer[ctx.buf_ptr] = data[i]
i, ctx.buf_ptr = i + 1, ctx.buf_ptr + 1
}
if rem != 0 {
ctx.bits_in_last_byte = rem
ctx.buffer[ctx.buf_ptr] = data[i]
ctx.buf_ptr += 1
}
}
final_odin :: proc(ctx: ^Groestl_Context, hash: []byte) {
hashbytelen := ctx.hashbitlen / 8
if ctx.bits_in_last_byte != 0 {
ctx.buffer[ctx.buf_ptr - 1] &= ((1 << uint(ctx.bits_in_last_byte)) - 1) << (8 - uint(ctx.bits_in_last_byte))
ctx.buffer[ctx.buf_ptr - 1] ~= 0x1 << (7 - uint(ctx.bits_in_last_byte))
} else {
ctx.buffer[ctx.buf_ptr] = 0x80
ctx.buf_ptr += 1
}
if ctx.buf_ptr > ctx.statesize - 8 {
for ctx.buf_ptr < ctx.statesize {
ctx.buffer[ctx.buf_ptr] = 0
ctx.buf_ptr += 1
}
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
ctx.buf_ptr = 0
}
for ctx.buf_ptr < ctx.statesize - 8 {
ctx.buffer[ctx.buf_ptr] = 0
ctx.buf_ptr += 1
}
ctx.block_counter += 1
ctx.buf_ptr = ctx.statesize
for ctx.buf_ptr > ctx.statesize - 8 {
ctx.buf_ptr -= 1
ctx.buffer[ctx.buf_ptr] = byte(ctx.block_counter)
ctx.block_counter >>= 8
}
transform(ctx, ctx.buffer[:], u32(ctx.statesize))
output_transformation(ctx)
for i, j := ctx.statesize - hashbytelen , 0; i < ctx.statesize; i, j = i + 1, j + 1 {
hash[j] = ctx.chaining[i % 8][i / 8]
}
}
File diff suppressed because it is too large Load Diff
+319 -396
View File
@@ -6,7 +6,6 @@ package jh
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the JH hashing algorithm, as defined in <https://www3.ntu.edu.sg/home/wuhj/research/jh/index.html>
*/
@@ -14,99 +13,83 @@ package jh
import "core:os"
import "core:io"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_28 = hash_bytes_odin_28
ctx.hash_file_28 = hash_file_odin_28
ctx.hash_stream_28 = hash_stream_odin_28
ctx.hash_bytes_32 = hash_bytes_odin_32
ctx.hash_file_32 = hash_file_odin_32
ctx.hash_stream_32 = hash_stream_odin_32
ctx.hash_bytes_48 = hash_bytes_odin_48
ctx.hash_file_48 = hash_file_odin_48
ctx.hash_stream_48 = hash_stream_odin_48
ctx.hash_bytes_64 = hash_bytes_odin_64
ctx.hash_file_64 = hash_file_odin_64
ctx.hash_stream_64 = hash_stream_odin_64
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan does nothing, since JH is not available in Botan
@(warning="JH is not provided by the Botan API. Odin implementation will be used")
use_botan :: #force_inline proc() {
use_odin()
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
@(private)
_create_jh_ctx :: #force_inline proc(size: _ctx.Hash_Size) {
ctx: Jh_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = size
#partial switch size {
case ._28: ctx.hashbitlen = 224
case ._32: ctx.hashbitlen = 256
case ._48: ctx.hashbitlen = 384
case ._64: ctx.hashbitlen = 512
}
}
/*
High level API
*/
DIGEST_SIZE_224 :: 28
DIGEST_SIZE_256 :: 32
DIGEST_SIZE_384 :: 48
DIGEST_SIZE_512 :: 64
// hash_string_224 will hash the given input and return the
// computed hash
hash_string_224 :: proc(data: string) -> [28]byte {
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// hash_bytes_224 will hash the given input and return the
// computed hash
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
_create_jh_ctx(._28)
return _hash_impl->hash_bytes_28(data)
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
hash: [DIGEST_SIZE_224]byte
ctx: Jh_Context
ctx.hashbitlen = 224
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_224 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_224 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
ctx: Jh_Context
ctx.hashbitlen = 224
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
_create_jh_ctx(._28)
return _hash_impl->hash_stream_28(s)
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
hash: [DIGEST_SIZE_224]byte
ctx: Jh_Context
ctx.hashbitlen = 224
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_224 will read the file provided by the given handle
// and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
_create_jh_ctx(._28)
return _hash_impl->hash_file_28(hd, load_at_once)
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
@@ -114,33 +97,78 @@ hash_224 :: proc {
hash_file_224,
hash_bytes_224,
hash_string_224,
hash_bytes_to_buffer_224,
hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [32]byte {
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_jh_ctx(._32)
return _hash_impl->hash_bytes_32(data)
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: Jh_Context
ctx.hashbitlen = 256
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
ctx: Jh_Context
ctx.hashbitlen = 256
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_jh_ctx(._32)
return _hash_impl->hash_stream_32(s)
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: Jh_Context
ctx.hashbitlen = 256
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_jh_ctx(._32)
return _hash_impl->hash_file_32(hd, load_at_once)
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -148,33 +176,78 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
hash_string_384 :: proc(data: string) -> [48]byte {
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
_create_jh_ctx(._48)
return _hash_impl->hash_bytes_48(data)
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
hash: [DIGEST_SIZE_384]byte
ctx: Jh_Context
ctx.hashbitlen = 384
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_384 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_384 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
ctx: Jh_Context
ctx.hashbitlen = 384
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
_create_jh_ctx(._48)
return _hash_impl->hash_stream_48(s)
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
hash: [DIGEST_SIZE_384]byte
ctx: Jh_Context
ctx.hashbitlen = 384
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_384 will read the file provided by the given handle
// and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
_create_jh_ctx(._48)
return _hash_impl->hash_file_48(hd, load_at_once)
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
@@ -182,33 +255,78 @@ hash_384 :: proc {
hash_file_384,
hash_bytes_384,
hash_string_384,
hash_bytes_to_buffer_384,
hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
hash_string_512 :: proc(data: string) -> [64]byte {
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_jh_ctx(._64)
return _hash_impl->hash_bytes_64(data)
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
hash: [DIGEST_SIZE_512]byte
ctx: Jh_Context
ctx.hashbitlen = 512
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_512 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_512 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
ctx: Jh_Context
ctx.hashbitlen = 512
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_jh_ctx(._64)
return _hash_impl->hash_stream_64(s)
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
hash: [DIGEST_SIZE_512]byte
ctx: Jh_Context
ctx.hashbitlen = 512
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_512 will read the file provided by the given handle
// and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_jh_ctx(._64)
return _hash_impl->hash_file_64(hd, load_at_once)
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -216,207 +334,106 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
hash_bytes_to_buffer_512,
hash_string_to_buffer_512,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
init :: proc(ctx: ^Jh_Context) {
assert(ctx.hashbitlen == 224 || ctx.hashbitlen == 256 || ctx.hashbitlen == 384 || ctx.hashbitlen == 512, "hashbitlen must be set to 224, 256, 384 or 512")
ctx.H[1] = byte(ctx.hashbitlen) & 0xff
ctx.H[0] = byte(ctx.hashbitlen >> 8) & 0xff
F8(ctx)
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
}
update :: proc(ctx: ^Jh_Context, data: []byte) {
databitlen := u64(len(data)) * 8
ctx.databitlen += databitlen
i := u64(0)
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
if (ctx.buffer_size > 0) && ((ctx.buffer_size + databitlen) < 512) {
if (databitlen & 7) == 0 {
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
} else {
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3) + 1])
}
ctx.buffer_size += databitlen
databitlen = 0
}
return hash
}
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
if (ctx.buffer_size > 0 ) && ((ctx.buffer_size + databitlen) >= 512) {
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
i = 64 - (ctx.buffer_size >> 3)
databitlen = databitlen - (512 - ctx.buffer_size)
F8(ctx)
ctx.buffer_size = 0
}
for databitlen >= 512 {
copy(ctx.buffer[:], data[i:i + 64])
F8(ctx)
i += 64
databitlen -= 512
}
if databitlen > 0 {
if (databitlen & 7) == 0 {
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3)])
} else {
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3) + 1])
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
ctx.buffer_size = databitlen
}
}
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
if !load_at_once {
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_28(ctx, buf[:]), ok
final :: proc(ctx: ^Jh_Context, hash: []byte) {
if ctx.databitlen & 0x1ff == 0 {
for i := 0; i < 64; i += 1 {
ctx.buffer[i] = 0
}
}
return [28]byte{}, false
}
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
ctx.buffer[0] = 0x80
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
F8(ctx)
} else {
return hash, false
}
}
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
if ctx.buffer_size & 7 == 0 {
for i := (ctx.databitlen & 0x1ff) >> 3; i < 64; i += 1 {
ctx.buffer[i] = 0
}
} else {
for i := ((ctx.databitlen & 0x1ff) >> 3) + 1; i < 64; i += 1 {
ctx.buffer[i] = 0
}
}
}
return [32]byte{}, false
}
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
hash: [48]byte
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
hash: [48]byte
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7))
F8(ctx)
for i := 0; i < 64; i += 1 {
ctx.buffer[i] = 0
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
F8(ctx)
}
}
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_48(ctx, buf[:]), ok
}
}
return [48]byte{}, false
}
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_jh_ctx(ctx.hash_size)
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
init_odin(&c)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Jh_Context); ok {
final_odin(&c, hash)
switch ctx.hashbitlen {
case 224: copy(hash[:], ctx.H[100:128])
case 256: copy(hash[:], ctx.H[96:128])
case 384: copy(hash[:], ctx.H[80:128])
case 512: copy(hash[:], ctx.H[64:128])
}
}
@@ -424,7 +441,7 @@ _final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
JH implementation
*/
JH_ROUNDCONSTANT_ZERO := [64]byte {
ROUNDCONSTANT_ZERO := [64]byte {
0x6, 0xa, 0x0, 0x9, 0xe, 0x6, 0x6, 0x7,
0xf, 0x3, 0xb, 0xc, 0xc, 0x9, 0x0, 0x8,
0xb, 0x2, 0xf, 0xb, 0x1, 0x3, 0x6, 0x6,
@@ -435,7 +452,7 @@ JH_ROUNDCONSTANT_ZERO := [64]byte {
0x0, 0x6, 0x6, 0x7, 0x3, 0x2, 0x2, 0xa,
}
JH_S := [2][16]byte {
SBOX := [2][16]byte {
{9, 0, 4, 11, 13, 12, 3, 15, 1, 10, 2, 6, 7, 5, 8, 14},
{3, 12, 6, 13, 5, 7, 1, 9, 15, 2, 0, 4, 11, 10, 14, 8},
}
@@ -450,7 +467,7 @@ Jh_Context :: struct {
buffer: [64]byte,
}
JH_E8_finaldegroup :: proc(ctx: ^Jh_Context) {
E8_finaldegroup :: proc(ctx: ^Jh_Context) {
t0,t1,t2,t3: byte
tem: [256]byte
for i := 0; i < 128; i += 1 {
@@ -473,11 +490,11 @@ JH_E8_finaldegroup :: proc(ctx: ^Jh_Context) {
}
}
jh_update_roundconstant :: proc(ctx: ^Jh_Context) {
update_roundconstant :: proc(ctx: ^Jh_Context) {
tem: [64]byte
t: byte
for i := 0; i < 64; i += 1 {
tem[i] = JH_S[0][ctx.roundconstant[i]]
tem[i] = SBOX[0][ctx.roundconstant[i]]
}
for i := 0; i < 64; i += 2 {
tem[i + 1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf
@@ -499,14 +516,14 @@ jh_update_roundconstant :: proc(ctx: ^Jh_Context) {
}
}
JH_R8 :: proc(ctx: ^Jh_Context) {
R8 :: proc(ctx: ^Jh_Context) {
t: byte
tem, roundconstant_expanded: [256]byte
for i := u32(0); i < 256; i += 1 {
roundconstant_expanded[i] = (ctx.roundconstant[i >> 2] >> (3 - (i & 3)) ) & 1
}
for i := 0; i < 256; i += 1 {
tem[i] = JH_S[roundconstant_expanded[i]][ctx.A[i]]
tem[i] = SBOX[roundconstant_expanded[i]][ctx.A[i]]
}
for i := 0; i < 256; i += 2 {
tem[i+1] ~= ((tem[i] << 1) ~ (tem[i] >> 3) ~ ((tem[i] >> 2) & 2)) & 0xf
@@ -528,7 +545,7 @@ JH_R8 :: proc(ctx: ^Jh_Context) {
}
}
JH_E8_initialgroup :: proc(ctx: ^Jh_Context) {
E8_initialgroup :: proc(ctx: ^Jh_Context) {
t0, t1, t2, t3: byte
tem: [256]byte
for i := u32(0); i < 256; i += 1 {
@@ -544,118 +561,24 @@ JH_E8_initialgroup :: proc(ctx: ^Jh_Context) {
}
}
JH_E8 :: proc(ctx: ^Jh_Context) {
E8 :: proc(ctx: ^Jh_Context) {
for i := 0; i < 64; i += 1 {
ctx.roundconstant[i] = JH_ROUNDCONSTANT_ZERO[i]
ctx.roundconstant[i] = ROUNDCONSTANT_ZERO[i]
}
JH_E8_initialgroup(ctx)
E8_initialgroup(ctx)
for i := 0; i < 42; i += 1 {
JH_R8(ctx)
jh_update_roundconstant(ctx)
R8(ctx)
update_roundconstant(ctx)
}
JH_E8_finaldegroup(ctx)
E8_finaldegroup(ctx)
}
JH_F8 :: proc(ctx: ^Jh_Context) {
F8 :: proc(ctx: ^Jh_Context) {
for i := 0; i < 64; i += 1 {
ctx.H[i] ~= ctx.buffer[i]
}
JH_E8(ctx)
E8(ctx)
for i := 0; i < 64; i += 1 {
ctx.H[i + 64] ~= ctx.buffer[i]
}
}
init_odin :: proc(ctx: ^Jh_Context) {
ctx.H[1] = byte(ctx.hashbitlen) & 0xff
ctx.H[0] = byte(ctx.hashbitlen >> 8) & 0xff
JH_F8(ctx)
}
update_odin :: proc(ctx: ^Jh_Context, data: []byte) {
databitlen := u64(len(data)) * 8
ctx.databitlen += databitlen
i := u64(0)
if (ctx.buffer_size > 0) && ((ctx.buffer_size + databitlen) < 512) {
if (databitlen & 7) == 0 {
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
} else {
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3) + 1])
}
ctx.buffer_size += databitlen
databitlen = 0
}
if (ctx.buffer_size > 0 ) && ((ctx.buffer_size + databitlen) >= 512) {
copy(ctx.buffer[ctx.buffer_size >> 3:], data[:64 - (ctx.buffer_size >> 3)])
i = 64 - (ctx.buffer_size >> 3)
databitlen = databitlen - (512 - ctx.buffer_size)
JH_F8(ctx)
ctx.buffer_size = 0
}
for databitlen >= 512 {
copy(ctx.buffer[:], data[i:i + 64])
JH_F8(ctx)
i += 64
databitlen -= 512
}
if databitlen > 0 {
if (databitlen & 7) == 0 {
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3)])
} else {
copy(ctx.buffer[:], data[i:i + ((databitlen & 0x1ff) >> 3) + 1])
}
ctx.buffer_size = databitlen
}
}
final_odin :: proc(ctx: ^Jh_Context, hash: []byte) {
if ctx.databitlen & 0x1ff == 0 {
for i := 0; i < 64; i += 1 {
ctx.buffer[i] = 0
}
ctx.buffer[0] = 0x80
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
JH_F8(ctx)
} else {
if ctx.buffer_size & 7 == 0 {
for i := (ctx.databitlen & 0x1ff) >> 3; i < 64; i += 1 {
ctx.buffer[i] = 0
}
} else {
for i := ((ctx.databitlen & 0x1ff) >> 3) + 1; i < 64; i += 1 {
ctx.buffer[i] = 0
}
}
ctx.buffer[(ctx.databitlen & 0x1ff) >> 3] |= 1 << (7 - (ctx.databitlen & 7))
JH_F8(ctx)
for i := 0; i < 64; i += 1 {
ctx.buffer[i] = 0
}
ctx.buffer[63] = byte(ctx.databitlen) & 0xff
ctx.buffer[62] = byte(ctx.databitlen >> 8) & 0xff
ctx.buffer[61] = byte(ctx.databitlen >> 16) & 0xff
ctx.buffer[60] = byte(ctx.databitlen >> 24) & 0xff
ctx.buffer[59] = byte(ctx.databitlen >> 32) & 0xff
ctx.buffer[58] = byte(ctx.databitlen >> 40) & 0xff
ctx.buffer[57] = byte(ctx.databitlen >> 48) & 0xff
ctx.buffer[56] = byte(ctx.databitlen >> 56) & 0xff
JH_F8(ctx)
}
switch ctx.hashbitlen {
case 224: copy(hash[:], ctx.H[100:128])
case 256: copy(hash[:], ctx.H[96:128])
case 384: copy(hash[:], ctx.H[80:128])
case 512: copy(hash[:], ctx.H[64:128])
}
}
+246 -301
View File
@@ -6,7 +6,6 @@ package keccak
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Interface for the Keccak hashing algorithm.
This is done because the padding in the SHA3 standard was changed by the NIST, resulting in a different output.
@@ -15,87 +14,89 @@ package keccak
import "core:os"
import "core:io"
import "../botan"
import "../_ctx"
import "../_sha3"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_28 = hash_bytes_odin_28
ctx.hash_file_28 = hash_file_odin_28
ctx.hash_stream_28 = hash_stream_odin_28
ctx.hash_bytes_32 = hash_bytes_odin_32
ctx.hash_file_32 = hash_file_odin_32
ctx.hash_stream_32 = hash_stream_odin_32
ctx.hash_bytes_48 = hash_bytes_odin_48
ctx.hash_file_48 = hash_file_odin_48
ctx.hash_stream_48 = hash_stream_odin_48
ctx.hash_bytes_64 = hash_bytes_odin_64
ctx.hash_file_64 = hash_file_odin_64
ctx.hash_stream_64 = hash_stream_odin_64
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_KECCAK)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE_224 :: 28
DIGEST_SIZE_256 :: 32
DIGEST_SIZE_384 :: 48
DIGEST_SIZE_512 :: 64
// hash_string_224 will hash the given input and return the
// computed hash
hash_string_224 :: proc(data: string) -> [28]byte {
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// hash_bytes_224 will hash the given input and return the
// computed hash
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
_create_keccak_ctx(28)
return _hash_impl->hash_bytes_28(data)
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
hash: [DIGEST_SIZE_224]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_224
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_224 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_224 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_224
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash)
}
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
_create_keccak_ctx(28)
return _hash_impl->hash_stream_28(s)
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
hash: [DIGEST_SIZE_224]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_224
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
}
// hash_file_224 will read the file provided by the given handle
// and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
_create_keccak_ctx(28)
return _hash_impl->hash_file_28(hd, load_at_once)
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
@@ -103,33 +104,81 @@ hash_224 :: proc {
hash_file_224,
hash_bytes_224,
hash_string_224,
hash_bytes_to_buffer_224,
hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [32]byte {
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_keccak_ctx(32)
return _hash_impl->hash_bytes_32(data)
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_256
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_256
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash)
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_keccak_ctx(32)
return _hash_impl->hash_stream_32(s)
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_256
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_keccak_ctx(32)
return _hash_impl->hash_file_32(hd, load_at_once)
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -137,33 +186,81 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
hash_string_384 :: proc(data: string) -> [48]byte {
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
_create_keccak_ctx(48)
return _hash_impl->hash_bytes_48(data)
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
hash: [DIGEST_SIZE_384]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_384
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_384 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_384 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_384
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash)
}
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
_create_keccak_ctx(48)
return _hash_impl->hash_stream_48(s)
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
hash: [DIGEST_SIZE_384]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_384
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
}
// hash_file_384 will read the file provided by the given handle
// and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
_create_keccak_ctx(48)
return _hash_impl->hash_file_48(hd, load_at_once)
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
@@ -171,33 +268,81 @@ hash_384 :: proc {
hash_file_384,
hash_bytes_384,
hash_string_384,
hash_bytes_to_buffer_384,
hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
hash_string_512 :: proc(data: string) -> [64]byte {
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_keccak_ctx(64)
return _hash_impl->hash_bytes_64(data)
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
hash: [DIGEST_SIZE_512]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_512
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_512 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_512 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_512
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash)
}
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_keccak_ctx(64)
return _hash_impl->hash_stream_64(s)
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
hash: [DIGEST_SIZE_512]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_512
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
}
// hash_file_512 will read the file provided by the given handle
// and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_keccak_ctx(64)
return _hash_impl->hash_file_64(hd, load_at_once)
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -205,225 +350,25 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
hash_bytes_to_buffer_512,
hash_string_to_buffer_512,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
Keccak_Context :: _sha3.Sha3_Context
init :: proc(ctx: ^_sha3.Sha3_Context) {
ctx.is_keccak = true
_sha3.init(ctx)
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
_sha3.update(ctx, data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
_sha3.update_odin(&c, data)
_sha3.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_sha3.update_odin(&c, buf[:read])
}
}
_sha3.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
if !load_at_once {
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_28(ctx, buf[:]), ok
}
}
return [28]byte{}, false
}
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
_sha3.update_odin(&c, data)
_sha3.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_sha3.update_odin(&c, buf[:read])
}
}
_sha3.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
hash: [48]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
_sha3.update_odin(&c, data)
_sha3.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
hash: [48]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_sha3.update_odin(&c, buf[:read])
}
}
_sha3.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_48(ctx, buf[:]), ok
}
}
return [48]byte{}, false
}
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
hash: [64]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
_sha3.update_odin(&c, data)
_sha3.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
hash: [64]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_sha3.update_odin(&c, buf[:read])
}
}
_sha3.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
@(private)
_create_keccak_ctx :: #force_inline proc(mdlen: int) {
ctx: _sha3.Sha3_Context
ctx.mdlen = mdlen
ctx.is_keccak = true
_hash_impl.internal_ctx = ctx
switch mdlen {
case 28: _hash_impl.hash_size = ._28
case 32: _hash_impl.hash_size = ._32
case 48: _hash_impl.hash_size = ._48
case 64: _hash_impl.hash_size = ._64
}
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
#partial switch ctx.hash_size {
case ._28: _create_keccak_ctx(28)
case ._32: _create_keccak_ctx(32)
case ._48: _create_keccak_ctx(48)
case ._64: _create_keccak_ctx(64)
}
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.final_odin(&c, hash)
}
final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
_sha3.final(ctx, hash)
}
+85 -165
View File
@@ -6,7 +6,6 @@ package md2
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the MD2 hashing algorithm, as defined in RFC 1319 <https://datatracker.ietf.org/doc/html/rfc1319>
*/
@@ -14,77 +13,77 @@ package md2
import "core:os"
import "core:io"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_16 = hash_bytes_odin
ctx.hash_file_16 = hash_file_odin
ctx.hash_stream_16 = hash_stream_odin
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan does nothing, since MD2 is not available in Botan
@(warning="MD2 is not provided by the Botan API. Odin implementation will be used")
use_botan :: #force_inline proc() {
use_odin()
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE :: 16
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [16]byte {
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [16]byte {
_create_md2_ctx()
return _hash_impl->hash_bytes_16(data)
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Md2_Context
// init(&ctx) No-op
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
ctx: Md2_Context
// init(&ctx) No-op
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_md2_ctx()
return _hash_impl->hash_stream_16(s)
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Md2_Context
// init(&ctx) No-op
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_md2_ctx()
return _hash_impl->hash_file_16(hd, load_at_once)
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -92,91 +91,40 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
@(warning="Init is a no-op for MD2")
init :: proc(ctx: ^Md2_Context) {
// No action needed here
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
update :: proc(ctx: ^Md2_Context, data: []byte) {
for i := 0; i < len(data); i += 1 {
ctx.data[ctx.datalen] = data[i]
ctx.datalen += 1
if (ctx.datalen == DIGEST_SIZE) {
transform(ctx, ctx.data[:])
ctx.datalen = 0
}
}
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
hash: [16]byte
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
final :: proc(ctx: ^Md2_Context, hash: []byte) {
to_pad := byte(DIGEST_SIZE - ctx.datalen)
for ctx.datalen < DIGEST_SIZE {
ctx.data[ctx.datalen] = to_pad
ctx.datalen += 1
}
return hash
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
hash: [16]byte
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
}
}
return [16]byte{}, false
}
@(private)
_create_md2_ctx :: #force_inline proc() {
ctx: Md2_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._16
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_md2_ctx()
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
init_odin(&c)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Md2_Context); ok {
final_odin(&c, hash)
transform(ctx, ctx.data[:])
transform(ctx, ctx.checksum[:])
for i := 0; i < DIGEST_SIZE; i += 1 {
hash[i] = ctx.state[i]
}
}
@@ -185,9 +133,9 @@ _final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
*/
Md2_Context :: struct {
data: [16]byte,
state: [16 * 3]byte,
checksum: [16]byte,
data: [DIGEST_SIZE]byte,
state: [DIGEST_SIZE * 3]byte,
checksum: [DIGEST_SIZE]byte,
datalen: int,
}
@@ -214,49 +162,21 @@ PI_TABLE := [?]byte {
transform :: proc(ctx: ^Md2_Context, data: []byte) {
j,k,t: byte
for j = 0; j < 16; j += 1 {
ctx.state[j + 16] = data[j]
ctx.state[j + 16 * 2] = (ctx.state[j + 16] ~ ctx.state[j])
for j = 0; j < DIGEST_SIZE; j += 1 {
ctx.state[j + DIGEST_SIZE] = data[j]
ctx.state[j + DIGEST_SIZE * 2] = (ctx.state[j + DIGEST_SIZE] ~ ctx.state[j])
}
t = 0
for j = 0; j < 16 + 2; j += 1 {
for k = 0; k < 16 * 3; k += 1 {
for j = 0; j < DIGEST_SIZE + 2; j += 1 {
for k = 0; k < DIGEST_SIZE * 3; k += 1 {
ctx.state[k] ~= PI_TABLE[t]
t = ctx.state[k]
}
t = (t + j) & 0xff
}
t = ctx.checksum[16 - 1]
for j = 0; j < 16; j += 1 {
t = ctx.checksum[DIGEST_SIZE - 1]
for j = 0; j < DIGEST_SIZE; j += 1 {
ctx.checksum[j] ~= PI_TABLE[data[j] ~ t]
t = ctx.checksum[j]
}
}
init_odin :: proc(ctx: ^Md2_Context) {
// No action needed here
}
update_odin :: proc(ctx: ^Md2_Context, data: []byte) {
for i := 0; i < len(data); i += 1 {
ctx.data[ctx.datalen] = data[i]
ctx.datalen += 1
if (ctx.datalen == 16) {
transform(ctx, ctx.data[:])
ctx.datalen = 0
}
}
}
final_odin :: proc(ctx: ^Md2_Context, hash: []byte) {
to_pad := byte(16 - ctx.datalen)
for ctx.datalen < 16 {
ctx.data[ctx.datalen] = to_pad
ctx.datalen += 1
}
transform(ctx, ctx.data[:])
transform(ctx, ctx.checksum[:])
for i := 0; i < 16; i += 1 {
hash[i] = ctx.state[i]
}
}
+102 -181
View File
@@ -16,77 +16,78 @@ import "core:os"
import "core:io"
import "../util"
import "../botan"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_16 = hash_bytes_odin
ctx.hash_file_16 = hash_file_odin
ctx.hash_stream_16 = hash_stream_odin
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_MD4)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE :: 16
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [16]byte {
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [16]byte {
_create_md4_ctx()
return _hash_impl->hash_bytes_16(data)
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Md4_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
ctx: Md4_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_md4_ctx()
return _hash_impl->hash_stream_16(s)
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Md4_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_md4_ctx()
return _hash_impl->hash_file_16(hd, load_at_once)
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -94,91 +95,69 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
init :: proc(ctx: ^Md4_Context) {
ctx.state[0] = 0x67452301
ctx.state[1] = 0xefcdab89
ctx.state[2] = 0x98badcfe
ctx.state[3] = 0x10325476
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
hash: [16]byte
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
hash: [16]byte
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
update :: proc(ctx: ^Md4_Context, data: []byte) {
for i := 0; i < len(data); i += 1 {
ctx.data[ctx.datalen] = data[i]
ctx.datalen += 1
if(ctx.datalen == BLOCK_SIZE) {
transform(ctx, ctx.data[:])
ctx.bitlen += 512
ctx.datalen = 0
}
}
return [16]byte{}, false
}
@(private)
_create_md4_ctx :: #force_inline proc() {
ctx: Md4_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._16
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_md4_ctx()
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
init_odin(&c)
final :: proc(ctx: ^Md4_Context, hash: []byte) {
i := ctx.datalen
if ctx.datalen < 56 {
ctx.data[i] = 0x80
i += 1
for i < 56 {
ctx.data[i] = 0x00
i += 1
}
} else if ctx.datalen >= 56 {
ctx.data[i] = 0x80
i += 1
for i < BLOCK_SIZE {
ctx.data[i] = 0x00
i += 1
}
transform(ctx, ctx.data[:])
mem.set(&ctx.data, 0, 56)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
update_odin(&c, data)
}
}
ctx.bitlen += u64(ctx.datalen * 8)
ctx.data[56] = byte(ctx.bitlen)
ctx.data[57] = byte(ctx.bitlen >> 8)
ctx.data[58] = byte(ctx.bitlen >> 16)
ctx.data[59] = byte(ctx.bitlen >> 24)
ctx.data[60] = byte(ctx.bitlen >> 32)
ctx.data[61] = byte(ctx.bitlen >> 40)
ctx.data[62] = byte(ctx.bitlen >> 48)
ctx.data[63] = byte(ctx.bitlen >> 56)
transform(ctx, ctx.data[:])
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Md4_Context); ok {
final_odin(&c, hash)
for i = 0; i < 4; i += 1 {
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
}
}
@@ -214,9 +193,9 @@ HH :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
transform :: proc(ctx: ^Md4_Context, data: []byte) {
a, b, c, d, i, j: u32
m: [16]u32
m: [DIGEST_SIZE]u32
for i, j = 0, 0; i < 16; i += 1 {
for i, j = 0, 0; i < DIGEST_SIZE; i += 1 {
m[i] = u32(data[j]) | (u32(data[j + 1]) << 8) | (u32(data[j + 2]) << 16) | (u32(data[j + 3]) << 24)
j += 4
}
@@ -282,61 +261,3 @@ transform :: proc(ctx: ^Md4_Context, data: []byte) {
ctx.state[2] += c
ctx.state[3] += d
}
init_odin :: proc(ctx: ^Md4_Context) {
ctx.state[0] = 0x67452301
ctx.state[1] = 0xefcdab89
ctx.state[2] = 0x98badcfe
ctx.state[3] = 0x10325476
}
update_odin :: proc(ctx: ^Md4_Context, data: []byte) {
for i := 0; i < len(data); i += 1 {
ctx.data[ctx.datalen] = data[i]
ctx.datalen += 1
if(ctx.datalen == BLOCK_SIZE) {
transform(ctx, ctx.data[:])
ctx.bitlen += 512
ctx.datalen = 0
}
}
}
final_odin :: proc(ctx: ^Md4_Context, hash: []byte) {
i := ctx.datalen
if ctx.datalen < 56 {
ctx.data[i] = 0x80
i += 1
for i < 56 {
ctx.data[i] = 0x00
i += 1
}
} else if ctx.datalen >= 56 {
ctx.data[i] = 0x80
i += 1
for i < BLOCK_SIZE {
ctx.data[i] = 0x00
i += 1
}
transform(ctx, ctx.data[:])
mem.set(&ctx.data, 0, 56)
}
ctx.bitlen += u64(ctx.datalen * 8)
ctx.data[56] = byte(ctx.bitlen)
ctx.data[57] = byte(ctx.bitlen >> 8)
ctx.data[58] = byte(ctx.bitlen >> 16)
ctx.data[59] = byte(ctx.bitlen >> 24)
ctx.data[60] = byte(ctx.bitlen >> 32)
ctx.data[61] = byte(ctx.bitlen >> 40)
ctx.data[62] = byte(ctx.bitlen >> 48)
ctx.data[63] = byte(ctx.bitlen >> 56)
transform(ctx, ctx.data[:])
for i = 0; i < 4; i += 1 {
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
}
}
+103 -183
View File
@@ -6,7 +6,6 @@ package md5
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the MD5 hashing algorithm, as defined in RFC 1321 <https://datatracker.ietf.org/doc/html/rfc1321>
*/
@@ -16,77 +15,78 @@ import "core:os"
import "core:io"
import "../util"
import "../botan"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_16 = hash_bytes_odin
ctx.hash_file_16 = hash_file_odin
ctx.hash_stream_16 = hash_stream_odin
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_MD5)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE :: 16
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [16]byte {
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [16]byte {
_create_md5_ctx()
return _hash_impl->hash_bytes_16(data)
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Md5_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
ctx: Md5_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_md5_ctx()
return _hash_impl->hash_stream_16(s)
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Md5_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_md5_ctx()
return _hash_impl->hash_file_16(hd, load_at_once)
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -94,91 +94,71 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
init :: proc(ctx: ^Md5_Context) {
ctx.state[0] = 0x67452301
ctx.state[1] = 0xefcdab89
ctx.state[2] = 0x98badcfe
ctx.state[3] = 0x10325476
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
hash: [16]byte
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
hash: [16]byte
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
update :: proc(ctx: ^Md5_Context, data: []byte) {
for i := 0; i < len(data); i += 1 {
ctx.data[ctx.datalen] = data[i]
ctx.datalen += 1
if(ctx.datalen == BLOCK_SIZE) {
transform(ctx, ctx.data[:])
ctx.bitlen += 512
ctx.datalen = 0
}
}
return [16]byte{}, false
}
@(private)
_create_md5_ctx :: #force_inline proc() {
ctx: Md5_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._16
}
final :: proc(ctx: ^Md5_Context, hash: []byte){
i : u32
i = ctx.datalen
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_md5_ctx()
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
init_odin(&c)
if ctx.datalen < 56 {
ctx.data[i] = 0x80
i += 1
for i < 56 {
ctx.data[i] = 0x00
i += 1
}
} else if ctx.datalen >= 56 {
ctx.data[i] = 0x80
i += 1
for i < BLOCK_SIZE {
ctx.data[i] = 0x00
i += 1
}
transform(ctx, ctx.data[:])
mem.set(&ctx.data, 0, 56)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
update_odin(&c, data)
}
}
ctx.bitlen += u64(ctx.datalen * 8)
ctx.data[56] = byte(ctx.bitlen)
ctx.data[57] = byte(ctx.bitlen >> 8)
ctx.data[58] = byte(ctx.bitlen >> 16)
ctx.data[59] = byte(ctx.bitlen >> 24)
ctx.data[60] = byte(ctx.bitlen >> 32)
ctx.data[61] = byte(ctx.bitlen >> 40)
ctx.data[62] = byte(ctx.bitlen >> 48)
ctx.data[63] = byte(ctx.bitlen >> 56)
transform(ctx, ctx.data[:])
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Md5_Context); ok {
final_odin(&c, hash)
for i = 0; i < 4; i += 1 {
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
}
}
@@ -218,9 +198,9 @@ II :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u
transform :: proc(ctx: ^Md5_Context, data: []byte) {
i, j: u32
m: [16]u32
m: [DIGEST_SIZE]u32
for i, j = 0, 0; i < 16; i+=1 {
for i, j = 0, 0; i < DIGEST_SIZE; i+=1 {
m[i] = u32(data[j]) + u32(data[j + 1]) << 8 + u32(data[j + 2]) << 16 + u32(data[j + 3]) << 24
j += 4
}
@@ -303,63 +283,3 @@ transform :: proc(ctx: ^Md5_Context, data: []byte) {
ctx.state[2] += c
ctx.state[3] += d
}
init_odin :: proc(ctx: ^Md5_Context) {
ctx.state[0] = 0x67452301
ctx.state[1] = 0xefcdab89
ctx.state[2] = 0x98badcfe
ctx.state[3] = 0x10325476
}
update_odin :: proc(ctx: ^Md5_Context, data: []byte) {
for i := 0; i < len(data); i += 1 {
ctx.data[ctx.datalen] = data[i]
ctx.datalen += 1
if(ctx.datalen == BLOCK_SIZE) {
transform(ctx, ctx.data[:])
ctx.bitlen += 512
ctx.datalen = 0
}
}
}
final_odin :: proc(ctx: ^Md5_Context, hash: []byte){
i : u32
i = ctx.datalen
if ctx.datalen < 56 {
ctx.data[i] = 0x80
i += 1
for i < 56 {
ctx.data[i] = 0x00
i += 1
}
} else if ctx.datalen >= 56 {
ctx.data[i] = 0x80
i += 1
for i < BLOCK_SIZE {
ctx.data[i] = 0x00
i += 1
}
transform(ctx, ctx.data[:])
mem.set(&ctx.data, 0, 56)
}
ctx.bitlen += u64(ctx.datalen * 8)
ctx.data[56] = byte(ctx.bitlen)
ctx.data[57] = byte(ctx.bitlen >> 8)
ctx.data[58] = byte(ctx.bitlen >> 16)
ctx.data[59] = byte(ctx.bitlen >> 24)
ctx.data[60] = byte(ctx.bitlen >> 32)
ctx.data[61] = byte(ctx.bitlen >> 40)
ctx.data[62] = byte(ctx.bitlen >> 48)
ctx.data[63] = byte(ctx.bitlen >> 56)
transform(ctx, ctx.data[:])
for i = 0; i < 4; i += 1 {
hash[i] = byte(ctx.state[0] >> (i * 8)) & 0x000000ff
hash[i + 4] = byte(ctx.state[1] >> (i * 8)) & 0x000000ff
hash[i + 8] = byte(ctx.state[2] >> (i * 8)) & 0x000000ff
hash[i + 12] = byte(ctx.state[3] >> (i * 8)) & 0x000000ff
}
}
+163
View File
@@ -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
}
}
+7
View File
@@ -0,0 +1,7 @@
package crypto
when ODIN_OS != "linux" {
_rand_bytes :: proc (dst: []byte) {
unimplemented("crypto: rand_bytes not supported on this OS")
}
}
+37
View File
@@ -0,0 +1,37 @@
package crypto
import "core:fmt"
import "core:os"
import "core:sys/unix"
_MAX_PER_CALL_BYTES :: 33554431 // 2^25 - 1
_rand_bytes :: proc (dst: []byte) {
dst := dst
l := len(dst)
for l > 0 {
to_read := min(l, _MAX_PER_CALL_BYTES)
ret := unix.sys_getrandom(raw_data(dst), to_read, 0)
if ret < 0 {
switch os.Errno(-ret) {
case os.EINTR:
// Call interupted by a signal handler, just retry the
// request.
continue
case os.ENOSYS:
// The kernel is apparently prehistoric (< 3.17 circa 2014)
// and does not support getrandom.
panic("crypto: getrandom not available in kernel")
case:
// All other failures are things that should NEVER happen
// unless the kernel interface changes (ie: the Linux
// developers break userland).
panic(fmt.tprintf("crypto: getrandom failed: %d", ret))
}
}
l -= ret
dst = dst[ret:]
}
}
+317 -446
View File
@@ -6,7 +6,6 @@ package ripemd
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation for the RIPEMD hashing algorithm as defined in <https://homes.esat.kuleuven.be/~bosselae/ripemd160.html>
*/
@@ -15,86 +14,81 @@ import "core:os"
import "core:io"
import "../util"
import "../botan"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_16 = hash_bytes_odin_16
ctx.hash_file_16 = hash_file_odin_16
ctx.hash_stream_16 = hash_stream_odin_16
ctx.hash_bytes_20 = hash_bytes_odin_20
ctx.hash_file_20 = hash_file_odin_20
ctx.hash_stream_20 = hash_stream_odin_20
ctx.hash_bytes_32 = hash_bytes_odin_32
ctx.hash_file_32 = hash_file_odin_32
ctx.hash_stream_32 = hash_stream_odin_32
ctx.hash_bytes_40 = hash_bytes_odin_40
ctx.hash_file_40 = hash_file_odin_40
ctx.hash_stream_40 = hash_stream_odin_40
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_RIPEMD_160)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE_128 :: 16
DIGEST_SIZE_160 :: 20
DIGEST_SIZE_256 :: 32
DIGEST_SIZE_320 :: 40
// hash_string_128 will hash the given input and return the
// computed hash
hash_string_128 :: proc(data: string) -> [16]byte {
hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128(transmute([]byte)(data))
}
// hash_bytes_128 will hash the given input and return the
// computed hash
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
_create_ripemd_ctx(16)
return _hash_impl->hash_bytes_16(data)
hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
hash: [DIGEST_SIZE_128]byte
ctx: Ripemd128_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_128 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_128 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
ctx: Ripemd128_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_128 will read the stream in chunks and compute a
// hash from its contents
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_ripemd_ctx(16)
return _hash_impl->hash_stream_16(s)
hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
hash: [DIGEST_SIZE_128]byte
ctx: Ripemd128_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_128 will read the file provided by the given handle
// and compute a hash
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_ripemd_ctx(16)
return _hash_impl->hash_file_16(hd, load_at_once)
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(buf[:]), ok
}
}
return [DIGEST_SIZE_128]byte{}, false
}
hash_128 :: proc {
@@ -102,33 +96,75 @@ hash_128 :: proc {
hash_file_128,
hash_bytes_128,
hash_string_128,
hash_bytes_to_buffer_128,
hash_string_to_buffer_128,
}
// hash_string_160 will hash the given input and return the
// computed hash
hash_string_160 :: proc(data: string) -> [20]byte {
hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
return hash_bytes_160(transmute([]byte)(data))
}
// hash_bytes_160 will hash the given input and return the
// computed hash
hash_bytes_160 :: proc(data: []byte) -> [20]byte {
_create_ripemd_ctx(20)
return _hash_impl->hash_bytes_20(data)
hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
hash: [DIGEST_SIZE_160]byte
ctx: Ripemd160_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_160 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_160 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_160 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
ctx: Ripemd160_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_160 will read the stream in chunks and compute a
// hash from its contents
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
_create_ripemd_ctx(20)
return _hash_impl->hash_stream_20(s)
hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
hash: [DIGEST_SIZE_160]byte
ctx: Ripemd160_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_160 will read the file provided by the given handle
// and compute a hash
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
_create_ripemd_ctx(20)
return _hash_impl->hash_file_20(hd, load_at_once)
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
if !load_at_once {
return hash_stream_160(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_160(buf[:]), ok
}
}
return [DIGEST_SIZE_160]byte{}, false
}
hash_160 :: proc {
@@ -136,33 +172,75 @@ hash_160 :: proc {
hash_file_160,
hash_bytes_160,
hash_string_160,
hash_bytes_to_buffer_160,
hash_string_to_buffer_160,
}
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [32]byte {
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_ripemd_ctx(32)
return _hash_impl->hash_bytes_32(data)
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: Ripemd256_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
ctx: Ripemd256_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_ripemd_ctx(32)
return _hash_impl->hash_stream_32(s)
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: Ripemd256_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_ripemd_ctx(32)
return _hash_impl->hash_file_32(hd, load_at_once)
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -170,33 +248,75 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
// hash_string_320 will hash the given input and return the
// computed hash
hash_string_320 :: proc(data: string) -> [40]byte {
hash_string_320 :: proc(data: string) -> [DIGEST_SIZE_320]byte {
return hash_bytes_320(transmute([]byte)(data))
}
// hash_bytes_320 will hash the given input and return the
// computed hash
hash_bytes_320 :: proc(data: []byte) -> [40]byte {
_create_ripemd_ctx(40)
return _hash_impl->hash_bytes_40(data)
hash_bytes_320 :: proc(data: []byte) -> [DIGEST_SIZE_320]byte {
hash: [DIGEST_SIZE_320]byte
ctx: Ripemd320_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_320 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_320 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_320(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_320 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_320 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_320, "Size of destination buffer is smaller than the digest size")
ctx: Ripemd320_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_320 will read the stream in chunks and compute a
// hash from its contents
hash_stream_320 :: proc(s: io.Stream) -> ([40]byte, bool) {
_create_ripemd_ctx(40)
return _hash_impl->hash_stream_40(s)
hash_stream_320 :: proc(s: io.Stream) -> ([DIGEST_SIZE_320]byte, bool) {
hash: [DIGEST_SIZE_320]byte
ctx: Ripemd320_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_320 will read the file provided by the given handle
// and compute a hash
hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([40]byte, bool) {
_create_ripemd_ctx(40)
return _hash_impl->hash_file_40(hd, load_at_once)
hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_320]byte, bool) {
if !load_at_once {
return hash_stream_320(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_320(buf[:]), ok
}
}
return [DIGEST_SIZE_320]byte{}, false
}
hash_320 :: proc {
@@ -204,263 +324,126 @@ hash_320 :: proc {
hash_file_320,
hash_bytes_320,
hash_string_320,
hash_bytes_to_buffer_320,
hash_string_to_buffer_320,
}
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
hash: [16]byte
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
/*
Low level API
*/
init :: proc(ctx: ^$T) {
when T == Ripemd128_Context {
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
} else when T == Ripemd160_Context {
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
} else when T == Ripemd256_Context {
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
ctx.s[4], ctx.s[5], ctx.s[6], ctx.s[7] = S5, S6, S7, S8
} else when T == Ripemd320_Context {
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
ctx.s[5], ctx.s[6], ctx.s[7], ctx.s[8], ctx.s[9] = S5, S6, S7, S8, S9
}
return hash
}
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
hash: [16]byte
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
update :: proc(ctx: ^$T, data: []byte) {
ctx.tc += u64(len(data))
data := data
if ctx.nx > 0 {
n := len(data)
when T == Ripemd128_Context {
if n > RIPEMD_128_BLOCK_SIZE - ctx.nx {
n = RIPEMD_128_BLOCK_SIZE - ctx.nx
}
} else when T == Ripemd160_Context {
if n > RIPEMD_160_BLOCK_SIZE - ctx.nx {
n = RIPEMD_160_BLOCK_SIZE - ctx.nx
}
} else when T == Ripemd256_Context{
if n > RIPEMD_256_BLOCK_SIZE - ctx.nx {
n = RIPEMD_256_BLOCK_SIZE - ctx.nx
}
} else when T == Ripemd320_Context{
if n > RIPEMD_320_BLOCK_SIZE - ctx.nx {
n = RIPEMD_320_BLOCK_SIZE - ctx.nx
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_16(ctx, buf[:]), ok
for i := 0; i < n; i += 1 {
ctx.x[ctx.nx + i] = data[i]
}
}
return [16]byte{}, false
}
hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
hash: [20]byte
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
hash: [20]byte
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
ctx.nx += n
when T == Ripemd128_Context {
if ctx.nx == RIPEMD_128_BLOCK_SIZE {
block(ctx, ctx.x[0:])
ctx.nx = 0
}
} else when T == Ripemd160_Context {
if ctx.nx == RIPEMD_160_BLOCK_SIZE {
block(ctx, ctx.x[0:])
ctx.nx = 0
}
} else when T == Ripemd256_Context{
if ctx.nx == RIPEMD_256_BLOCK_SIZE {
block(ctx, ctx.x[0:])
ctx.nx = 0
}
} else when T == Ripemd320_Context{
if ctx.nx == RIPEMD_320_BLOCK_SIZE {
block(ctx, ctx.x[0:])
ctx.nx = 0
}
}
final_odin(&c, hash[:])
return hash, true
data = data[n:]
}
n := block(ctx, data)
data = data[n:]
if len(data) > 0 {
ctx.nx = copy(ctx.x[:], data)
}
}
final :: proc(ctx: ^$T, hash: []byte) {
d := ctx
tc := d.tc
tmp: [64]byte
tmp[0] = 0x80
if tc % 64 < 56 {
update(d, tmp[0:56 - tc % 64])
} else {
return hash, false
update(d, tmp[0:64 + 56 - tc % 64])
}
tc <<= 3
for i : u32 = 0; i < 8; i += 1 {
tmp[i] = byte(tc >> (8 * i))
}
update(d, tmp[0:8])
when T == Ripemd128_Context {
size :: RIPEMD_128_SIZE
} else when T == Ripemd160_Context {
size :: RIPEMD_160_SIZE
} else when T == Ripemd256_Context{
size :: RIPEMD_256_SIZE
} else when T == Ripemd320_Context{
size :: RIPEMD_320_SIZE
}
digest: [size]byte
for s, i in d.s {
digest[i * 4] = byte(s)
digest[i * 4 + 1] = byte(s >> 8)
digest[i * 4 + 2] = byte(s >> 16)
digest[i * 4 + 3] = byte(s >> 24)
}
copy(hash[:], digest[:])
}
hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
if !load_at_once {
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_20(ctx, buf[:]), ok
}
}
return [20]byte{}, false
}
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
hash_bytes_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [40]byte {
hash: [40]byte
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([40]byte, bool) {
hash: [40]byte
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_40 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([40]byte, bool) {
if !load_at_once {
return hash_stream_odin_40(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_40(ctx, buf[:]), ok
}
}
return [40]byte{}, false
}
@(private)
_create_ripemd_ctx :: #force_inline proc(hash_size: int) {
switch hash_size {
case 16:
ctx: Ripemd128_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._16
case 20:
ctx: Ripemd160_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._20
case 32:
ctx: Ripemd256_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._32
case 40:
ctx: Ripemd320_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._40
}
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
#partial switch ctx.hash_size {
case ._16:
_create_ripemd_ctx(16)
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
init_odin(&c)
}
case ._20:
_create_ripemd_ctx(20)
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
init_odin(&c)
}
case ._32:
_create_ripemd_ctx(32)
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
init_odin(&c)
}
case ._40:
_create_ripemd_ctx(40)
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
init_odin(&c)
}
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
#partial switch ctx.hash_size {
case ._16:
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
update_odin(&c, data)
}
case ._20:
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
update_odin(&c, data)
}
case ._32:
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
update_odin(&c, data)
}
case ._40:
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
update_odin(&c, data)
}
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
#partial switch ctx.hash_size {
case ._16:
if c, ok := ctx.internal_ctx.(Ripemd128_Context); ok {
final_odin(&c, hash)
}
case ._20:
if c, ok := ctx.internal_ctx.(Ripemd160_Context); ok {
final_odin(&c, hash)
}
case ._32:
if c, ok := ctx.internal_ctx.(Ripemd256_Context); ok {
final_odin(&c, hash)
}
case ._40:
if c, ok := ctx.internal_ctx.(Ripemd320_Context); ok {
final_odin(&c, hash)
}
}
}
/*
RIPEMD implementation
@@ -574,20 +557,6 @@ RIPEMD_160_R1 := [80]uint {
8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11,
}
init_odin :: proc(ctx: ^$T) {
when T == Ripemd128_Context {
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
} else when T == Ripemd160_Context {
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
} else when T == Ripemd256_Context {
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3] = S0, S1, S2, S3
ctx.s[4], ctx.s[5], ctx.s[6], ctx.s[7] = S5, S6, S7, S8
} else when T == Ripemd320_Context {
ctx.s[0], ctx.s[1], ctx.s[2], ctx.s[3], ctx.s[4] = S0, S1, S2, S3, S4
ctx.s[5], ctx.s[6], ctx.s[7], ctx.s[8], ctx.s[9] = S5, S6, S7, S8, S9
}
}
block :: #force_inline proc (ctx: ^$T, p: []byte) -> int {
when T == Ripemd128_Context {
return ripemd_128_block(ctx, p)
@@ -948,101 +917,3 @@ ripemd_320_block :: proc(ctx: ^$T, p: []byte) -> int {
}
return n
}
update_odin :: proc(ctx: ^$T, p: []byte) {
ctx.tc += u64(len(p))
p := p
if ctx.nx > 0 {
n := len(p)
when T == Ripemd128_Context {
if n > RIPEMD_128_BLOCK_SIZE - ctx.nx {
n = RIPEMD_128_BLOCK_SIZE - ctx.nx
}
} else when T == Ripemd160_Context {
if n > RIPEMD_160_BLOCK_SIZE - ctx.nx {
n = RIPEMD_160_BLOCK_SIZE - ctx.nx
}
} else when T == Ripemd256_Context{
if n > RIPEMD_256_BLOCK_SIZE - ctx.nx {
n = RIPEMD_256_BLOCK_SIZE - ctx.nx
}
} else when T == Ripemd320_Context{
if n > RIPEMD_320_BLOCK_SIZE - ctx.nx {
n = RIPEMD_320_BLOCK_SIZE - ctx.nx
}
}
for i := 0; i < n; i += 1 {
ctx.x[ctx.nx + i] = p[i]
}
ctx.nx += n
when T == Ripemd128_Context {
if ctx.nx == RIPEMD_128_BLOCK_SIZE {
block(ctx, ctx.x[0:])
ctx.nx = 0
}
} else when T == Ripemd160_Context {
if ctx.nx == RIPEMD_160_BLOCK_SIZE {
block(ctx, ctx.x[0:])
ctx.nx = 0
}
} else when T == Ripemd256_Context{
if ctx.nx == RIPEMD_256_BLOCK_SIZE {
block(ctx, ctx.x[0:])
ctx.nx = 0
}
} else when T == Ripemd320_Context{
if ctx.nx == RIPEMD_320_BLOCK_SIZE {
block(ctx, ctx.x[0:])
ctx.nx = 0
}
}
p = p[n:]
}
n := block(ctx, p)
p = p[n:]
if len(p) > 0 {
ctx.nx = copy(ctx.x[:], p)
}
}
final_odin :: proc(ctx: ^$T, hash: []byte) {
d := ctx
tc := d.tc
tmp: [64]byte
tmp[0] = 0x80
if tc % 64 < 56 {
update_odin(d, tmp[0:56 - tc % 64])
} else {
update_odin(d, tmp[0:64 + 56 - tc % 64])
}
tc <<= 3
for i : u32 = 0; i < 8; i += 1 {
tmp[i] = byte(tc >> (8 * i))
}
update_odin(d, tmp[0:8])
when T == Ripemd128_Context {
size :: RIPEMD_128_SIZE
} else when T == Ripemd160_Context {
size :: RIPEMD_160_SIZE
} else when T == Ripemd256_Context{
size :: RIPEMD_256_SIZE
} else when T == Ripemd320_Context{
size :: RIPEMD_320_SIZE
}
digest: [size]byte
for s, i in d.s {
digest[i * 4] = byte(s)
digest[i * 4 + 1] = byte(s >> 8)
digest[i * 4 + 2] = byte(s >> 16)
digest[i * 4 + 3] = byte(s >> 24)
}
copy(hash[:], digest[:])
}
+110 -190
View File
@@ -6,7 +6,6 @@ package sha1
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the SHA1 hashing algorithm, as defined in RFC 3174 <https://datatracker.ietf.org/doc/html/rfc3174>
*/
@@ -16,77 +15,78 @@ import "core:os"
import "core:io"
import "../util"
import "../botan"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_20 = hash_bytes_odin
ctx.hash_file_20 = hash_file_odin
ctx.hash_stream_20 = hash_stream_odin
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA1)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE :: 20
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [20]byte {
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [20]byte {
_create_sha1_ctx()
return _hash_impl->hash_bytes_20(data)
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Sha1_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
ctx: Sha1_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([20]byte, bool) {
_create_sha1_ctx()
return _hash_impl->hash_stream_20(s)
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Sha1_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
_create_sha1_ctx()
return _hash_impl->hash_file_20(hd, load_at_once)
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -94,92 +94,78 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
init :: proc(ctx: ^Sha1_Context) {
ctx.state[0] = 0x67452301
ctx.state[1] = 0xefcdab89
ctx.state[2] = 0x98badcfe
ctx.state[3] = 0x10325476
ctx.state[4] = 0xc3d2e1f0
ctx.k[0] = 0x5a827999
ctx.k[1] = 0x6ed9eba1
ctx.k[2] = 0x8f1bbcdc
ctx.k[3] = 0xca62c1d6
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
update :: proc(ctx: ^Sha1_Context, data: []byte) {
for i := 0; i < len(data); i += 1 {
ctx.data[ctx.datalen] = data[i]
ctx.datalen += 1
if (ctx.datalen == BLOCK_SIZE) {
transform(ctx, ctx.data[:])
ctx.bitlen += 512
ctx.datalen = 0
}
}
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
final :: proc(ctx: ^Sha1_Context, hash: []byte) {
i := ctx.datalen
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
hash: [20]byte
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
hash: [20]byte
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
if ctx.datalen < 56 {
ctx.data[i] = 0x80
i += 1
for i < 56 {
ctx.data[i] = 0x00
i += 1
}
}
return [20]byte{}, false
}
}
else {
ctx.data[i] = 0x80
i += 1
for i < BLOCK_SIZE {
ctx.data[i] = 0x00
i += 1
}
transform(ctx, ctx.data[:])
mem.set(&ctx.data, 0, 56)
}
@(private)
_create_sha1_ctx :: #force_inline proc() {
ctx: Sha1_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._20
}
ctx.bitlen += u64(ctx.datalen * 8)
ctx.data[63] = u8(ctx.bitlen)
ctx.data[62] = u8(ctx.bitlen >> 8)
ctx.data[61] = u8(ctx.bitlen >> 16)
ctx.data[60] = u8(ctx.bitlen >> 24)
ctx.data[59] = u8(ctx.bitlen >> 32)
ctx.data[58] = u8(ctx.bitlen >> 40)
ctx.data[57] = u8(ctx.bitlen >> 48)
ctx.data[56] = u8(ctx.bitlen >> 56)
transform(ctx, ctx.data[:])
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_sha1_ctx()
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
init_odin(&c)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Sha1_Context); ok {
final_odin(&c, hash)
}
for j: u32 = 0; j < 4; j += 1 {
hash[j] = u8(ctx.state[0] >> (24 - j * 8)) & 0x000000ff
hash[j + 4] = u8(ctx.state[1] >> (24 - j * 8)) & 0x000000ff
hash[j + 8] = u8(ctx.state[2] >> (24 - j * 8)) & 0x000000ff
hash[j + 12] = u8(ctx.state[3] >> (24 - j * 8)) & 0x000000ff
hash[j + 16] = u8(ctx.state[4] >> (24 - j * 8)) & 0x000000ff
}
}
/*
@@ -258,69 +244,3 @@ transform :: proc(ctx: ^Sha1_Context, data: []byte) {
ctx.state[3] += d
ctx.state[4] += e
}
init_odin :: proc(ctx: ^Sha1_Context) {
ctx.state[0] = 0x67452301
ctx.state[1] = 0xefcdab89
ctx.state[2] = 0x98badcfe
ctx.state[3] = 0x10325476
ctx.state[4] = 0xc3d2e1f0
ctx.k[0] = 0x5a827999
ctx.k[1] = 0x6ed9eba1
ctx.k[2] = 0x8f1bbcdc
ctx.k[3] = 0xca62c1d6
}
update_odin :: proc(ctx: ^Sha1_Context, data: []byte) {
for i := 0; i < len(data); i += 1 {
ctx.data[ctx.datalen] = data[i]
ctx.datalen += 1
if (ctx.datalen == BLOCK_SIZE) {
transform(ctx, ctx.data[:])
ctx.bitlen += 512
ctx.datalen = 0
}
}
}
final_odin :: proc(ctx: ^Sha1_Context, hash: []byte) {
i := ctx.datalen
if ctx.datalen < 56 {
ctx.data[i] = 0x80
i += 1
for i < 56 {
ctx.data[i] = 0x00
i += 1
}
}
else {
ctx.data[i] = 0x80
i += 1
for i < BLOCK_SIZE {
ctx.data[i] = 0x00
i += 1
}
transform(ctx, ctx.data[:])
mem.set(&ctx.data, 0, 56)
}
ctx.bitlen += u64(ctx.datalen * 8)
ctx.data[63] = u8(ctx.bitlen)
ctx.data[62] = u8(ctx.bitlen >> 8)
ctx.data[61] = u8(ctx.bitlen >> 16)
ctx.data[60] = u8(ctx.bitlen >> 24)
ctx.data[59] = u8(ctx.bitlen >> 32)
ctx.data[58] = u8(ctx.bitlen >> 40)
ctx.data[57] = u8(ctx.bitlen >> 48)
ctx.data[56] = u8(ctx.bitlen >> 56)
transform(ctx, ctx.data[:])
for j: u32 = 0; j < 4; j += 1 {
hash[j] = u8(ctx.state[0] >> (24 - j * 8)) & 0x000000ff
hash[j + 4] = u8(ctx.state[1] >> (24 - j * 8)) & 0x000000ff
hash[j + 8] = u8(ctx.state[2] >> (24 - j * 8)) & 0x000000ff
hash[j + 12] = u8(ctx.state[3] >> (24 - j * 8)) & 0x000000ff
hash[j + 16] = u8(ctx.state[4] >> (24 - j * 8)) & 0x000000ff
}
}
+327 -430
View File
@@ -6,7 +6,6 @@ package sha2
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the SHA2 hashing algorithm, as defined in <https://csrc.nist.gov/csrc/media/publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf>
and in RFC 3874 <https://datatracker.ietf.org/doc/html/rfc3874>
@@ -17,102 +16,84 @@ import "core:os"
import "core:io"
import "../util"
import "../botan"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_28 = hash_bytes_odin_28
ctx.hash_file_28 = hash_file_odin_28
ctx.hash_stream_28 = hash_stream_odin_28
ctx.hash_bytes_32 = hash_bytes_odin_32
ctx.hash_file_32 = hash_file_odin_32
ctx.hash_stream_32 = hash_stream_odin_32
ctx.hash_bytes_48 = hash_bytes_odin_48
ctx.hash_file_48 = hash_file_odin_48
ctx.hash_stream_48 = hash_stream_odin_48
ctx.hash_bytes_64 = hash_bytes_odin_64
ctx.hash_file_64 = hash_file_odin_64
ctx.hash_stream_64 = hash_stream_odin_64
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA2)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
@(private)
_create_sha256_ctx :: #force_inline proc(is224: bool) {
ctx: Sha256_Context
ctx.is224 = is224
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = is224 ? ._28 : ._32
}
@(private)
_create_sha512_ctx :: #force_inline proc(is384: bool) {
ctx: Sha512_Context
ctx.is384 = is384
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = is384 ? ._48 : ._64
}
/*
High level API
*/
DIGEST_SIZE_224 :: 28
DIGEST_SIZE_256 :: 32
DIGEST_SIZE_384 :: 48
DIGEST_SIZE_512 :: 64
// hash_string_224 will hash the given input and return the
// computed hash
hash_string_224 :: proc(data: string) -> [28]byte {
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// hash_bytes_224 will hash the given input and return the
// computed hash
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
_create_sha256_ctx(true)
return _hash_impl->hash_bytes_28(data)
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
hash: [DIGEST_SIZE_224]byte
ctx: Sha256_Context
ctx.is224 = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_224 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_224 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
ctx: Sha256_Context
ctx.is224 = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
_create_sha256_ctx(true)
return _hash_impl->hash_stream_28(s)
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
hash: [DIGEST_SIZE_224]byte
ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_224 will read the file provided by the given handle
// and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
_create_sha256_ctx(true)
return _hash_impl->hash_file_28(hd, load_at_once)
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
@@ -120,33 +101,78 @@ hash_224 :: proc {
hash_file_224,
hash_bytes_224,
hash_string_224,
hash_bytes_to_buffer_224,
hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [32]byte {
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_sha256_ctx(false)
return _hash_impl->hash_bytes_32(data)
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: Sha256_Context
ctx.is224 = false
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
ctx: Sha256_Context
ctx.is224 = false
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_sha256_ctx(false)
return _hash_impl->hash_stream_32(s)
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_sha256_ctx(false)
return _hash_impl->hash_file_32(hd, load_at_once)
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -154,33 +180,78 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
hash_string_384 :: proc(data: string) -> [48]byte {
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
_create_sha512_ctx(true)
return _hash_impl->hash_bytes_48(data)
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
hash: [DIGEST_SIZE_384]byte
ctx: Sha512_Context
ctx.is384 = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_384 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_384 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
ctx: Sha512_Context
ctx.is384 = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
_create_sha512_ctx(true)
return _hash_impl->hash_stream_48(s)
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
hash: [DIGEST_SIZE_384]byte
ctx: Sha512_Context
ctx.is384 = true
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_384 will read the file provided by the given handle
// and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
_create_sha512_ctx(true)
return _hash_impl->hash_file_48(hd, load_at_once)
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
@@ -188,33 +259,78 @@ hash_384 :: proc {
hash_file_384,
hash_bytes_384,
hash_string_384,
hash_bytes_to_buffer_384,
hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
hash_string_512 :: proc(data: string) -> [64]byte {
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_sha512_ctx(false)
return _hash_impl->hash_bytes_64(data)
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
hash: [DIGEST_SIZE_512]byte
ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_512 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_512 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_sha512_ctx(false)
return _hash_impl->hash_stream_64(s)
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
hash: [DIGEST_SIZE_512]byte
ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_512 will read the file provided by the given handle
// and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_sha512_ctx(false)
return _hash_impl->hash_file_64(hd, load_at_once)
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -222,231 +338,129 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
hash_bytes_to_buffer_512,
hash_string_to_buffer_512,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
init :: proc(ctx: ^$T) {
when T == Sha256_Context {
if ctx.is224 {
ctx.h[0] = 0xc1059ed8
ctx.h[1] = 0x367cd507
ctx.h[2] = 0x3070dd17
ctx.h[3] = 0xf70e5939
ctx.h[4] = 0xffc00b31
ctx.h[5] = 0x68581511
ctx.h[6] = 0x64f98fa7
ctx.h[7] = 0xbefa4fa4
} else {
ctx.h[0] = 0x6a09e667
ctx.h[1] = 0xbb67ae85
ctx.h[2] = 0x3c6ef372
ctx.h[3] = 0xa54ff53a
ctx.h[4] = 0x510e527f
ctx.h[5] = 0x9b05688c
ctx.h[6] = 0x1f83d9ab
ctx.h[7] = 0x5be0cd19
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
if !load_at_once {
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_28(ctx, buf[:]), ok
} else when T == Sha512_Context {
if ctx.is384 {
ctx.h[0] = 0xcbbb9d5dc1059ed8
ctx.h[1] = 0x629a292a367cd507
ctx.h[2] = 0x9159015a3070dd17
ctx.h[3] = 0x152fecd8f70e5939
ctx.h[4] = 0x67332667ffc00b31
ctx.h[5] = 0x8eb44a8768581511
ctx.h[6] = 0xdb0c2e0d64f98fa7
ctx.h[7] = 0x47b5481dbefa4fa4
} else {
ctx.h[0] = 0x6a09e667f3bcc908
ctx.h[1] = 0xbb67ae8584caa73b
ctx.h[2] = 0x3c6ef372fe94f82b
ctx.h[3] = 0xa54ff53a5f1d36f1
ctx.h[4] = 0x510e527fade682d1
ctx.h[5] = 0x9b05688c2b3e6c1f
ctx.h[6] = 0x1f83d9abfb41bd6b
ctx.h[7] = 0x5be0cd19137e2179
}
}
return [28]byte{}, false
}
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
update :: proc(ctx: ^$T, data: []byte) {
length := uint(len(data))
block_nb: uint
new_len, rem_len, tmp_len: uint
shifted_message := make([]byte, length)
when T == Sha256_Context {
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
} else when T == Sha512_Context {
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
}
return hash
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
tmp_len = CURR_BLOCK_SIZE - ctx.length
rem_len = length < tmp_len ? length : tmp_len
copy(ctx.block[ctx.length:], data[:rem_len])
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
hash: [48]byte
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
hash: [48]byte
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_48(ctx, buf[:]), ok
}
}
return [48]byte{}, false
}
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
if ctx.hash_size == ._28 || ctx.hash_size == ._32 {
_create_sha256_ctx(ctx.hash_size == ._28)
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
init_odin(&c)
}
if ctx.length + length < CURR_BLOCK_SIZE {
ctx.length += length
return
}
if ctx.hash_size == ._48 || ctx.hash_size == ._64 {
_create_sha512_ctx(ctx.hash_size == ._48)
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
init_odin(&c)
}
}
new_len = length - rem_len
block_nb = new_len / CURR_BLOCK_SIZE
shifted_message = data[rem_len:]
sha2_transf(ctx, ctx.block[:], 1)
sha2_transf(ctx, shifted_message, block_nb)
rem_len = new_len % CURR_BLOCK_SIZE
when T == Sha256_Context {copy(ctx.block[:], shifted_message[block_nb << 6:rem_len])}
else when T == Sha512_Context {copy(ctx.block[:], shifted_message[block_nb << 7:rem_len])}
ctx.length = rem_len
when T == Sha256_Context {ctx.tot_len += (block_nb + 1) << 6}
else when T == Sha512_Context {ctx.tot_len += (block_nb + 1) << 7}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
#partial switch ctx.hash_size {
case ._28, ._32:
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
update_odin(&c, data)
}
case ._48, ._64:
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
update_odin(&c, data)
}
}
}
final :: proc(ctx: ^$T, hash: []byte) {
block_nb, pm_len, len_b: u32
i: i32
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
#partial switch ctx.hash_size {
case ._28, ._32:
if c, ok := ctx.internal_ctx.(Sha256_Context); ok {
final_odin(&c, hash)
}
case ._48, ._64:
if c, ok := ctx.internal_ctx.(Sha512_Context); ok {
final_odin(&c, hash)
}
}
when T == Sha256_Context {CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE}
else when T == Sha512_Context {CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE}
when T == Sha256_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 9) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
else when T == Sha512_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 17) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
len_b = u32(ctx.tot_len + ctx.length) << 3
when T == Sha256_Context {pm_len = block_nb << 6}
else when T == Sha512_Context {pm_len = block_nb << 7}
mem.set(rawptr(&(ctx.block[ctx.length:])[0]), 0, int(uint(pm_len) - ctx.length))
ctx.block[ctx.length] = 0x80
util.PUT_U32_BE(ctx.block[pm_len - 4:], len_b)
sha2_transf(ctx, ctx.block[:], uint(block_nb))
when T == Sha256_Context {
if ctx.is224 {
for i = 0; i < 7; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])}
} else {
for i = 0; i < 8; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])}
}
} else when T == Sha512_Context {
if ctx.is384 {
for i = 0; i < 6; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])}
} else {
for i = 0; i < 8; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])}
}
}
}
/*
@@ -590,50 +604,6 @@ PACK64 :: #force_inline proc "contextless"(b: []byte, x: ^u64) {
x^ = u64(b[7]) | u64(b[6]) << 8 | u64(b[5]) << 16 | u64(b[4]) << 24 | u64(b[3]) << 32 | u64(b[2]) << 40 | u64(b[1]) << 48 | u64(b[0]) << 56
}
init_odin :: proc(ctx: ^$T) {
when T == Sha256_Context {
if ctx.is224 {
ctx.h[0] = 0xc1059ed8
ctx.h[1] = 0x367cd507
ctx.h[2] = 0x3070dd17
ctx.h[3] = 0xf70e5939
ctx.h[4] = 0xffc00b31
ctx.h[5] = 0x68581511
ctx.h[6] = 0x64f98fa7
ctx.h[7] = 0xbefa4fa4
} else {
ctx.h[0] = 0x6a09e667
ctx.h[1] = 0xbb67ae85
ctx.h[2] = 0x3c6ef372
ctx.h[3] = 0xa54ff53a
ctx.h[4] = 0x510e527f
ctx.h[5] = 0x9b05688c
ctx.h[6] = 0x1f83d9ab
ctx.h[7] = 0x5be0cd19
}
} else when T == Sha512_Context {
if ctx.is384 {
ctx.h[0] = 0xcbbb9d5dc1059ed8
ctx.h[1] = 0x629a292a367cd507
ctx.h[2] = 0x9159015a3070dd17
ctx.h[3] = 0x152fecd8f70e5939
ctx.h[4] = 0x67332667ffc00b31
ctx.h[5] = 0x8eb44a8768581511
ctx.h[6] = 0xdb0c2e0d64f98fa7
ctx.h[7] = 0x47b5481dbefa4fa4
} else {
ctx.h[0] = 0x6a09e667f3bcc908
ctx.h[1] = 0xbb67ae8584caa73b
ctx.h[2] = 0x3c6ef372fe94f82b
ctx.h[3] = 0xa54ff53a5f1d36f1
ctx.h[4] = 0x510e527fade682d1
ctx.h[5] = 0x9b05688c2b3e6c1f
ctx.h[6] = 0x1f83d9abfb41bd6b
ctx.h[7] = 0x5be0cd19137e2179
}
}
}
sha2_transf :: proc(ctx: ^$T, data: []byte, block_nb: uint) {
when T == Sha256_Context {
w: [64]u32
@@ -710,76 +680,3 @@ sha2_transf :: proc(ctx: ^$T, data: []byte, block_nb: uint) {
}
}
}
update_odin :: proc(ctx: ^$T, data: []byte) {
length := uint(len(data))
block_nb: uint
new_len, rem_len, tmp_len: uint
shifted_message := make([]byte, length)
when T == Sha256_Context {
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
} else when T == Sha512_Context {
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
}
tmp_len = CURR_BLOCK_SIZE - ctx.length
rem_len = length < tmp_len ? length : tmp_len
copy(ctx.block[ctx.length:], data[:rem_len])
if ctx.length + length < CURR_BLOCK_SIZE {
ctx.length += length
return
}
new_len = length - rem_len
block_nb = new_len / CURR_BLOCK_SIZE
shifted_message = data[rem_len:]
sha2_transf(ctx, ctx.block[:], 1)
sha2_transf(ctx, shifted_message, block_nb)
rem_len = new_len % CURR_BLOCK_SIZE
when T == Sha256_Context {copy(ctx.block[:], shifted_message[block_nb << 6:rem_len])}
else when T == Sha512_Context {copy(ctx.block[:], shifted_message[block_nb << 7:rem_len])}
ctx.length = rem_len
when T == Sha256_Context {ctx.tot_len += (block_nb + 1) << 6}
else when T == Sha512_Context {ctx.tot_len += (block_nb + 1) << 7}
}
final_odin :: proc(ctx: ^$T, hash: []byte) {
block_nb, pm_len, len_b: u32
i: i32
when T == Sha256_Context {CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE}
else when T == Sha512_Context {CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE}
when T == Sha256_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 9) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
else when T == Sha512_Context {block_nb = 1 + ((CURR_BLOCK_SIZE - 17) < (ctx.length % CURR_BLOCK_SIZE) ? 1 : 0)}
len_b = u32(ctx.tot_len + ctx.length) << 3
when T == Sha256_Context {pm_len = block_nb << 6}
else when T == Sha512_Context {pm_len = block_nb << 7}
mem.set(rawptr(&(ctx.block[ctx.length:])[0]), 0, int(uint(pm_len) - ctx.length))
ctx.block[ctx.length] = 0x80
util.PUT_U32_BE(ctx.block[pm_len - 4:], len_b)
sha2_transf(ctx, ctx.block[:], uint(block_nb))
when T == Sha256_Context {
if ctx.is224 {
for i = 0; i < 7; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])}
} else {
for i = 0; i < 8; i += 1 {util.PUT_U32_BE(hash[i << 2:], ctx.h[i])}
}
} else when T == Sha512_Context {
if ctx.is384 {
for i = 0; i < 6; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])}
} else {
for i = 0; i < 8; i += 1 {util.PUT_U64_BE(hash[i << 3:], ctx.h[i])}
}
}
}
+233 -301
View File
@@ -6,7 +6,6 @@ package sha3
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Interface for the SHA3 hashing algorithm. The SHAKE functionality can be found in package shake.
If you wish to compute a Keccak hash, you can use the keccak package, it will use the original padding.
@@ -15,87 +14,85 @@ package sha3
import "core:os"
import "core:io"
import "../botan"
import "../_ctx"
import "../_sha3"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_28 = hash_bytes_odin_28
ctx.hash_file_28 = hash_file_odin_28
ctx.hash_stream_28 = hash_stream_odin_28
ctx.hash_bytes_32 = hash_bytes_odin_32
ctx.hash_file_32 = hash_file_odin_32
ctx.hash_stream_32 = hash_stream_odin_32
ctx.hash_bytes_48 = hash_bytes_odin_48
ctx.hash_file_48 = hash_file_odin_48
ctx.hash_stream_48 = hash_stream_odin_48
ctx.hash_bytes_64 = hash_bytes_odin_64
ctx.hash_file_64 = hash_file_odin_64
ctx.hash_stream_64 = hash_stream_odin_64
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHA3)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE_224 :: 28
DIGEST_SIZE_256 :: 32
DIGEST_SIZE_384 :: 48
DIGEST_SIZE_512 :: 64
// hash_string_224 will hash the given input and return the
// computed hash
hash_string_224 :: proc(data: string) -> [28]byte {
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// hash_bytes_224 will hash the given input and return the
// computed hash
hash_bytes_224 :: proc(data: []byte) -> [28]byte {
_create_sha3_ctx(28)
return _hash_impl->hash_bytes_28(data)
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
hash: [DIGEST_SIZE_224]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_224
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_224 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_224 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_224
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash)
}
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
_create_sha3_ctx(28)
return _hash_impl->hash_stream_28(s)
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
hash: [DIGEST_SIZE_224]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_224
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
}
// hash_file_224 will read the file provided by the given handle
// and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
_create_sha3_ctx(28)
return _hash_impl->hash_file_28(hd, load_at_once)
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
@@ -103,33 +100,78 @@ hash_224 :: proc {
hash_file_224,
hash_bytes_224,
hash_string_224,
hash_bytes_to_buffer_224,
hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [32]byte {
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_sha3_ctx(32)
return _hash_impl->hash_bytes_32(data)
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_256
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_256
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash)
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_sha3_ctx(32)
return _hash_impl->hash_stream_32(s)
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_256
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_sha3_ctx(32)
return _hash_impl->hash_file_32(hd, load_at_once)
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -137,33 +179,78 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
hash_string_384 :: proc(data: string) -> [48]byte {
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
hash_bytes_384 :: proc(data: []byte) -> [48]byte {
_create_sha3_ctx(48)
return _hash_impl->hash_bytes_48(data)
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
hash: [DIGEST_SIZE_384]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_384
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_384 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_384 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_384
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash)
}
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
_create_sha3_ctx(48)
return _hash_impl->hash_stream_48(s)
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
hash: [DIGEST_SIZE_384]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_384
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
}
// hash_file_384 will read the file provided by the given handle
// and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
_create_sha3_ctx(48)
return _hash_impl->hash_file_48(hd, load_at_once)
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
@@ -171,33 +258,78 @@ hash_384 :: proc {
hash_file_384,
hash_bytes_384,
hash_string_384,
hash_bytes_to_buffer_384,
hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
hash_string_512 :: proc(data: string) -> [64]byte {
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_sha3_ctx(64)
return _hash_impl->hash_bytes_64(data)
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
hash: [DIGEST_SIZE_512]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_512
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_512 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_512 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_512
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash)
}
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_sha3_ctx(64)
return _hash_impl->hash_stream_64(s)
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
hash: [DIGEST_SIZE_512]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_512
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.final(&ctx, hash[:])
return hash, true
}
// hash_file_512 will read the file provided by the given handle
// and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_sha3_ctx(64)
return _hash_impl->hash_file_64(hd, load_at_once)
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -205,224 +337,24 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
hash_bytes_to_buffer_512,
hash_string_to_buffer_512,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
Sha3_Context :: _sha3.Sha3_Context
init :: proc(ctx: ^_sha3.Sha3_Context) {
_sha3.init(ctx)
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
_sha3.update(ctx, data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [28]byte {
hash: [28]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
_sha3.update_odin(&c, data)
_sha3.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([28]byte, bool) {
hash: [28]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_sha3.update_odin(&c, buf[:read])
}
}
_sha3.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_28 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
if !load_at_once {
return hash_stream_odin_28(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_28(ctx, buf[:]), ok
}
}
return [28]byte{}, false
}
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
_sha3.update_odin(&c, data)
_sha3.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_sha3.update_odin(&c, buf[:read])
}
}
_sha3.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
hash_bytes_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [48]byte {
hash: [48]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
_sha3.update_odin(&c, data)
_sha3.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([48]byte, bool) {
hash: [48]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_sha3.update_odin(&c, buf[:read])
}
}
_sha3.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_48 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
if !load_at_once {
return hash_stream_odin_48(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_48(ctx, buf[:]), ok
}
}
return [48]byte{}, false
}
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
hash: [64]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
_sha3.update_odin(&c, data)
_sha3.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
hash: [64]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_sha3.update_odin(&c, buf[:read])
}
}
_sha3.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
@(private)
_create_sha3_ctx :: #force_inline proc(mdlen: int) {
ctx: _sha3.Sha3_Context
ctx.mdlen = mdlen
_hash_impl.internal_ctx = ctx
switch mdlen {
case 28: _hash_impl.hash_size = ._28
case 32: _hash_impl.hash_size = ._32
case 48: _hash_impl.hash_size = ._48
case 64: _hash_impl.hash_size = ._64
}
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
#partial switch ctx.hash_size {
case ._28: _create_sha3_ctx(28)
case ._32: _create_sha3_ctx(32)
case ._48: _create_sha3_ctx(48)
case ._64: _create_sha3_ctx(64)
}
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.final_odin(&c, hash)
}
final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
_sha3.final(ctx, hash)
}
+128 -194
View File
@@ -6,7 +6,6 @@ package shake
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Interface for the SHAKE hashing algorithm.
The SHA3 functionality can be found in package sha3.
@@ -15,81 +14,86 @@ package shake
import "core:os"
import "core:io"
import "../botan"
import "../_ctx"
import "../_sha3"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_16 = hash_bytes_odin_16
ctx.hash_file_16 = hash_file_odin_16
ctx.hash_stream_16 = hash_stream_odin_16
ctx.hash_bytes_32 = hash_bytes_odin_32
ctx.hash_file_32 = hash_file_odin_32
ctx.hash_stream_32 = hash_stream_odin_32
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_SHAKE)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE_128 :: 16
DIGEST_SIZE_256 :: 32
// hash_string_128 will hash the given input and return the
// computed hash
hash_string_128 :: proc(data: string) -> [16]byte {
hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128(transmute([]byte)(data))
}
// hash_bytes_128 will hash the given input and return the
// computed hash
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
_create_shake_ctx(16)
return _hash_impl->hash_bytes_16(data)
hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
hash: [DIGEST_SIZE_128]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_128
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.shake_xof(&ctx)
_sha3.shake_out(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_128 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_128 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_128
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.shake_xof(&ctx)
_sha3.shake_out(&ctx, hash)
}
// hash_stream_128 will read the stream in chunks and compute a
// hash from its contents
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_shake_ctx(16)
return _hash_impl->hash_stream_16(s)
hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
hash: [DIGEST_SIZE_128]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_128
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.shake_xof(&ctx)
_sha3.shake_out(&ctx, hash[:])
return hash, true
}
// hash_file_128 will read the file provided by the given handle
// and compute a hash
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_shake_ctx(16)
return _hash_impl->hash_file_16(hd, load_at_once)
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(buf[:]), ok
}
}
return [DIGEST_SIZE_128]byte{}, false
}
hash_128 :: proc {
@@ -97,33 +101,81 @@ hash_128 :: proc {
hash_file_128,
hash_bytes_128,
hash_string_128,
hash_bytes_to_buffer_128,
hash_string_to_buffer_128,
}
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [32]byte {
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_shake_ctx(32)
return _hash_impl->hash_bytes_32(data)
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_256
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.shake_xof(&ctx)
_sha3.shake_out(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_256
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.shake_xof(&ctx)
_sha3.shake_out(&ctx, hash)
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_shake_ctx(32)
return _hash_impl->hash_stream_32(s)
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
ctx.mdlen = DIGEST_SIZE_256
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
}
_sha3.shake_xof(&ctx)
_sha3.shake_out(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_shake_ctx(32)
return _hash_impl->hash_file_32(hd, load_at_once)
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -131,143 +183,25 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
Shake_Context :: _sha3.Sha3_Context
init :: proc(ctx: ^_sha3.Sha3_Context) {
_sha3.init(ctx)
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
update :: proc "contextless" (ctx: ^_sha3.Sha3_Context, data: []byte) {
_sha3.update(ctx, data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
hash: [16]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
_sha3.update_odin(&c, data)
_sha3.shake_xof_odin(&c)
_sha3.shake_out_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
hash: [16]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_sha3.update_odin(&c, buf[:read])
}
}
_sha3.shake_xof_odin(&c)
_sha3.shake_out_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_16(ctx, buf[:]), ok
}
}
return [16]byte{}, false
}
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
_sha3.update_odin(&c, data)
_sha3.shake_xof_odin(&c)
_sha3.shake_out_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_sha3.update_odin(&c, buf[:read])
}
}
_sha3.shake_xof_odin(&c)
_sha3.shake_out_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
@(private)
_create_shake_ctx :: #force_inline proc(mdlen: int) {
ctx: _sha3.Sha3_Context
ctx.mdlen = mdlen
_hash_impl.internal_ctx = ctx
switch mdlen {
case 16: _hash_impl.hash_size = ._16
case 32: _hash_impl.hash_size = ._32
}
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
#partial switch ctx.hash_size {
case ._16: _create_shake_ctx(16)
case ._32: _create_shake_ctx(32)
}
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.init_odin(&c)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(_sha3.Sha3_Context); ok {
_sha3.shake_xof_odin(&c)
_sha3.shake_out_odin(&c, hash[:])
}
final :: proc "contextless" (ctx: ^_sha3.Sha3_Context, hash: []byte) {
_sha3.shake_xof(ctx)
_sha3.shake_out(ctx, hash[:])
}
-487
View File
@@ -1,487 +0,0 @@
package skein
/*
Copyright 2021 zhibog
Made available under the BSD-3 license.
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the SKEIN hashing algorithm, as defined in <https://www.schneier.com/academic/skein/>
This package offers the internal state sizes of 256, 512 and 1024 bits and arbitrary output size.
*/
import "core:os"
import "core:io"
import "../botan"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
ctx.is_using_odin = false
} else {
_assign_hash_vtable(ctx)
ctx.is_using_odin = true
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
// @note(zh): Default to SKEIN-512
ctx.hash_bytes_slice = hash_bytes_skein512_odin
ctx.hash_file_slice = hash_file_skein512_odin
ctx.hash_stream_slice = hash_stream_skein512_odin
ctx.init = _init_skein512_odin
ctx.update = _update_skein512_odin
ctx.final = _final_skein512_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
_hash_impl.is_using_odin = false
// @note(zh): Botan only supports SKEIN-512.
botan.assign_hash_vtable(_hash_impl, botan.HASH_SKEIN_512)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
@(warning="SKEIN is not yet implemented in Odin. Botan bindings will be used")
use_odin :: #force_inline proc() {
// _hash_impl.is_using_odin = true
// _assign_hash_vtable(_hash_impl)
use_botan()
}
@(private)
_create_skein256_ctx :: #force_inline proc(size: int) {
_hash_impl.hash_size_val = size
if _hash_impl.is_using_odin {
ctx: Skein256_Context
ctx.h.bit_length = u64(size)
_hash_impl.internal_ctx = ctx
_hash_impl.hash_bytes_slice = hash_bytes_skein256_odin
_hash_impl.hash_file_slice = hash_file_skein256_odin
_hash_impl.hash_stream_slice = hash_stream_skein256_odin
_hash_impl.init = _init_skein256_odin
_hash_impl.update = _update_skein256_odin
_hash_impl.final = _final_skein256_odin
}
}
@(private)
_create_skein512_ctx :: #force_inline proc(size: int) {
_hash_impl.hash_size_val = size
if _hash_impl.is_using_odin {
ctx: Skein512_Context
ctx.h.bit_length = u64(size)
_hash_impl.internal_ctx = ctx
_hash_impl.hash_bytes_slice = hash_bytes_skein512_odin
_hash_impl.hash_file_slice = hash_file_skein512_odin
_hash_impl.hash_stream_slice = hash_stream_skein512_odin
_hash_impl.init = _init_skein512_odin
_hash_impl.update = _update_skein512_odin
_hash_impl.final = _final_skein512_odin
}
}
@(private)
_create_skein1024_ctx :: #force_inline proc(size: int) {
_hash_impl.hash_size_val = size
if _hash_impl.is_using_odin {
ctx: Skein1024_Context
ctx.h.bit_length = u64(size)
_hash_impl.internal_ctx = ctx
_hash_impl.hash_bytes_slice = hash_bytes_skein1024_odin
_hash_impl.hash_file_slice = hash_file_skein1024_odin
_hash_impl.hash_stream_slice = hash_stream_skein1024_odin
_hash_impl.init = _init_skein1024_odin
_hash_impl.update = _update_skein1024_odin
_hash_impl.final = _final_skein1024_odin
}
}
/*
High level API
*/
// hash_skein256_string will hash the given input and return the
// computed hash
hash_skein256_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
return hash_skein256_bytes(transmute([]byte)(data), bit_size, allocator)
}
// hash_skein256_bytes will hash the given input and return the
// computed hash
hash_skein256_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
_create_skein256_ctx(bit_size)
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
}
// hash_skein256_stream will read the stream in chunks and compute a
// hash from its contents
hash_skein256_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
_create_skein256_ctx(bit_size)
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
}
// hash_skein256_file will read the file provided by the given handle
// and compute a hash
hash_skein256_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
_create_skein256_ctx(bit_size)
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
}
hash_skein256 :: proc {
hash_skein256_stream,
hash_skein256_file,
hash_skein256_bytes,
hash_skein256_string,
}
// hash_skein512_string will hash the given input and return the
// computed hash
hash_skein512_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
return hash_skein512_bytes(transmute([]byte)(data), bit_size, allocator)
}
// hash_skein512_bytes will hash the given input and return the
// computed hash
hash_skein512_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
_create_skein512_ctx(bit_size)
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
}
// hash_skein512_stream will read the stream in chunks and compute a
// hash from its contents
hash_skein512_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
_create_skein512_ctx(bit_size)
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
}
// hash_skein512_file will read the file provided by the given handle
// and compute a hash
hash_skein512_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
_create_skein512_ctx(bit_size)
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
}
hash_skein512 :: proc {
hash_skein512_stream,
hash_skein512_file,
hash_skein512_bytes,
hash_skein512_string,
}
// hash_skein1024_string will hash the given input and return the
// computed hash
hash_skein1024_string :: proc(data: string, bit_size: int, allocator := context.allocator) -> []byte {
return hash_skein1024_bytes(transmute([]byte)(data), bit_size, allocator)
}
// hash_skein1024_bytes will hash the given input and return the
// computed hash
hash_skein1024_bytes :: proc(data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
_create_skein1024_ctx(bit_size)
return _hash_impl->hash_bytes_slice(data, bit_size, allocator)
}
// hash_skein1024_stream will read the stream in chunks and compute a
// hash from its contents
hash_skein1024_stream :: proc(s: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
_create_skein1024_ctx(bit_size)
return _hash_impl->hash_stream_slice(s, bit_size, allocator)
}
// hash_skein1024_file will read the file provided by the given handle
// and compute a hash
hash_skein1024_file :: proc(hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
_create_skein1024_ctx(bit_size)
return _hash_impl->hash_file_slice(hd, bit_size, load_at_once, allocator)
}
hash_skein1024 :: proc {
hash_skein1024_stream,
hash_skein1024_file,
hash_skein1024_bytes,
hash_skein1024_string,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
return hash
} else {
delete(hash)
return nil
}
}
hash_stream_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
delete(hash)
return nil, false
}
}
hash_file_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_skein256_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_skein256_odin(ctx, buf[:], bit_size, allocator), ok
}
}
return nil, false
}
hash_bytes_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
return hash
} else {
delete(hash)
return nil
}
}
hash_stream_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
delete(hash)
return nil, false
}
}
hash_file_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), ok
}
}
return nil, false
}
hash_bytes_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte, bit_size: int, allocator := context.allocator) -> []byte {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
return hash
} else {
delete(hash)
return nil
}
}
hash_stream_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream, bit_size: int, allocator := context.allocator) -> ([]byte, bool) {
hash := make([]byte, bit_size, allocator)
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
delete(hash)
return nil, false
}
}
hash_file_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, bit_size: int, load_at_once := false, allocator := context.allocator) -> ([]byte, bool) {
if !load_at_once {
return hash_stream_skein512_odin(ctx, os.stream_from_handle(hd), bit_size, allocator)
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_skein512_odin(ctx, buf[:], bit_size, allocator), ok
}
}
return nil, false
}
@(private)
_init_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_skein256_ctx(ctx.hash_size_val)
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
init_odin(&c)
}
}
@(private)
_update_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_skein256_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Skein256_Context); ok {
final_odin(&c, hash)
}
}
@(private)
_init_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_skein512_ctx(ctx.hash_size_val)
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
init_odin(&c)
}
}
@(private)
_update_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_skein512_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Skein512_Context); ok {
final_odin(&c, hash)
}
}
@(private)
_init_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_skein1024_ctx(ctx.hash_size_val)
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
init_odin(&c)
}
}
@(private)
_update_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_skein1024_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Skein1024_Context); ok {
final_odin(&c, hash)
}
}
/*
SKEIN implementation
*/
STATE_WORDS_256 :: 4
STATE_WORDS_512 :: 8
STATE_WORDS_1024 :: 16
STATE_BYTES_256 :: 32
STATE_BYTES_512 :: 64
STATE_BYTES_1024 :: 128
Skein_Header :: struct {
bit_length: u64,
bcnt: u64,
t: [2]u64,
}
Skein256_Context :: struct {
h: Skein_Header,
x: [STATE_WORDS_256]u64,
b: [STATE_BYTES_256]byte,
}
Skein512_Context :: struct {
h: Skein_Header,
x: [STATE_WORDS_512]u64,
b: [STATE_BYTES_512]byte,
}
Skein1024_Context :: struct {
h: Skein_Header,
x: [STATE_WORDS_1024]u64,
b: [STATE_BYTES_1024]byte,
}
init_odin :: proc(ctx: ^$T) {
}
update_odin :: proc(ctx: ^$T, data: []byte) {
}
final_odin :: proc(ctx: ^$T, hash: []byte) {
}
+104 -187
View File
@@ -6,7 +6,6 @@ package sm3
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the SM3 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02>
*/
@@ -15,77 +14,78 @@ import "core:os"
import "core:io"
import "../util"
import "../botan"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_32 = hash_bytes_odin
ctx.hash_file_32 = hash_file_odin
ctx.hash_stream_32 = hash_stream_odin
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_SM3)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE :: 32
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [32]byte {
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [32]byte {
_create_sm3_ctx()
return _hash_impl->hash_bytes_32(data)
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Sm3_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
ctx: Sm3_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_sm3_ctx()
return _hash_impl->hash_stream_32(s)
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Sm3_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_sm3_ctx()
return _hash_impl->hash_file_32(hd, load_at_once)
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -93,92 +93,72 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
init :: proc(ctx: ^Sm3_Context) {
ctx.state[0] = IV[0]
ctx.state[1] = IV[1]
ctx.state[2] = IV[2]
ctx.state[3] = IV[3]
ctx.state[4] = IV[4]
ctx.state[5] = IV[5]
ctx.state[6] = IV[6]
ctx.state[7] = IV[7]
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
}
update :: proc(ctx: ^Sm3_Context, data: []byte) {
data := data
ctx.length += u64(len(data))
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
if ctx.bitlength > 0 {
n := copy(ctx.x[ctx.bitlength:], data[:])
ctx.bitlength += u64(n)
if ctx.bitlength == 64 {
block(ctx, ctx.x[:])
ctx.bitlength = 0
}
final_odin(&c, hash[:])
return hash, true
data = data[n:]
}
if len(data) >= 64 {
n := len(data) &~ (64 - 1)
block(ctx, data[:n])
data = data[n:]
}
if len(data) > 0 {
ctx.bitlength = u64(copy(ctx.x[:], data[:]))
}
}
final :: proc(ctx: ^Sm3_Context, hash: []byte) {
length := ctx.length
pad: [64]byte
pad[0] = 0x80
if length % 64 < 56 {
update(ctx, pad[0: 56 - length % 64])
} else {
return hash, false
update(ctx, pad[0: 64 + 56 - length % 64])
}
}
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
length <<= 3
util.PUT_U64_BE(pad[:], length)
update(ctx, pad[0: 8])
assert(ctx.bitlength == 0)
@(private)
_create_sm3_ctx :: #force_inline proc() {
ctx: Sm3_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._32
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_sm3_ctx()
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
init_odin(&c)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Sm3_Context); ok {
final_odin(&c, hash)
}
util.PUT_U32_BE(hash[0:], ctx.state[0])
util.PUT_U32_BE(hash[4:], ctx.state[1])
util.PUT_U32_BE(hash[8:], ctx.state[2])
util.PUT_U32_BE(hash[12:], ctx.state[3])
util.PUT_U32_BE(hash[16:], ctx.state[4])
util.PUT_U32_BE(hash[20:], ctx.state[5])
util.PUT_U32_BE(hash[24:], ctx.state[6])
util.PUT_U32_BE(hash[28:], ctx.state[7])
}
/*
@@ -192,25 +172,11 @@ Sm3_Context :: struct {
length: u64,
}
BLOCK_SIZE_IN_BYTES :: 64
BLOCK_SIZE_IN_32 :: 16
IV := [8]u32 {
0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
}
init_odin :: proc(ctx: ^Sm3_Context) {
ctx.state[0] = IV[0]
ctx.state[1] = IV[1]
ctx.state[2] = IV[2]
ctx.state[3] = IV[3]
ctx.state[4] = IV[4]
ctx.state[5] = IV[5]
ctx.state[6] = IV[6]
ctx.state[7] = IV[7]
}
block :: proc "contextless" (ctx: ^Sm3_Context, buf: []byte) {
buf := buf
@@ -282,52 +248,3 @@ block :: proc "contextless" (ctx: ^Sm3_Context, buf: []byte) {
ctx.state[0], ctx.state[1], ctx.state[2], ctx.state[3] = state0, state1, state2, state3
ctx.state[4], ctx.state[5], ctx.state[6], ctx.state[7] = state4, state5, state6, state7
}
update_odin :: proc(ctx: ^Sm3_Context, data: []byte) {
data := data
ctx.length += u64(len(data))
if ctx.bitlength > 0 {
n := copy(ctx.x[ctx.bitlength:], data[:])
ctx.bitlength += u64(n)
if ctx.bitlength == 64 {
block(ctx, ctx.x[:])
ctx.bitlength = 0
}
data = data[n:]
}
if len(data) >= 64 {
n := len(data) &~ (64 - 1)
block(ctx, data[:n])
data = data[n:]
}
if len(data) > 0 {
ctx.bitlength = u64(copy(ctx.x[:], data[:]))
}
}
final_odin :: proc(ctx: ^Sm3_Context, hash: []byte) {
length := ctx.length
pad: [64]byte
pad[0] = 0x80
if length % 64 < 56 {
update_odin(ctx, pad[0: 56 - length % 64])
} else {
update_odin(ctx, pad[0: 64 + 56 - length % 64])
}
length <<= 3
util.PUT_U64_BE(pad[:], length)
update_odin(ctx, pad[0: 8])
assert(ctx.bitlength == 0)
util.PUT_U32_BE(hash[0:], ctx.state[0])
util.PUT_U32_BE(hash[4:], ctx.state[1])
util.PUT_U32_BE(hash[8:], ctx.state[2])
util.PUT_U32_BE(hash[12:], ctx.state[3])
util.PUT_U32_BE(hash[16:], ctx.state[4])
util.PUT_U32_BE(hash[20:], ctx.state[5])
util.PUT_U32_BE(hash[24:], ctx.state[6])
util.PUT_U32_BE(hash[28:], ctx.state[7])
}
+159 -238
View File
@@ -6,7 +6,6 @@ package streebog
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the Streebog hashing algorithm, standardized as GOST R 34.11-2012 in RFC 6986 <https://datatracker.ietf.org/doc/html/rfc6986>
*/
@@ -15,88 +14,82 @@ import "core:os"
import "core:io"
import "../util"
import "../botan"
import "../_ctx"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_32 = hash_bytes_odin_32
ctx.hash_file_32 = hash_file_odin_32
ctx.hash_stream_32 = hash_stream_odin_32
ctx.hash_bytes_64 = hash_bytes_odin_64
ctx.hash_file_64 = hash_file_odin_64
ctx.hash_stream_64 = hash_stream_odin_64
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_STREEBOG)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
@(private)
_create_streebog_ctx :: #force_inline proc(is256: bool) {
ctx: Streebog_Context
ctx.is256 = is256
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = is256 ? ._32 : ._64
}
/*
High level API
*/
DIGEST_SIZE_256 :: 32
DIGEST_SIZE_512 :: 64
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [32]byte {
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [32]byte {
_create_streebog_ctx(true)
return _hash_impl->hash_bytes_32(data)
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: Streebog_Context
ctx.is256 = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
ctx: Streebog_Context
ctx.is256 = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
_create_streebog_ctx(true)
return _hash_impl->hash_stream_32(s)
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: Streebog_Context
ctx.is256 = true
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
_create_streebog_ctx(true)
return _hash_impl->hash_file_32(hd, load_at_once)
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -104,33 +97,75 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
// hash_string_512 will hash the given input and return the
// computed hash
hash_string_512 :: proc(data: string) -> [64]byte {
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
hash_bytes_512 :: proc(data: []byte) -> [64]byte {
_create_streebog_ctx(false)
return _hash_impl->hash_bytes_64(data)
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
hash: [DIGEST_SIZE_512]byte
ctx: Streebog_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_512 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_512 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
ctx: Streebog_Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
}
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_streebog_ctx(false)
return _hash_impl->hash_stream_64(s)
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
hash: [DIGEST_SIZE_512]byte
ctx: Streebog_Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_512 will read the file provided by the given handle
// and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_streebog_ctx(false)
return _hash_impl->hash_file_64(hd, load_at_once)
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -138,126 +173,72 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
hash_bytes_to_buffer_512,
hash_string_to_buffer_512,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
init :: proc(ctx: ^Streebog_Context) {
if ctx.is256 {
ctx.hash_size = 256
for _, i in ctx.h {
ctx.h[i] = 0x01
}
} else {
ctx.hash_size = 512
}
ctx.v_512[1] = 0x02
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
update :: proc(ctx: ^Streebog_Context, data: []byte) {
length := u64(len(data))
chk_size: u64
data := data
for (length > 63) && (ctx.buf_size == 0) {
stage2(ctx, data)
data = data[64:]
length -= 64
}
for length != 0 {
chk_size = 64 - ctx.buf_size
if chk_size > length {
chk_size = length
}
copy(ctx.buffer[ctx.buf_size:], data[:chk_size])
ctx.buf_size += chk_size
length -= chk_size
data = data[chk_size:]
if ctx.buf_size == 64 {
stage2(ctx, ctx.buffer[:])
ctx.buf_size = 0
}
}
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
final :: proc(ctx: ^Streebog_Context, hash: []byte) {
t: [64]byte
t[1] = byte((ctx.buf_size * 8) >> 8) & 0xff
t[0] = byte((ctx.buf_size) * 8) & 0xff
hash_bytes_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [32]byte {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
padding(ctx)
hash_stream_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([32]byte, bool) {
hash: [32]byte
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
G(ctx.h[:], ctx.n[:], ctx.buffer[:])
hash_file_odin_32 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
if !load_at_once {
return hash_stream_odin_32(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_32(ctx, buf[:]), ok
}
}
return [32]byte{}, false
}
add_mod_512(ctx.n[:], t[:], ctx.n[:])
add_mod_512(ctx.sigma[:], ctx.buffer[:], ctx.sigma[:])
hash_bytes_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
init_odin(&c)
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
G(ctx.h[:], ctx.v_0[:], ctx.n[:])
G(ctx.h[:], ctx.v_0[:], ctx.sigma[:])
hash_stream_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_64 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin_64(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_64(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
_create_streebog_ctx(ctx.hash_size == ._32)
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
init_odin(&c)
}
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Streebog_Context); ok {
final_odin(&c, hash)
}
if ctx.is256 {
copy(hash[:], ctx.h[32:])
} else {
copy(hash[:], ctx.h[:])
}
}
/*
@@ -534,63 +515,3 @@ padding :: proc(ctx: ^Streebog_Context) {
copy(ctx.buffer[:], t[:])
}
}
init_odin :: proc(ctx: ^Streebog_Context) {
if ctx.is256 {
ctx.hash_size = 256
for _, i in ctx.h {
ctx.h[i] = 0x01
}
} else {
ctx.hash_size = 512
}
ctx.v_512[1] = 0x02
}
update_odin :: proc(ctx: ^Streebog_Context, data: []byte) {
length := u64(len(data))
chk_size: u64
data := data
for (length > 63) && (ctx.buf_size == 0) {
stage2(ctx, data)
data = data[64:]
length -= 64
}
for length != 0 {
chk_size = 64 - ctx.buf_size
if chk_size > length {
chk_size = length
}
copy(ctx.buffer[ctx.buf_size:], data[:chk_size])
ctx.buf_size += chk_size
length -= chk_size
data = data[chk_size:]
if ctx.buf_size == 64 {
stage2(ctx, ctx.buffer[:])
ctx.buf_size = 0
}
}
}
final_odin :: proc(ctx: ^Streebog_Context, hash: []byte) {
t: [64]byte
t[1] = byte((ctx.buf_size * 8) >> 8) & 0xff
t[0] = byte((ctx.buf_size) * 8) & 0xff
padding(ctx)
G(ctx.h[:], ctx.n[:], ctx.buffer[:])
add_mod_512(ctx.n[:], t[:], ctx.n[:])
add_mod_512(ctx.sigma[:], ctx.buffer[:], ctx.sigma[:])
G(ctx.h[:], ctx.v_0[:], ctx.n[:])
G(ctx.h[:], ctx.v_0[:], ctx.sigma[:])
if ctx.is256 {
copy(hash[:], ctx.h[32:])
} else {
copy(hash[:], ctx.h[:])
}
}
+180 -231
View File
@@ -6,7 +6,6 @@ package tiger
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Interface for the Tiger1 variant of the Tiger hashing algorithm as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
*/
@@ -14,84 +13,84 @@ package tiger
import "core:os"
import "core:io"
import "../botan"
import "../_ctx"
import "../_tiger"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_16 = hash_bytes_odin_16
ctx.hash_file_16 = hash_file_odin_16
ctx.hash_stream_16 = hash_stream_odin_16
ctx.hash_bytes_20 = hash_bytes_odin_20
ctx.hash_file_20 = hash_file_odin_20
ctx.hash_stream_20 = hash_stream_odin_20
ctx.hash_bytes_24 = hash_bytes_odin_24
ctx.hash_file_24 = hash_file_odin_24
ctx.hash_stream_24 = hash_stream_odin_24
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_TIGER)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE_128 :: 16
DIGEST_SIZE_160 :: 20
DIGEST_SIZE_192 :: 24
// hash_string_128 will hash the given input and return the
// computed hash
hash_string_128 :: proc(data: string) -> [16]byte {
hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128(transmute([]byte)(data))
}
// hash_bytes_128 will hash the given input and return the
// computed hash
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
_create_tiger_ctx(16)
return _hash_impl->hash_bytes_16(data)
hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
hash: [DIGEST_SIZE_128]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_128 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_128 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash)
}
// hash_stream_128 will read the stream in chunks and compute a
// hash from its contents
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_tiger_ctx(16)
return _hash_impl->hash_stream_16(s)
hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
hash: [DIGEST_SIZE_128]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
}
// hash_file_128 will read the file provided by the given handle
// and compute a hash
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_tiger_ctx(16)
return _hash_impl->hash_file_16(hd, load_at_once)
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(buf[:]), ok
}
}
return [DIGEST_SIZE_128]byte{}, false
}
hash_128 :: proc {
@@ -99,33 +98,78 @@ hash_128 :: proc {
hash_file_128,
hash_bytes_128,
hash_string_128,
hash_bytes_to_buffer_128,
hash_string_to_buffer_128,
}
// hash_string_160 will hash the given input and return the
// computed hash
hash_string_160 :: proc(data: string) -> [20]byte {
hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
return hash_bytes_160(transmute([]byte)(data))
}
// hash_bytes_160 will hash the given input and return the
// computed hash
hash_bytes_160 :: proc(data: []byte) -> [20]byte {
_create_tiger_ctx(20)
return _hash_impl->hash_bytes_20(data)
hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
hash: [DIGEST_SIZE_160]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_160 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_160 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_160 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash)
}
// hash_stream_160 will read the stream in chunks and compute a
// hash from its contents
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
_create_tiger_ctx(20)
return _hash_impl->hash_stream_20(s)
hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
hash: [DIGEST_SIZE_160]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
}
// hash_file_160 will read the file provided by the given handle
// and compute a hash
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
_create_tiger_ctx(20)
return _hash_impl->hash_file_20(hd, load_at_once)
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
if !load_at_once {
return hash_stream_160(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_160(buf[:]), ok
}
}
return [DIGEST_SIZE_160]byte{}, false
}
hash_160 :: proc {
@@ -133,33 +177,78 @@ hash_160 :: proc {
hash_file_160,
hash_bytes_160,
hash_string_160,
hash_bytes_to_buffer_160,
hash_string_to_buffer_160,
}
// hash_string_192 will hash the given input and return the
// computed hash
hash_string_192 :: proc(data: string) -> [24]byte {
hash_string_192 :: proc(data: string) -> [DIGEST_SIZE_192]byte {
return hash_bytes_192(transmute([]byte)(data))
}
// hash_bytes_192 will hash the given input and return the
// computed hash
hash_bytes_192 :: proc(data: []byte) -> [24]byte {
_create_tiger_ctx(24)
return _hash_impl->hash_bytes_24(data)
hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
hash: [DIGEST_SIZE_192]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_192 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_192 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_192(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_192 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_192 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_192, "Size of destination buffer is smaller than the digest size")
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash)
}
// hash_stream_192 will read the stream in chunks and compute a
// hash from its contents
hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
_create_tiger_ctx(24)
return _hash_impl->hash_stream_24(s)
hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
hash: [DIGEST_SIZE_192]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
}
// hash_file_192 will read the file provided by the given handle
// and compute a hash
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
_create_tiger_ctx(24)
return _hash_impl->hash_file_24(hd, load_at_once)
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_192]byte, bool) {
if !load_at_once {
return hash_stream_192(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_192(buf[:]), ok
}
}
return [DIGEST_SIZE_192]byte{}, false
}
hash_192 :: proc {
@@ -167,165 +256,25 @@ hash_192 :: proc {
hash_file_192,
hash_bytes_192,
hash_string_192,
hash_bytes_to_buffer_192,
hash_string_to_buffer_192,
}
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
hash: [16]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
_tiger.update_odin(&c, data)
_tiger.final_odin(&c, hash[:])
}
return hash
}
/*
Low level API
*/
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
hash: [16]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_tiger.update_odin(&c, buf[:read])
}
}
_tiger.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
Tiger_Context :: _tiger.Tiger_Context
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_16(ctx, buf[:]), ok
}
}
return [16]byte{}, false
}
hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
hash: [20]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
_tiger.update_odin(&c, data)
_tiger.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
hash: [20]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_tiger.update_odin(&c, buf[:read])
}
}
_tiger.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
if !load_at_once {
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_20(ctx, buf[:]), ok
}
}
return [20]byte{}, false
}
hash_bytes_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte {
hash: [24]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
_tiger.update_odin(&c, data)
_tiger.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([24]byte, bool) {
hash: [24]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_tiger.update_odin(&c, buf[:read])
}
}
_tiger.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
if !load_at_once {
return hash_stream_odin_24(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_24(ctx, buf[:]), ok
}
}
return [24]byte{}, false
}
@(private)
_create_tiger_ctx :: #force_inline proc(hash_size: int) {
ctx: _tiger.Tiger_Context
init :: proc(ctx: ^_tiger.Tiger_Context) {
ctx.ver = 1
_hash_impl.internal_ctx = ctx
switch hash_size {
case 16: _hash_impl.hash_size = ._16
case 20: _hash_impl.hash_size = ._20
case 24: _hash_impl.hash_size = ._24
}
_tiger.init(ctx)
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
#partial switch ctx.hash_size {
case ._16: _create_tiger_ctx(16)
case ._20: _create_tiger_ctx(20)
case ._24: _create_tiger_ctx(24)
}
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
}
update :: proc(ctx: ^_tiger.Tiger_Context, data: []byte) {
_tiger.update(ctx, data)
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.final_odin(&c, hash)
}
}
final :: proc(ctx: ^_tiger.Tiger_Context, hash: []byte) {
_tiger.final(ctx, hash)
}
+179 -230
View File
@@ -6,7 +6,6 @@ package tiger2
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Interface for the Tiger2 variant of the Tiger hashing algorithm as defined in <https://www.cs.technion.ac.il/~biham/Reports/Tiger/>
*/
@@ -14,84 +13,84 @@ package tiger2
import "core:os"
import "core:io"
import "../_ctx"
import "../_tiger"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_16 = hash_bytes_odin_16
ctx.hash_file_16 = hash_file_odin_16
ctx.hash_stream_16 = hash_stream_odin_16
ctx.hash_bytes_20 = hash_bytes_odin_20
ctx.hash_file_20 = hash_file_odin_20
ctx.hash_stream_20 = hash_stream_odin_20
ctx.hash_bytes_24 = hash_bytes_odin_24
ctx.hash_file_24 = hash_file_odin_24
ctx.hash_stream_24 = hash_stream_odin_24
ctx.init = _init_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan does nothing, since Tiger2 is not available in Botan
@(warning="Tiger2 is not provided by the Botan API. Odin implementation will be used")
use_botan :: #force_inline proc() {
use_odin()
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE_128 :: 16
DIGEST_SIZE_160 :: 20
DIGEST_SIZE_192 :: 24
// hash_string_128 will hash the given input and return the
// computed hash
hash_string_128 :: proc(data: string) -> [16]byte {
hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128(transmute([]byte)(data))
}
// hash_bytes_128 will hash the given input and return the
// computed hash
hash_bytes_128 :: proc(data: []byte) -> [16]byte {
_create_tiger2_ctx(16)
return _hash_impl->hash_bytes_16(data)
hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
hash: [DIGEST_SIZE_128]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_128 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_128 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash)
}
// hash_stream_128 will read the stream in chunks and compute a
// hash from its contents
hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
_create_tiger2_ctx(16)
return _hash_impl->hash_stream_16(s)
hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
hash: [DIGEST_SIZE_128]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
}
// hash_file_128 will read the file provided by the given handle
// and compute a hash
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
_create_tiger2_ctx(16)
return _hash_impl->hash_file_16(hd, load_at_once)
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(buf[:]), ok
}
}
return [DIGEST_SIZE_128]byte{}, false
}
hash_128 :: proc {
@@ -99,33 +98,78 @@ hash_128 :: proc {
hash_file_128,
hash_bytes_128,
hash_string_128,
hash_bytes_to_buffer_128,
hash_string_to_buffer_128,
}
// hash_string_160 will hash the given input and return the
// computed hash
hash_string_160 :: proc(data: string) -> [20]byte {
hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
return hash_bytes_160(transmute([]byte)(data))
}
// hash_bytes_160 will hash the given input and return the
// computed hash
hash_bytes_160 :: proc(data: []byte) -> [20]byte {
_create_tiger2_ctx(20)
return _hash_impl->hash_bytes_20(data)
hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
hash: [DIGEST_SIZE_160]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_160 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_160 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_160 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash)
}
// hash_stream_160 will read the stream in chunks and compute a
// hash from its contents
hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
_create_tiger2_ctx(20)
return _hash_impl->hash_stream_20(s)
hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
hash: [DIGEST_SIZE_160]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
}
// hash_file_160 will read the file provided by the given handle
// and compute a hash
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
_create_tiger2_ctx(20)
return _hash_impl->hash_file_20(hd, load_at_once)
hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
if !load_at_once {
return hash_stream_160(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_160(buf[:]), ok
}
}
return [DIGEST_SIZE_160]byte{}, false
}
hash_160 :: proc {
@@ -133,33 +177,78 @@ hash_160 :: proc {
hash_file_160,
hash_bytes_160,
hash_string_160,
hash_bytes_to_buffer_160,
hash_string_to_buffer_160,
}
// hash_string_192 will hash the given input and return the
// computed hash
hash_string_192 :: proc(data: string) -> [24]byte {
hash_string_192 :: proc(data: string) -> [DIGEST_SIZE_192]byte {
return hash_bytes_192(transmute([]byte)(data))
}
// hash_bytes_192 will hash the given input and return the
// computed hash
hash_bytes_192 :: proc(data: []byte) -> [24]byte {
_create_tiger2_ctx(24)
return _hash_impl->hash_bytes_24(data)
hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
hash: [DIGEST_SIZE_192]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_192 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_192 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_192(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_192 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_192 :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE_192, "Size of destination buffer is smaller than the digest size")
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
_tiger.update(&ctx, data)
_tiger.final(&ctx, hash)
}
// hash_stream_192 will read the stream in chunks and compute a
// hash from its contents
hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
_create_tiger2_ctx(24)
return _hash_impl->hash_stream_24(s)
hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
hash: [DIGEST_SIZE_192]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
}
_tiger.final(&ctx, hash[:])
return hash, true
}
// hash_file_192 will read the file provided by the given handle
// and compute a hash
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
_create_tiger2_ctx(24)
return _hash_impl->hash_file_24(hd, load_at_once)
hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_192]byte, bool) {
if !load_at_once {
return hash_stream_192(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_192(buf[:]), ok
}
}
return [DIGEST_SIZE_192]byte{}, false
}
hash_192 :: proc {
@@ -167,165 +256,25 @@ hash_192 :: proc {
hash_file_192,
hash_bytes_192,
hash_string_192,
hash_bytes_to_buffer_192,
hash_string_to_buffer_192,
}
hash_bytes_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [16]byte {
hash: [16]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
_tiger.update_odin(&c, data)
_tiger.final_odin(&c, hash[:])
}
return hash
}
/*
Low level API
*/
hash_stream_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([16]byte, bool) {
hash: [16]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_tiger.update_odin(&c, buf[:read])
}
}
_tiger.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
Tiger_Context :: _tiger.Tiger_Context
hash_file_odin_16 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
if !load_at_once {
return hash_stream_odin_16(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_16(ctx, buf[:]), ok
}
}
return [16]byte{}, false
}
hash_bytes_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [20]byte {
hash: [20]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
_tiger.update_odin(&c, data)
_tiger.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([20]byte, bool) {
hash: [20]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_tiger.update_odin(&c, buf[:read])
}
}
_tiger.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_20 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
if !load_at_once {
return hash_stream_odin_20(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_20(ctx, buf[:]), ok
}
}
return [20]byte{}, false
}
hash_bytes_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [24]byte {
hash: [24]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
_tiger.update_odin(&c, data)
_tiger.final_odin(&c, hash[:])
}
return hash
}
hash_stream_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([24]byte, bool) {
hash: [24]byte
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
_tiger.update_odin(&c, buf[:read])
}
}
_tiger.final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
hash_file_odin_24 :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
if !load_at_once {
return hash_stream_odin_24(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin_24(ctx, buf[:]), ok
}
}
return [24]byte{}, false
}
@(private)
_create_tiger2_ctx :: #force_inline proc(hash_size: int) {
ctx: _tiger.Tiger_Context
init :: proc(ctx: ^_tiger.Tiger_Context) {
ctx.ver = 2
_hash_impl.internal_ctx = ctx
switch hash_size {
case 16: _hash_impl.hash_size = ._16
case 20: _hash_impl.hash_size = ._20
case 24: _hash_impl.hash_size = ._24
}
_tiger.init(ctx)
}
@(private)
_init_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
#partial switch ctx.hash_size {
case ._16: _create_tiger2_ctx(16)
case ._20: _create_tiger2_ctx(20)
case ._24: _create_tiger2_ctx(24)
}
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.init_odin(&c)
}
update :: proc(ctx: ^_tiger.Tiger_Context, data: []byte) {
_tiger.update(ctx, data)
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(_tiger.Tiger_Context); ok {
_tiger.final_odin(&c, hash)
}
final :: proc(ctx: ^_tiger.Tiger_Context, hash: []byte) {
_tiger.final(ctx, hash)
}
+143 -207
View File
@@ -6,7 +6,6 @@ package whirlpool
List of contributors:
zhibog, dotbmp: Initial implementation.
Jeroen van Rijn: Context design to be able to change from Odin implementation to bindings.
Implementation of the Whirlpool hashing algorithm, as defined in <https://web.archive.org/web/20171129084214/http://www.larc.usp.br/~pbarreto/WhirlpoolPage.html>
*/
@@ -14,77 +13,79 @@ package whirlpool
import "core:os"
import "core:io"
import "../botan"
import "../_ctx"
import "../util"
/*
Context initialization and switching between the Odin implementation and the bindings
*/
USE_BOTAN_LIB :: bool(#config(USE_BOTAN_LIB, false))
@(private)
_init_vtable :: #force_inline proc() -> ^_ctx.Hash_Context {
ctx := _ctx._init_vtable()
when USE_BOTAN_LIB {
use_botan()
} else {
_assign_hash_vtable(ctx)
}
return ctx
}
@(private)
_assign_hash_vtable :: #force_inline proc(ctx: ^_ctx.Hash_Context) {
ctx.hash_bytes_64 = hash_bytes_odin
ctx.hash_file_64 = hash_file_odin
ctx.hash_stream_64 = hash_stream_odin
ctx.update = _update_odin
ctx.final = _final_odin
}
_hash_impl := _init_vtable()
// use_botan assigns the internal vtable of the hash context to use the Botan bindings
use_botan :: #force_inline proc() {
botan.assign_hash_vtable(_hash_impl, botan.HASH_WHIRLPOOL)
}
// use_odin assigns the internal vtable of the hash context to use the Odin implementation
use_odin :: #force_inline proc() {
_assign_hash_vtable(_hash_impl)
}
/*
High level API
*/
DIGEST_SIZE :: 64
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [64]byte {
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [64]byte {
_create_whirlpool_ctx()
return _hash_impl->hash_bytes_64(data)
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Whirlpool_Context
// init(&ctx) No-op
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
ctx: Whirlpool_Context
// init(&ctx) No-op
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
_create_whirlpool_ctx()
return _hash_impl->hash_stream_64(s)
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Whirlpool_Context
// init(&ctx) No-op
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = s->impl_read(buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
_create_whirlpool_ctx()
return _hash_impl->hash_file_64(hd, load_at_once)
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -92,82 +93,111 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
init :: proc(ctx: ^_ctx.Hash_Context) {
_hash_impl->init()
@(warning="Init is a no-op for Whirlpool")
init :: proc(ctx: ^Whirlpool_Context) {
// No action needed here
}
update :: proc(ctx: ^_ctx.Hash_Context, data: []byte) {
_hash_impl->update(data)
update :: proc(ctx: ^Whirlpool_Context, source: []byte) {
source_pos: int
nn := len(source)
source_bits := u64(nn * 8)
source_gap := u32((8 - (int(source_bits & 7))) & 7)
buffer_rem := uint(ctx.buffer_bits & 7)
b: u32
for i, carry, value := 31, u32(0), u32(source_bits); i >= 0 && (carry != 0 || value != 0); i -= 1 {
carry += u32(ctx.bitlength[i]) + (u32(value & 0xff))
ctx.bitlength[i] = byte(carry)
carry >>= 8
value >>= 8
}
for source_bits > 8 {
b = u32(u32((source[source_pos] << source_gap) & 0xff) | u32((source[source_pos+1] & 0xff) >> (8 - source_gap)))
ctx.buffer[ctx.buffer_pos] |= u8(b >> buffer_rem)
ctx.buffer_pos += 1
ctx.buffer_bits += int(8 - buffer_rem)
if ctx.buffer_bits == 512 {
transform(ctx)
ctx.buffer_bits = 0
ctx.buffer_pos = 0
}
ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem))
ctx.buffer_bits += int(buffer_rem)
source_bits -= 8
source_pos += 1
}
if source_bits > 0 {
b = u32((source[source_pos] << source_gap) & 0xff)
ctx.buffer[ctx.buffer_pos] |= byte(b) >> buffer_rem
} else {b = 0}
if u64(buffer_rem) + source_bits < 8 {
ctx.buffer_bits += int(source_bits)
} else {
ctx.buffer_pos += 1
ctx.buffer_bits += 8 - int(buffer_rem)
source_bits -= u64(8 - buffer_rem)
if ctx.buffer_bits == 512 {
transform(ctx)
ctx.buffer_bits = 0
ctx.buffer_pos = 0
}
ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem))
ctx.buffer_bits += int(source_bits)
}
}
final :: proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
_hash_impl->final(hash)
}
final :: proc(ctx: ^Whirlpool_Context, hash: []byte) {
n := ctx
n.buffer[n.buffer_pos] |= 0x80 >> (uint(n.buffer_bits) & 7)
n.buffer_pos += 1
hash_bytes_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) -> [64]byte {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
update_odin(&c, data)
final_odin(&c, hash[:])
}
return hash
}
if n.buffer_pos > 64 - 32 {
if n.buffer_pos < 64 {
for i := 0; i < 64 - n.buffer_pos; i += 1 {
n.buffer[n.buffer_pos + i] = 0
}
}
transform(ctx)
n.buffer_pos = 0
}
hash_stream_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, fs: io.Stream) -> ([64]byte, bool) {
hash: [64]byte
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = fs->impl_read(buf)
if read > 0 {
update_odin(&c, buf[:read])
}
}
final_odin(&c, hash[:])
return hash, true
} else {
return hash, false
}
}
if n.buffer_pos < 64 - 32 {
for i := 0; i < (64 - 32) - n.buffer_pos; i += 1 {
n.buffer[n.buffer_pos + i] = 0
}
}
n.buffer_pos = 64 - 32
hash_file_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
if !load_at_once {
return hash_stream_odin(ctx, os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_odin(ctx, buf[:]), ok
}
}
return [64]byte{}, false
}
for i := 0; i < 32; i += 1 {
n.buffer[n.buffer_pos + i] = n.bitlength[i]
}
transform(ctx)
@(private)
_create_whirlpool_ctx :: #force_inline proc() {
ctx: Whirlpool_Context
_hash_impl.internal_ctx = ctx
_hash_impl.hash_size = ._64
}
@(private)
_update_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, data: []byte) {
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
update_odin(&c, data)
}
}
@(private)
_final_odin :: #force_inline proc(ctx: ^_ctx.Hash_Context, hash: []byte) {
if c, ok := ctx.internal_ctx.(Whirlpool_Context); ok {
final_odin(&c, hash)
}
for i := 0; i < 8; i += 1 {
hash[i * 8] = byte(n.hash[i] >> 56)
hash[i * 8 + 1] = byte(n.hash[i] >> 48)
hash[i * 8 + 2] = byte(n.hash[i] >> 40)
hash[i * 8 + 3] = byte(n.hash[i] >> 32)
hash[i * 8 + 4] = byte(n.hash[i] >> 24)
hash[i * 8 + 5] = byte(n.hash[i] >> 16)
hash[i * 8 + 6] = byte(n.hash[i] >> 8)
hash[i * 8 + 7] = byte(n.hash[i])
}
}
/*
@@ -774,97 +804,3 @@ transform :: proc (ctx: ^Whirlpool_Context) {
}
for i := 0; i < 8; i += 1 {ctx.hash[i] ~= state[i] ~ block[i]}
}
update_odin :: proc(ctx: ^Whirlpool_Context, source: []byte) {
source_pos: int
nn := len(source)
source_bits := u64(nn * 8)
source_gap := u32((8 - (int(source_bits & 7))) & 7)
buffer_rem := uint(ctx.buffer_bits & 7)
b: u32
for i, carry, value := 31, u32(0), u32(source_bits); i >= 0 && (carry != 0 || value != 0); i -= 1 {
carry += u32(ctx.bitlength[i]) + (u32(value & 0xff))
ctx.bitlength[i] = byte(carry)
carry >>= 8
value >>= 8
}
for source_bits > 8 {
b = u32(u32((source[source_pos] << source_gap) & 0xff) | u32((source[source_pos+1] & 0xff) >> (8 - source_gap)))
ctx.buffer[ctx.buffer_pos] |= u8(b >> buffer_rem)
ctx.buffer_pos += 1
ctx.buffer_bits += int(8 - buffer_rem)
if ctx.buffer_bits == 512 {
transform(ctx)
ctx.buffer_bits = 0
ctx.buffer_pos = 0
}
ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem))
ctx.buffer_bits += int(buffer_rem)
source_bits -= 8
source_pos += 1
}
if source_bits > 0 {
b = u32((source[source_pos] << source_gap) & 0xff)
ctx.buffer[ctx.buffer_pos] |= byte(b) >> buffer_rem
} else {b = 0}
if u64(buffer_rem) + source_bits < 8 {
ctx.buffer_bits += int(source_bits)
} else {
ctx.buffer_pos += 1
ctx.buffer_bits += 8 - int(buffer_rem)
source_bits -= u64(8 - buffer_rem)
if ctx.buffer_bits == 512 {
transform(ctx)
ctx.buffer_bits = 0
ctx.buffer_pos = 0
}
ctx.buffer[ctx.buffer_pos] = byte(b << (8 - buffer_rem))
ctx.buffer_bits += int(source_bits)
}
}
final_odin :: proc(ctx: ^Whirlpool_Context, hash: []byte) {
n := ctx
n.buffer[n.buffer_pos] |= 0x80 >> (uint(n.buffer_bits) & 7)
n.buffer_pos += 1
if n.buffer_pos > 64 - 32 {
if n.buffer_pos < 64 {
for i := 0; i < 64 - n.buffer_pos; i += 1 {
n.buffer[n.buffer_pos + i] = 0
}
}
transform(ctx)
n.buffer_pos = 0
}
if n.buffer_pos < 64 - 32 {
for i := 0; i < (64 - 32) - n.buffer_pos; i += 1 {
n.buffer[n.buffer_pos + i] = 0
}
}
n.buffer_pos = 64 - 32
for i := 0; i < 32; i += 1 {
n.buffer[n.buffer_pos + i] = n.bitlength[i]
}
transform(ctx)
for i := 0; i < 8; i += 1 {
hash[i * 8] = byte(n.hash[i] >> 56)
hash[i * 8 + 1] = byte(n.hash[i] >> 48)
hash[i * 8 + 2] = byte(n.hash[i] >> 40)
hash[i * 8 + 3] = byte(n.hash[i] >> 32)
hash[i * 8 + 4] = byte(n.hash[i] >> 24)
hash[i * 8 + 5] = byte(n.hash[i] >> 16)
hash[i * 8 + 6] = byte(n.hash[i] >> 8)
hash[i * 8 + 7] = byte(n.hash[i])
}
}
+126
View File
@@ -0,0 +1,126 @@
package x25519
import field "core:crypto/_fiat/field_curve25519"
import "core:mem"
SCALAR_SIZE :: 32
POINT_SIZE :: 32
_BASE_POINT: [32]byte = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
_scalar_bit :: #force_inline proc "contextless" (s: ^[32]byte, i: int) -> u8 {
if i < 0 {
return 0
}
return (s[i>>3] >> uint(i&7)) & 1
}
_scalarmult :: proc (out, scalar, point: ^[32]byte) {
// Montgomery pseduo-multiplication taken from Monocypher.
// computes the scalar product
x1: field.Tight_Field_Element = ---
field.fe_from_bytes(&x1, point)
// computes the actual scalar product (the result is in x2 and z2)
x2, x3, z2, z3: field.Tight_Field_Element = ---, ---, ---, ---
t0, t1: field.Loose_Field_Element = ---, ---
// Montgomery ladder
// In projective coordinates, to avoid divisions: x = X / Z
// We don't care about the y coordinate, it's only 1 bit of information
field.fe_one(&x2) // "zero" point
field.fe_zero(&z2)
field.fe_set(&x3, &x1) // "one" point
field.fe_one(&z3)
swap: int
for pos := 255-1; pos >= 0; pos = pos - 1 {
// constant time conditional swap before ladder step
b := int(_scalar_bit(scalar, pos))
swap ~= b // xor trick avoids swapping at the end of the loop
field.fe_cond_swap(&x2, &x3, swap)
field.fe_cond_swap(&z2, &z3, swap)
swap = b // anticipates one last swap after the loop
// Montgomery ladder step: replaces (P2, P3) by (P2*2, P2+P3)
// with differential addition
//
// Note: This deliberately omits reductions after add/sub operations
// if the result is only ever used as the input to a mul/square since
// the implementations of those can deal with non-reduced inputs.
//
// fe_tighten_cast is only used to store a fully reduced
// output in a Loose_Field_Element, or to provide such a
// Loose_Field_Element as a Tight_Field_Element argument.
field.fe_sub(&t0, &x3, &z3)
field.fe_sub(&t1, &x2, &z2)
field.fe_add(field.fe_relax_cast(&x2), &x2, &z2) // x2 - unreduced
field.fe_add(field.fe_relax_cast(&z2), &x3, &z3) // z2 - unreduced
field.fe_carry_mul(&z3, &t0, field.fe_relax_cast(&x2))
field.fe_carry_mul(&z2, field.fe_relax_cast(&z2), &t1) // z2 - reduced
field.fe_carry_square(field.fe_tighten_cast(&t0), &t1) // t0 - reduced
field.fe_carry_square(field.fe_tighten_cast(&t1), field.fe_relax_cast(&x2)) // t1 - reduced
field.fe_add(field.fe_relax_cast(&x3), &z3, &z2) // x3 - unreduced
field.fe_sub(field.fe_relax_cast(&z2), &z3, &z2) // z2 - unreduced
field.fe_carry_mul(&x2, &t1, &t0) // x2 - reduced
field.fe_sub(&t1, field.fe_tighten_cast(&t1), field.fe_tighten_cast(&t0)) // safe - t1/t0 is reduced
field.fe_carry_square(&z2, field.fe_relax_cast(&z2)) // z2 - reduced
field.fe_carry_scmul_121666(&z3, &t1)
field.fe_carry_square(&x3, field.fe_relax_cast(&x3)) // x3 - reduced
field.fe_add(&t0, field.fe_tighten_cast(&t0), &z3) // safe - t0 is reduced
field.fe_carry_mul(&z3, field.fe_relax_cast(&x1), field.fe_relax_cast(&z2))
field.fe_carry_mul(&z2, &t1, &t0)
}
// last swap is necessary to compensate for the xor trick
// Note: after this swap, P3 == P2 + P1.
field.fe_cond_swap(&x2, &x3, swap)
field.fe_cond_swap(&z2, &z3, swap)
// normalises the coordinates: x == X / Z
field.fe_carry_inv(&z2, field.fe_relax_cast(&z2))
field.fe_carry_mul(&x2, field.fe_relax_cast(&x2), field.fe_relax_cast(&z2))
field.fe_to_bytes(out, &x2)
mem.zero_explicit(&x1, size_of(x1))
mem.zero_explicit(&x2, size_of(x2))
mem.zero_explicit(&x3, size_of(x3))
mem.zero_explicit(&z2, size_of(z2))
mem.zero_explicit(&z3, size_of(z3))
mem.zero_explicit(&t0, size_of(t0))
mem.zero_explicit(&t1, size_of(t1))
}
scalarmult :: proc (dst, scalar, point: []byte) {
if len(scalar) != SCALAR_SIZE {
panic("crypto/x25519: invalid scalar size")
}
if len(point) != POINT_SIZE {
panic("crypto/x25519: invalid point size")
}
if len(dst) != POINT_SIZE {
panic("crypto/x25519: invalid destination point size")
}
// "clamp" the scalar
e: [32]byte = ---
copy_slice(e[:], scalar)
e[0] &= 248
e[31] &= 127
e[31] |= 64
p: [32]byte = ---
copy_slice(p[:], point)
d: [32]byte = ---
_scalarmult(&d, &e, &p)
copy_slice(dst, d[:])
mem.zero_explicit(&e, size_of(e))
mem.zero_explicit(&d, size_of(d))
}
scalarmult_basepoint :: proc (dst, scalar: []byte) {
// TODO/perf: Switch to using a precomputed table.
scalarmult(dst, scalar, _BASE_POINT[:])
}
+6 -6
View File
@@ -27,7 +27,7 @@
// Construction history, or BSP trees would make the format too large to serve its purpose.
// The facilities of the formats to store meta data should make the format flexible enough
// for most uses. Adding HxA support should be something anyone can do in a days work.
//
// Structure:
// ----------
// HxA is designed to be extremely simple to parse, and is therefore based around conventions. It has
@@ -45,17 +45,17 @@
// of a number of named layers. All layers in the stack have the same number of elements. Each layer
// describes one property of the primitive. Each layer can have multiple channels and each layer can
// store data of a different type.
//
// HaX stores 3 kinds of nodes
// - Pixel data.
// - Polygon geometry data.
// - Meta data only.
//
// Pixel Nodes stores pixels in a layer stack. A layer may store things like Albedo, Roughness,
// Reflectance, Light maps, Masks, Normal maps, and Displacement. Layers use the channels of the
// layers to store things like color. The length of the layer stack is determined by the type and
// dimensions stored in the
//
// Geometry data is stored in 3 separate layer stacks for: vertex data, corner data and face data. The
// vertex data stores things like verities, blend shapes, weight maps, and vertex colors. The first
// layer in a vertex stack has to be a 3 channel layer named "position" describing the base position
@@ -63,7 +63,7 @@
// for things like UV, normals, and adjacency. The first layer in a corner stack has to be a 1 channel
// integer layer named "index" describing the vertices used to form polygons. The last value in each
// polygon has a negative - 1 index to indicate the end of the polygon.
//
// Example:
// A quad and a tri with the vertex index:
// [0, 1, 2, 3] [1, 4, 2]
@@ -72,7 +72,7 @@
// The face stack stores values per face. the length of the face stack has to match the number of
// negative values in the index layer in the corner stack. The face stack can be used to store things
// like material index.
//
// Storage
// -------
// All data is stored in little endian byte order with no padding. The layout mirrors the structs
+4 -4
View File
@@ -18,7 +18,7 @@ Marshal_Error :: union {
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
b := strings.make_builder(allocator)
defer if err != nil || data == nil {
defer if err != .None {
strings.destroy_builder(&b)
}
@@ -27,7 +27,7 @@ marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: M
if len(b.buf) != 0 {
data = b.buf[:]
}
return
return data, .None
}
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
@@ -285,8 +285,8 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
case runtime.Type_Info_Integer:
switch info.endianness {
case .Platform: return false
case .Little: return ODIN_ENDIAN != "little"
case .Big: return ODIN_ENDIAN != "big"
case .Little: return ODIN_ENDIAN != .Little
case .Big: return ODIN_ENDIAN != .Big
}
}
return false
+4 -3
View File
@@ -106,6 +106,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
}
parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
err = .None
token := p.curr_token
#partial switch token.kind {
case .Null:
@@ -175,6 +176,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
}
parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
err = .None
expect_token(p, .Open_Bracket) or_return
array: Array
@@ -266,15 +268,14 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, er
break
}
}
return
return obj, .None
}
parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
expect_token(p, .Open_Brace) or_return
obj := parse_object_body(p, .Close_Brace) or_return
expect_token(p, .Close_Brace) or_return
value = obj
return
return obj, .None
}
+26 -25
View File
@@ -52,11 +52,11 @@ unmarshal_any :: proc(data: []byte, v: any, spec := DEFAULT_SPECIFICATION, alloc
if p.spec == .MJSON {
#partial switch p.curr_token.kind {
case .Ident, .String:
return unmarsal_object(&p, data, .EOF)
return unmarshal_object(&p, data, .EOF)
}
}
return unmarsal_value(&p, data)
return unmarshal_value(&p, data)
}
@@ -148,7 +148,7 @@ assign_float :: proc(val: any, f: $T) -> bool {
@(private)
unmarsal_string :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
val := val
switch dst in &val {
case string:
@@ -198,7 +198,7 @@ unmarsal_string :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Inf
@(private)
unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
token := p.curr_token
@@ -222,6 +222,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
advance_token(p)
return
case .False, .True:
advance_token(p)
if assign_bool(v, token.kind == .True) {
return
}
@@ -256,7 +257,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
case .Ident:
advance_token(p)
if p.spec == .MJSON {
if unmarsal_string(p, any{v.data, ti.id}, token.text, ti) {
if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) {
return nil
}
}
@@ -265,7 +266,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
case .String:
advance_token(p)
str := unquote_string(token, p.spec, p.allocator) or_return
if unmarsal_string(p, any{v.data, ti.id}, str, ti) {
if unmarshal_string_token(p, any{v.data, ti.id}, str, ti) {
return nil
}
delete(str, p.allocator)
@@ -273,10 +274,10 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
case .Open_Brace:
return unmarsal_object(p, v, .Close_Brace)
return unmarshal_object(p, v, .Close_Brace)
case .Open_Bracket:
return unmarsal_array(p, v)
return unmarshal_array(p, v)
case:
if p.spec != .JSON {
@@ -311,16 +312,16 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
@(private)
unmarsal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_location) -> Token {
unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_location) -> Token {
prev := p.curr_token
err := expect_token(p, kind)
assert(err == nil, "unmarsal_expect_token")
assert(err == nil, "unmarshal_expect_token")
return prev
}
@(private)
unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
if end_token == .Close_Brace {
@@ -341,7 +342,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
key, _ := parse_object_key(p, p.allocator)
defer delete(key, p.allocator)
unmarsal_expect_token(p, .Colon)
unmarshal_expect_token(p, .Colon)
fields := reflect.struct_fields_zipped(ti.id)
@@ -377,7 +378,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
field_ptr := rawptr(uintptr(v.data) + offset)
field := any{field_ptr, type.id}
unmarsal_value(p, field) or_return
unmarshal_value(p, field) or_return
if parse_comma(p) {
break struct_loop
@@ -406,11 +407,11 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
map_loop: for p.curr_token.kind != end_token {
key, _ := parse_object_key(p, p.allocator)
unmarsal_expect_token(p, .Colon)
unmarshal_expect_token(p, .Colon)
mem.zero_slice(elem_backing)
if err := unmarsal_value(p, map_backing_value); err != nil {
if err := unmarshal_value(p, map_backing_value); err != nil {
delete(key, p.allocator)
return err
}
@@ -442,7 +443,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
enumerated_array_loop: for p.curr_token.kind != end_token {
key, _ := parse_object_key(p, p.allocator)
unmarsal_expect_token(p, .Colon)
unmarshal_expect_token(p, .Colon)
defer delete(key, p.allocator)
index := -1
@@ -459,7 +460,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
index_ptr := rawptr(uintptr(v.data) + uintptr(index*t.elem_size))
index_any := any{index_ptr, t.elem.id}
unmarsal_value(p, index_any) or_return
unmarshal_value(p, index_any) or_return
if parse_comma(p) {
break enumerated_array_loop
@@ -479,10 +480,10 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
@(private)
unmarsal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
unmarshal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
p_backup := p^
p.allocator = mem.nil_allocator()
unmarsal_expect_token(p, .Open_Bracket)
unmarshal_expect_token(p, .Open_Bracket)
array_length_loop: for p.curr_token.kind != .Close_Bracket {
_, _ = parse_value(p)
length += 1
@@ -496,9 +497,9 @@ unmarsal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
}
@(private)
unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
unmarshal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
assign_array :: proc(p: ^Parser, base: rawptr, elem: ^reflect.Type_Info, length: uintptr) -> Unmarshal_Error {
unmarsal_expect_token(p, .Open_Bracket)
unmarshal_expect_token(p, .Open_Bracket)
for idx: uintptr = 0; p.curr_token.kind != .Close_Bracket; idx += 1 {
assert(idx < length)
@@ -506,14 +507,14 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
elem_ptr := rawptr(uintptr(base) + idx*uintptr(elem.size))
elem := any{elem_ptr, elem.id}
unmarsal_value(p, elem) or_return
unmarshal_value(p, elem) or_return
if parse_comma(p) {
break
}
}
unmarsal_expect_token(p, .Close_Bracket)
unmarshal_expect_token(p, .Close_Bracket)
return nil
@@ -523,7 +524,7 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
ti := reflect.type_info_base(type_info_of(v.id))
length := unmarsal_count_array(p)
length := unmarshal_count_array(p)
#partial switch t in ti.variant {
case reflect.Type_Info_Slice:
@@ -577,4 +578,4 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
}
return UNSUPPORTED_TYPE
}
}
+1 -1
View File
@@ -64,6 +64,7 @@ If not present, the width is whatever is necessary to represent the value.
Precision is specified after the (optional) width followed by a period followed by a decimal number.
If no period is present, a default precision is used.
A period with no following number specifies a precision of 0.
Examples:
%f default width, default precision
%8f width 8, default precision
@@ -84,7 +85,6 @@ Other flags:
add leading 0z for dozenal (%#z)
add leading 0x or 0X for hexadecimal (%#x or %#X)
remove leading 0x for %p (%#p)
' ' (space) leave a space for elided sign in numbers (% d)
0 pad with leading zeros rather than spaces
+36 -12
View File
@@ -11,6 +11,7 @@ import "core:time"
import "core:unicode/utf8"
import "core:intrinsics"
// Internal data structure that stores the required information for formatted printing
Info :: struct {
minus: bool,
plus: bool,
@@ -46,9 +47,13 @@ Register_User_Formatter_Error :: enum {
// it is prefixed with `_` rather than marked with a private attribute so that users can access it if necessary
_user_formatters: ^map[typeid]User_Formatter
// set_user_formatters assigns m to a global value allowing the user have custom print formatting for specific
// types
set_user_formatters :: proc(m: ^map[typeid]User_Formatter) {
_user_formatters = m
}
// register_user_formatter assigns a formatter to a specific typeid. set_user_formatters must be called
// before any use of this procedure.
register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Register_User_Formatter_Error {
if _user_formatters == nil {
return .No_User_Formatter
@@ -61,7 +66,7 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
}
// aprint* procedures return a string that was allocated with the current context
// aprint procedure return a string that was allocated with the current context
// They must be freed accordingly
aprint :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
@@ -69,12 +74,16 @@ aprint :: proc(args: ..any, sep := " ") -> string {
sbprint(buf=&str, args=args, sep=sep)
return strings.to_string(str)
}
// aprintln procedure return a string that was allocated with the current context
// They must be freed accordingly
aprintln :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
strings.init_builder(&str)
sbprintln(buf=&str, args=args, sep=sep)
return strings.to_string(str)
}
// aprintf procedure return a string that was allocated with the current context
// They must be freed accordingly
aprintf :: proc(fmt: string, args: ..any) -> string {
str: strings.Builder
strings.init_builder(&str)
@@ -83,19 +92,21 @@ aprintf :: proc(fmt: string, args: ..any) -> string {
}
// tprint* procedures return a string that was allocated with the current context's temporary allocator
// tprint procedure return a string that was allocated with the current context's temporary allocator
tprint :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
strings.init_builder(&str, context.temp_allocator)
sbprint(buf=&str, args=args, sep=sep)
return strings.to_string(str)
}
// tprintln procedure return a string that was allocated with the current context's temporary allocator
tprintln :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
strings.init_builder(&str, context.temp_allocator)
sbprintln(buf=&str, args=args, sep=sep)
return strings.to_string(str)
}
// tprintf procedure return a string that was allocated with the current context's temporary allocator
tprintf :: proc(fmt: string, args: ..any) -> string {
str: strings.Builder
strings.init_builder(&str, context.temp_allocator)
@@ -104,21 +115,24 @@ tprintf :: proc(fmt: string, args: ..any) -> string {
}
// bprint* procedures return a string using a buffer from an array
// bprint procedures return a string using a buffer from an array
bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
sb := strings.builder_from_slice(buf[0:len(buf)])
return sbprint(buf=&sb, args=args, sep=sep)
}
// bprintln procedures return a string using a buffer from an array
bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
sb := strings.builder_from_slice(buf[0:len(buf)])
return sbprintln(buf=&sb, args=args, sep=sep)
}
// bprintf procedures return a string using a buffer from an array
bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
sb := strings.builder_from_slice(buf[0:len(buf)])
return sbprintf(&sb, fmt, ..args)
}
// formatted assert
assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) -> bool {
if !condition {
p := context.assertion_failure_proc
@@ -131,6 +145,7 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati
return condition
}
// formatted panic
panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
p := context.assertion_failure_proc
if p == nil {
@@ -142,24 +157,26 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
// sbprint formats using the default print settings and writes to buf
sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
wprint(w=strings.to_writer(buf), args=args, sep=sep)
return strings.to_string(buf^)
}
// sbprintln formats using the default print settings and writes to buf
sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
wprintln(w=strings.to_writer(buf), args=args, sep=sep)
return strings.to_string(buf^)
}
// sbprintf formats according to the specififed format string and writes to buf
sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string {
wprintf(w=strings.to_writer(buf), fmt=fmt, args=args)
return strings.to_string(buf^)
}
// wprint formats using the default print settings and writes to w
wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
fi: Info
fi.writer = w
@@ -194,6 +211,7 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
return int(size1 - size0)
}
// wprintln formats using the default print settings and writes to w
wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
fi: Info
fi.writer = w
@@ -214,6 +232,7 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
return int(size1 - size0)
}
// wprintf formats according to the specififed format string and writes to w
wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
fi: Info
arg_index: int = 0
@@ -493,11 +512,13 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
return int(size1 - size0)
}
// wprint_type is a utility procedure to write a ^runtime.Type_Info value to w
wprint_type :: proc(w: io.Writer, info: ^runtime.Type_Info) -> (int, io.Error) {
n, err := reflect.write_type(w, info)
io.flush(auto_cast w)
return n, err
}
// wprint_typeid is a utility procedure to write a typeid value to w
wprint_typeid :: proc(w: io.Writer, id: typeid) -> (int, io.Error) {
n, err := reflect.write_type(w, type_info_of(id))
io.flush(auto_cast w)
@@ -829,7 +850,7 @@ _pad :: proc(fi: ^Info, s: string) {
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
switch verb {
case 'f', 'F', 'v':
case 'f', 'F', 'g', 'G', 'v':
prec: int = 3
if fi.prec_set {
prec = fi.prec
@@ -1092,8 +1113,8 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
case runtime.Type_Info_Integer:
switch info.endianness {
case .Platform: return false
case .Little: return ODIN_ENDIAN != "little"
case .Big: return ODIN_ENDIAN != "big"
case .Little: return ODIN_ENDIAN != .Little
case .Big: return ODIN_ENDIAN != .Big
}
}
return false
@@ -1927,8 +1948,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fi.indent += 1
if fi.hash {
// Printed as it is written
io.write_byte(fi.writer, '\n')
// TODO(bill): Should this render it like in written form? e.g. tranposed
for row in 0..<info.row_count {
fmt_write_indent(fi)
for col in 0..<info.column_count {
@@ -1939,13 +1960,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
data := uintptr(v.data) + uintptr(offset)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
}
io.write_string(fi.writer, ";\n")
io.write_string(fi.writer, ",\n")
}
} else {
// Printed in Row-Major layout to match text layout
for row in 0..<info.row_count {
if row > 0 { io.write_string(fi.writer, ", ") }
if row > 0 { io.write_string(fi.writer, "; ") }
for col in 0..<info.column_count {
if col > 0 { io.write_string(fi.writer, "; ") }
if col > 0 { io.write_string(fi.writer, ", ") }
offset := (row + col*info.elem_stride)*info.elem_size
@@ -2075,9 +2097,11 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
case f32be: fmt_float(fi, f64(a), 32, verb)
case f64be: fmt_float(fi, f64(a), 64, verb)
case complex32: fmt_complex(fi, complex128(a), 32, verb)
case complex64: fmt_complex(fi, complex128(a), 64, verb)
case complex128: fmt_complex(fi, a, 128, verb)
case quaternion64: fmt_quaternion(fi, quaternion256(a), 64, verb)
case quaternion128: fmt_quaternion(fi, quaternion256(a), 128, verb)
case quaternion256: fmt_quaternion(fi, a, 256, verb)
+49
View File
@@ -0,0 +1,49 @@
//+build js
package fmt
import "core:io"
foreign import "odin_env"
@(private="file")
foreign odin_env {
write :: proc "c" (fd: u32, p: []byte) ---
}
@(private="file")
write_vtable := &io.Stream_VTable{
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
fd := u32(uintptr(s.stream_data))
write(fd, p)
return len(p), nil
},
}
@(private="file")
stdout := io.Writer{
stream = {
stream_vtable = write_vtable,
stream_data = rawptr(uintptr(1)),
},
}
@(private="file")
stderr := io.Writer{
stream = {
stream_vtable = write_vtable,
stream_data = rawptr(uintptr(2)),
},
}
// print formats using the default print settings and writes to stdout
print :: proc(args: ..any, sep := " ") -> int { return wprint(w=stdout, args=args, sep=sep) }
// println formats using the default print settings and writes to stdout
println :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stdout, args=args, sep=sep) }
// printf formats according to the specififed format string and writes to stdout
printf :: proc(fmt: string, args: ..any) -> int { return wprintf(stdout, fmt, ..args) }
// eprint formats using the default print settings and writes to stderr
eprint :: proc(args: ..any, sep := " ") -> int { return wprint(w=stderr, args=args, sep=sep) }
// eprintln formats using the default print settings and writes to stderr
eprintln :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stderr, args=args, sep=sep) }
// eprintf formats according to the specififed format string and writes to stderr
eprintf :: proc(fmt: string, args: ..any) -> int { return wprintf(stderr, fmt, ..args) }
+10 -2
View File
@@ -1,19 +1,22 @@
//+build !freestanding
//+build !freestanding !js
package fmt
import "core:runtime"
import "core:os"
import "core:io"
// fprint formats using the default print settings and writes to fd
fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
w := io.to_writer(os.stream_from_handle(fd))
return wprint(w=w, args=args, sep=sep)
}
// fprintln formats using the default print settings and writes to fd
fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
w := io.to_writer(os.stream_from_handle(fd))
return wprintln(w=w, args=args, sep=sep)
}
// fprintf formats according to the specififed format string and writes to fd
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
w := io.to_writer(os.stream_from_handle(fd))
return wprintf(w, fmt, ..args)
@@ -27,11 +30,16 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
return wprint_typeid(w, id)
}
// print* procedures return the number of bytes written
// print formats using the default print settings and writes to os.stdout
print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
// println formats using the default print settings and writes to os.stdout
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
// printf formats according to the specififed format string and writes to os.stdout
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
// eprint formats using the default print settings and writes to os.stderr
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
// eprintln formats using the default print settings and writes to os.stderr
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
// eprintf formats according to the specififed format string and writes to os.stderr
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
+37 -22
View File
@@ -47,17 +47,34 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
}
@(optimization_mode="speed")
djb2 :: proc(data: []byte) -> u32 {
hash: u32 = 5381
djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
hash: u32 = seed
for b in data {
hash = (hash << 5) + hash + u32(b) // hash * 33 + u32(b)
}
return hash
}
djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check {
state := [4]u32{seed, seed, seed, seed}
s: u32 = 0
for p in data {
state[s] = (state[s] << 5) + state[s] + u32(p) // hash * 33 + u32(b)
s = (s + 1) & 3
}
(^u32le)(&result[0])^ = u32le(state[0])
(^u32le)(&result[4])^ = u32le(state[1])
(^u32le)(&result[8])^ = u32le(state[2])
(^u32le)(&result[12])^ = u32le(state[3])
return
}
@(optimization_mode="speed")
fnv32 :: proc(data: []byte) -> u32 {
h: u32 = 0x811c9dc5
fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed
for b in data {
h = (h * 0x01000193) ~ u32(b)
}
@@ -65,8 +82,8 @@ fnv32 :: proc(data: []byte) -> u32 {
}
@(optimization_mode="speed")
fnv64 :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325
fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed
for b in data {
h = (h * 0x100000001b3) ~ u64(b)
}
@@ -74,8 +91,8 @@ fnv64 :: proc(data: []byte) -> u64 {
}
@(optimization_mode="speed")
fnv32a :: proc(data: []byte) -> u32 {
h: u32 = 0x811c9dc5
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed
for b in data {
h = (h ~ u32(b)) * 0x01000193
}
@@ -83,8 +100,8 @@ fnv32a :: proc(data: []byte) -> u32 {
}
@(optimization_mode="speed")
fnv64a :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325
fnv64a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed
for b in data {
h = (h ~ u64(b)) * 0x100000001b3
}
@@ -92,8 +109,8 @@ fnv64a :: proc(data: []byte) -> u64 {
}
@(optimization_mode="speed")
jenkins :: proc(data: []byte) -> u32 {
hash: u32 = 0
jenkins :: proc(data: []byte, seed := u32(0)) -> u32 {
hash: u32 = seed
for b in data {
hash += u32(b)
hash += hash << 10
@@ -106,11 +123,11 @@ jenkins :: proc(data: []byte) -> u32 {
}
@(optimization_mode="speed")
murmur32 :: proc(data: []byte) -> u32 {
murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
c1_32: u32 : 0xcc9e2d51
c2_32: u32 : 0x1b873593
h1: u32 = 0
h1: u32 = seed
nblocks := len(data)/4
p := raw_data(data)
p1 := mem.ptr_offset(p, 4*nblocks)
@@ -156,14 +173,12 @@ murmur32 :: proc(data: []byte) -> u32 {
}
@(optimization_mode="speed")
murmur64 :: proc(data: []byte) -> u64 {
SEED :: 0x9747b28c
murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
when size_of(int) == 8 {
m :: 0xc6a4a7935bd1e995
r :: 47
h: u64 = SEED ~ (u64(len(data)) * m)
h: u64 = seed ~ (u64(len(data)) * m)
data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64))
for _, i in data64 {
@@ -198,8 +213,8 @@ murmur64 :: proc(data: []byte) -> u64 {
m :: 0x5bd1e995
r :: 24
h1 := u32(SEED) ~ u32(len(data))
h2 := u32(SEED) >> 32
h1 := u32(seed) ~ u32(len(data))
h2 := u32(seed) >> 32
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
len := len(data)
i := 0
@@ -262,8 +277,8 @@ murmur64 :: proc(data: []byte) -> u64 {
}
@(optimization_mode="speed")
sdbm :: proc(data: []byte) -> u32 {
hash: u32 = 0
sdbm :: proc(data: []byte, seed := u32(0)) -> u32 {
hash: u32 = seed
for b in data {
hash = u32(b) + (hash<<6) + (hash<<16) - hash
}
+1 -1
View File
@@ -96,7 +96,7 @@ XXH3_128_canonical_from_hash :: proc(hash: XXH128_hash_t) -> (canonical: XXH128_
#assert(size_of(XXH128_canonical) == size_of(XXH128_hash_t))
t := hash
when ODIN_ENDIAN == "little" {
when ODIN_ENDIAN == .Little {
t.high = byte_swap(t.high)
t.low = byte_swap(t.low)
}
+2
View File
@@ -6,6 +6,8 @@
Jeroen van Rijn: Initial implementation, optimization.
Ginger Bill: Cosmetic changes.
*/
// package image implements a general 2D image library to be used with other image related packages
package image
import "core:bytes"
+1 -1
View File
@@ -189,7 +189,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
img := image
// PBM 16-bit images are big endian
when ODIN_ENDIAN == "little" {
when ODIN_ENDIAN == .Little {
if img.depth == 16 {
// The pixel components are in Big Endian. Let's byteswap back.
input := mem.slice_data_cast([]u16, img.pixels.buf[:])
+6 -1
View File
@@ -6,6 +6,11 @@
Jeroen van Rijn: Initial implementation.
Ginger Bill: Cosmetic changes.
*/
// package png implements a PNG image reader
//
// The PNG specification is at https://www.w3.org/TR/PNG/.
package png
import "core:compress"
@@ -1611,7 +1616,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
}
}
}
when ODIN_ENDIAN == "little" {
when ODIN_ENDIAN == .Little {
if img.depth == 16 {
// The pixel components are in Big Endian. Let's byteswap.
input := mem.slice_data_cast([]u16be, img.pixels.buf[:])
+5
View File
@@ -197,3 +197,8 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
// Internal compiler use only
__entry_point :: proc() ---
+39 -2
View File
@@ -1,9 +1,13 @@
// package io provides basic interfaces for generic data stream primitives.
// The purpose of this package is wrap existing data structures and their
// operations into an abstracted stream interface.
package io
import "core:intrinsics"
import "core:runtime"
import "core:unicode/utf8"
// Seek whence values
Seek_From :: enum {
Start = 0, // seek relative to the origin of the file
Current = 1, // seek relative to the current offset
@@ -139,6 +143,10 @@ destroy :: proc(s: Stream) -> Error {
return .Empty
}
// read reads up to len(p) bytes into s. It returns the number of bytes read and any error if occurred.
//
// When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of
// bytes read along with the error.
read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
if s.stream_vtable != nil && s.impl_read != nil {
n, err = s->impl_read(p)
@@ -150,6 +158,7 @@ read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
return 0, .Empty
}
// write writes up to len(p) bytes into s. It returns the number of bytes written and any error if occurred.
write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) {
if s.stream_vtable != nil && s.impl_write != nil {
n, err = s->impl_write(p)
@@ -161,6 +170,13 @@ write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Erro
return 0, .Empty
}
// seek sets the offset of the next read or write to offset.
//
// .Start means seek relative to the origin of the file.
// .Current means seek relative to the current offset.
// .End means seek relative to the end.
//
// seek returns the new offset to the start of the file/stream, and any error if occurred.
seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
if s.stream_vtable != nil && s.impl_seek != nil {
return s->impl_seek(offset, whence)
@@ -168,6 +184,8 @@ seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error)
return 0, .Empty
}
// The behaviour of close after the first call is stream implementation defined.
// Different streams may document their own behaviour.
close :: proc(s: Closer) -> Error {
if s.stream_vtable != nil && s.impl_close != nil {
return s->impl_close()
@@ -184,6 +202,7 @@ flush :: proc(s: Flusher) -> Error {
return .None
}
// size returns the size of the stream. If the stream does not support querying its size, 0 will be returned.
size :: proc(s: Stream) -> i64 {
if s.stream_vtable == nil {
return 0
@@ -214,7 +233,12 @@ size :: proc(s: Stream) -> i64 {
// read_at reads len(p) bytes into p starting with the provided offset in the underlying Reader_At stream r.
// It returns the number of bytes read and any error if occurred.
//
// When read_at returns n < len(p), it returns a non-nil Error explaining why.
//
// If n == len(p), err may be either nil or .EOF
read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n: int, err: Error) {
defer if n_read != nil {
n_read^ += n
@@ -245,6 +269,11 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n:
}
// write_at writes len(p) bytes into p starting with the provided offset in the underlying Writer_At stream w.
// It returns the number of bytes written and any error if occurred.
//
// If write_at is writing to a Writer_At which has a seek offset, then write_at should not affect the underlying
// seek offset.
write_at :: proc(w: Writer_At, p: []byte, offset: i64, n_written: ^int = nil) -> (n: int, err: Error) {
defer if n_written != nil {
n_written^ += n
@@ -294,6 +323,7 @@ read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
}
// read_byte reads and returns the next byte from r.
read_byte :: proc(r: Byte_Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
defer if err == nil && n_read != nil {
n_read^ += 1
@@ -347,6 +377,7 @@ _write_byte :: proc(w: Byte_Writer, c: byte, n_written: ^int = nil) -> (err: Err
return err
}
// read_rune reads a single UTF-8 encoded Unicode codepoint and returns the rune and its size in bytes.
read_rune :: proc(br: Rune_Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: Error) {
defer if err == nil && n_read != nil {
n_read^ += size
@@ -405,10 +436,12 @@ unread_rune :: proc(s: Rune_Scanner) -> Error {
}
// write_string writes the contents of the string s to w.
write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int, err: Error) {
return write(s, transmute([]byte)str, n_written)
}
// write_rune writes a UTF-8 encoded rune to w.
write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err: Error) {
defer if err == nil && n_written != nil {
n_written^ += size
@@ -430,12 +463,16 @@ write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err
}
// read_full expected exactly len(buf) bytes from r into buf.
read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) {
return read_at_least(r, buf, len(buf))
}
// read_at_least reads from r into buf until it has read at least min bytes. It returns the number
// of bytes copied and an error if fewer bytes were read. `.EOF` is only returned if no bytes were read.
// `.Unexpected_EOF` is returned when an `.EOF ` is returned by the passed Reader after reading
// fewer than min bytes. If len(buf) is less than min, `.Short_Buffer` is returned.
read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) {
if len(buf) < min {
return 0, .Short_Buffer
+2 -4
View File
@@ -2,12 +2,10 @@
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
An arbitrary precision mathematics implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
This file collects public proc maps and their aliases.
*/
package math_big
/*

Some files were not shown because too many files have changed in this diff Show More