Compare commits

..

689 Commits

Author SHA1 Message Date
gingerBill aab122ede8 Remove ? 2024-04-11 15:59:22 +01:00
gingerBill 503964c769 Add @(static) check 2024-04-11 15:57:13 +01:00
gingerBill b2e887be36 Change stack overflow check to >256 KiB 2024-04-11 15:55:01 +01:00
gingerBill f36fb6d1ef Add nil checks 2024-04-11 15:41:01 +01:00
gingerBill 45d7a670ce Fix @(static) error message bug 2024-04-11 15:34:34 +01:00
gingerBill 86e82dc182 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-04-11 15:24:42 +01:00
gingerBill b6d2ac11b8 Add -vet-unused-variables and `-vet-unused-imports (-vet-unused is both) 2024-04-11 15:24:36 +01:00
gingerBill 6c38ae3658 Remove #optional_ok from docs 2024-04-11 15:24:08 +01:00
Jeroen van Rijn 532d477705 Merge pull request #3121 from RilleP/parsing-package-fixes
core:odin parsing fixes
2024-04-11 15:19:10 +02:00
gingerBill 0abbf3ba0a Fix #3412 2024-04-11 13:57:37 +01:00
RilleP 330c161625 remove semicolon 2024-04-11 09:36:28 +02:00
gingerBill 97db075e45 Add #panic to tell of moved package 2024-04-10 20:32:55 +01:00
RilleP 730f992bff fix indentation 2024-04-10 19:16:38 +02:00
RilleP 95a38d5a96 Merge branch 'master' into parsing-package-fixes 2024-04-10 19:10:33 +02:00
gingerBill 13e459980b Fix ptr_to_bit_field.field 2024-04-10 16:18:44 +01:00
gingerBill 3b5e515a22 Fix #3386 - fixed.to_string 2024-04-10 14:35:14 +01:00
gingerBill 3ad95d6be3 Add append_u128 2024-04-10 14:35:00 +01:00
gingerBill abd5fc606c Fix #3407 2024-04-10 14:12:41 +01:00
gingerBill 6678242280 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-04-10 13:40:31 +01:00
gingerBill fd487f66bc Add json:"name,omitempty" 2024-04-10 13:40:25 +01:00
gingerBill d0dc7395e9 Allow for comma-separate json names json:"name,flag" 2024-04-10 13:29:10 +01:00
Jeroen van Rijn 2ba2bc1fec Merge pull request #3288 from Yawning/feature/even-moar-crypto
core/crypto: Even more improvments
2024-04-10 14:19:25 +02:00
gingerBill 97e2d8916a Merge branch 'master' of https://github.com/odin-lang/Odin 2024-04-10 12:32:32 +01:00
gingerBill 3dfd61dd4f Make intrinsics.overflow_* NOT #optional_ok 2024-04-10 12:32:26 +01:00
gingerBill 0f39b9ef22 Merge pull request #3403 from iciuperca/master
Avoid loop index shadowing in vendor:OpenGL
2024-04-10 12:01:08 +01:00
gingerBill af67cc7afe Merge pull request #3397 from wrapperup/add-movefile-flags
Add flags for windows `MoveFileEx`
2024-04-10 12:01:01 +01:00
gingerBill 53558313d8 Merge pull request #3402 from Chickenkeeper/using-param-error-typo-fix
Fix typo in using parameter error message
2024-04-10 11:53:08 +01:00
Jeroen van Rijn 8a6a3e883c Merge pull request #3406 from laytan/bit-field-core-parser
add bit_field to `core:odin`
2024-04-10 03:12:40 +02:00
Laytan Laats af6d2480fa add bit_field parsing to core:odin/parser
Also adds it to the core type thingy like it is in the compiler.
2024-04-10 01:01:32 +02:00
iciuperca c644f79573 Avoid loop index shadowing
The inner loop uses the same index variable name "i" as the parent.

This causes an error message with -vet -strict-style
2024-04-09 18:24:57 +01:00
Chris 219eb58c08 Fix typo in using parameter error message 2024-04-09 12:03:41 +01:00
gingerBill 2207a01494 Merge pull request #3400 from Chickenkeeper/datetime-using-parameter-fix
Remove using parameter from validate_datetime
2024-04-09 12:03:11 +01:00
Chris 60478c0e07 Remove using from validate_datetime 2024-04-09 10:57:51 +01:00
wrapperup 9b496e82f3 add movefile flags 2024-04-09 01:50:16 -04:00
Yawning Angel fa1d681e65 tests/core/crypto: Start adding comprehensive curve25519 tests 2024-04-09 14:37:59 +09:00
Yawning Angel 893c3bef9a core/crypto/ed25519: Initial import 2024-04-09 14:37:59 +09:00
Yawning Angel d96f8bb5c1 core/crypto/ristretto255: Initial import 2024-04-09 14:37:59 +09:00
Yawning Angel 563c527419 core/crypto/_edwards25519: Initial import 2024-04-09 14:37:59 +09:00
Yawning Angel fec42a6d74 core/crypto/_fiat/field_scalar25519: Initial import 2024-04-09 10:23:58 +09:00
Yawning Angel 4defe88dec core/crypto/_fiat/field_poly1305: Mark more functions contextless 2024-04-09 10:23:58 +09:00
Yawning Angel 36f3001d59 core/crypto/_fiat/field_poly1305: Use multiply to calculate the mask 2024-04-09 10:23:58 +09:00
Yawning Angel 1ce279e6a1 core/crypto/_fiat/field_curve25519: Mark more functions contextless 2024-04-09 10:23:58 +09:00
Yawning Angel c951cbdbbc core/crypto/_fiat: odinfmt (NFC) 2024-04-09 10:23:58 +09:00
Yawning Angel 31aba5a728 core/crypto/_fiat/field_poly1305: Move routines (NFC) 2024-04-09 10:23:58 +09:00
Yawning Angel 9a418fd27b core/crypto/_fiat/field_curve25519: Move routines (NFC) 2024-04-09 10:23:58 +09:00
Yawning Angel f9b9521bf0 core/crypto/_fiat/field_curve25519: Use multiply to calculate the mask
Largely for consistency with the generic code, either is valid with Odin
semantics, but this is easier to comprehend.
2024-04-09 10:23:58 +09:00
Yawning Angel b155fdf8c9 core/crypto: Add has_rand_bytes
This allows runtime detection as to if `rand_bytes` is supported or not,
and lets us enable the test-case on all of the supported targets.
2024-04-09 10:23:58 +09:00
Yawning Angel a43a5b053c core/crypto: Add more documentation about assumptions (NFC) 2024-04-09 10:23:58 +09:00
gingerBill a14f0d8f58 Merge pull request #3396 from laytan/fix-nasm-check
fix nasm check
2024-04-09 00:09:13 +01:00
gingerBill 25f781d64b Merge branch 'master' of https://github.com/odin-lang/Odin 2024-04-08 23:58:36 +01:00
gingerBill 9933ca8b56 Make map_total_allocation_size public; Add map_total_allocation_size_from_value 2024-04-08 23:58:30 +01:00
Laytan Laats 9c958ee66d fix nasm check 2024-04-08 20:43:52 +02:00
gingerBill a00d96c0de Merge pull request #3395 from odin-lang/darwin-reorganization
Move `vendor:darwin/Foundation` to `core:sys/darwin/Foundation`
2024-04-08 16:30:44 +01:00
gingerBill ecac3aef32 Change allocator to permanent 2024-04-08 16:18:14 +01:00
gingerBill ec45504631 Obfuscate #line 2024-04-08 16:14:47 +01:00
gingerBill 810cf22e5d Obfuscate #file and #procedure when -obfuscate-source-code-locations is enabled 2024-04-08 16:08:35 +01:00
gingerBill ef82f3e71e Move vendor:darwin/Foundation to core:sys/darwin/Foundation 2024-04-08 13:47:46 +01:00
gingerBill 16dc79fc5c Add "type" field to -json-errors 2024-04-08 13:36:23 +01:00
gingerBill 2aca370a0a Merge pull request #3394 from laytan/wasm-fixes
Wasm fixes
2024-04-08 13:33:29 +01:00
Laytan Laats ce196529dc enable the required target feature atomics when using them in wasm 2024-04-08 13:56:04 +02:00
Laytan Laats 9d8bb7f4e4 fix _end being called before the actual end when using the step function 2024-04-08 13:54:23 +02:00
Laytan Laats 667883b3d5 fix js_wasm time.tick_now, performance.now() returns a float 2024-04-08 13:53:16 +02:00
gingerBill 114ddc4a10 Fix printing error when field name could not be found 2024-04-08 12:06:06 +01:00
gingerBill 0df9c8bffc Improve error messages for people using keywords instead of identifiers 2024-04-08 12:04:33 +01:00
Jeroen van Rijn 0a73ed0799 Merge pull request #3390 from Lperlind/master
Update "core:runtime" to "base:runtime"
2024-04-07 01:09:47 +02:00
Lucas Perlind a71cd07b36 Update "core:runtime" to "base:runtime" 2024-04-07 09:02:01 +10:00
gingerBill 2bb20a2c1c Merge pull request #3388 from laytan/fix-raygui-symbol
fix raygui `GuiFade` being renamed to `GuiSetAlpha`
2024-04-06 22:52:08 +01:00
gingerBill 352d526b94 Merge pull request #3389 from laytan/fix-wasm-atomics
fix wasm atomics
2024-04-06 22:51:58 +01:00
Laytan Laats b150f49c46 fix wasm atomics
Fixes #2745
2024-04-06 23:32:38 +02:00
Laytan Laats d83532d29e fix raygui GuiFade being renamed to GuiSetAlpha 2024-04-06 20:20:16 +02:00
gingerBill 83d3bc74b0 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-04-06 16:59:07 +01:00
gingerBill fbf01543d1 Remove duplicate table 2024-04-06 16:59:02 +01:00
gingerBill 8a98ee800a Merge pull request #3379 from atomicptr/task/add-sdl-sempost
Add missing SDL2_SemPost binding
2024-04-06 16:12:41 +01:00
gingerBill f72e3f689b Merge pull request #3383 from oskarnp/or_return_crash
Fix checker crash when or_return used for non-existing proc
2024-04-06 16:10:09 +01:00
Jeroen van Rijn cd6153a125 Merge pull request #3385 from blob1807/fixed-removed-undefined-var
core:math/fixed Removed undefined & usused vars in init_from_parts
2024-04-06 14:01:25 +02:00
blob1807 fde4e8c905 Removed undefined & usused vars
Removed undefined& usused vars in init_from_parts
2024-04-06 21:49:50 +10:00
oskarnp ca46484ae3 Fix checker crash when or_return/or_break/or_continue used for non-existing proc 2024-04-06 11:02:43 +02:00
Jeroen van Rijn 280adc8a85 Merge pull request #3381 from Yawning/fix/chacha20poly1305
core/crypto/poly1305: The final addition is NOT mod p
2024-04-05 16:57:31 +02:00
Yawning Angel a45721e9ad core/crypto/poly1305: The final addition is NOT mod p 2024-04-05 23:45:41 +09:00
Christopher Kaster 8d399fa7c0 add missing SDL2_SemPost binding 2024-04-05 09:00:57 +02:00
gingerBill 1422e5bc26 Merge pull request #3377 from laytan/fix-amd64-sysv-abi
fix amd64 sysv abi to pass asan everywhere
2024-04-05 00:06:56 +01:00
Laytan Laats 133b45d843 fix amd64 sysv abi to pass asan everywhere
I verified the PR by running the entire test suite of Odin itself with
`-sanitize:address` and also the ols test suite (which caused unique problems
before).

A test has also been added with some problematic code, Windows seems to
have problems with asan in CI or in general so it is not ran there.

The LB_ABI_COMPUTE_RETURN_TYPES block has been removed entirely because
it was unused, I got pretty confused why it didn't effect anything at
first.

Fixes #3211
2024-04-04 23:45:23 +02:00
gingerBill 510574aa7f Merge pull request #3373 from laytan/add-some-windows-wsa-symbols
add some wsa based additions to `core:sys/windows`
2024-04-04 20:40:31 +01:00
gingerBill 3fd3bf2d4d Merge pull request #3371 from mailgerigk/windows-setevent
Add windows.SetEvent
2024-04-04 18:09:21 +01:00
gingerBill fbff2b4fd6 Merge pull request #3372 from laytan/fix-lbarg-ignore-logic
fix lbArg_Ignore logic
2024-04-04 18:09:12 +01:00
gingerBill 7f0ca315b3 Merge pull request #3374 from laytan/fix-128-ints-alignment-arm64
fix 128 bit int alignment on arm64
2024-04-04 18:06:01 +01:00
Laytan Laats 31407d9b1b fix 128 bit int alignment on arm64
Fixes #2403
2024-04-04 18:39:41 +02:00
gingerBill 83e2f5ff74 Add better error messages with suggestions for using context as an identifier 2024-04-04 17:01:31 +01:00
gingerBill 2375ac22a7 Improve error messages for A variable declaration must be an identifier 2024-04-04 16:57:08 +01:00
gingerBill b979fd4c43 Remove consecutive linking libraries 2024-04-04 16:14:05 +01:00
gingerBill 0e5a482c42 Default to "smart" linker behaviour; Add -min-link-libs to use minimize link libs if wanted 2024-04-04 16:11:26 +01:00
Laytan Laats 1af84e082c add some wsa based additions to core:sys/windows 2024-04-04 17:09:12 +02:00
gingerBill d248cddf90 Remove dead newline 2024-04-04 16:07:55 +01:00
Laytan Laats a7056f2b4f fix lbArg_Ignore logic
Fixes #2698
2024-04-04 16:58:22 +02:00
gerigk 62cebe1bc9 Add windows.SetEvent 2024-04-04 16:00:01 +02:00
gingerBill 99aff7e3fb Merge pull request #3370 from laytan/fix-objc-proc-group-edge-case
fix objc proc group edge case
2024-04-04 13:03:27 +01:00
Laytan Laats 5339e1e1b6 fix objc proc group edge case
Fixes #2648
2024-04-03 21:21:46 +02:00
gingerBill 5fe0788cff Minimize code duplication in core:mem by using the base:runtime calls 2024-04-03 11:47:39 +01:00
gingerBill e42b16b106 Merge pull request #3366 from laytan/fix-vet-scope-bug
fix vet scope bug skipping some scopes
2024-04-03 11:40:01 +01:00
gingerBill a1d9442380 Merge pull request #3367 from laytan/fix-stack-overflow-warning
fix -vet warning for stack overflows not showing up
2024-04-03 11:39:25 +01:00
Laytan Laats b754c1e072 fix -vet warning for stack overflows not showing up
Due to the placement of this code, the warning would only ever be added
if the variable was also either unused or shadowed.
2024-04-03 01:05:54 +02:00
Laytan Laats 3a0df80066 correct newly found vets 2024-04-03 00:52:58 +02:00
gingerBill a9bfb3ac2e Clarity warning and error printing 2024-04-02 23:39:14 +01:00
gingerBill 692a47f080 Fix printing of warnings 2024-04-02 23:36:36 +01:00
Laytan Laats 21fcf7c874 fix vet scope bug skipping some scopes
Fixes #3146
2024-04-02 23:59:38 +02:00
gingerBill 05d07983c3 Merge pull request #3363 from laytan/fix-c-varargs-named-args
fix named arguments and untyped nil with #c_vararg
2024-04-02 18:36:10 +01:00
Laytan Laats fc30bde0f6 fix untyped nil into c varargs
Fixes #2842
2024-04-02 18:49:35 +02:00
Laytan Laats a8d8696e2f fix named arguments with #c_vararg
Previously `args=1`, `args={}`, `args={1, 2, 3}` would all crash the
compiler. Now it passes them correctly, and if given a compound literal,
the values are expanded into the call so you can use a named arg while
passing multiple values.

Fixes #3168
2024-04-02 17:25:11 +02:00
gingerBill 66f9ef9a00 Merge pull request #3361 from yay/ns-application-activate
Add NSApplication.activate to Foundation bindings.
2024-04-02 06:59:13 +01:00
Vitalii Kravchenko fdd4ef3c59 Add NSApplication.active to Foundation bindings. 2024-04-02 02:35:00 +01:00
Jeroen van Rijn f9f6fbfe1f Merge pull request #3360 from laytan/debug-info-fixes
fix wrong type in map debug info
2024-04-01 19:12:24 +02:00
Laytan Laats 4fc96e1ca5 change unneeded permanent allocation to temporary 2024-04-01 19:06:05 +02:00
Laytan Laats cfc85fd737 fix wrong type in map debug info 2024-04-01 18:42:10 +02:00
Jeroen van Rijn 6b7e9f0a2d Merge pull request #3359 from Kelimion/tests_digests
Add SHA3-512 digests verification to test assets.
2024-04-01 18:24:36 +02:00
Jeroen van Rijn b3580fa63a Make it a HMAC. 2024-04-01 18:20:14 +02:00
gingerBill 406aa587e2 Merge pull request #3358 from laytan/debug-info-fixes
debug info fixes/refactor
2024-04-01 16:53:56 +01:00
Jeroen van Rijn b2f432c223 Add SHA3-512 digests verification to test assets. 2024-04-01 17:18:31 +02:00
Laytan Laats 7c2352ea08 remove soa handling in debug info, fields are already added in checker 2024-04-01 16:51:16 +02:00
Laytan Laats 9647cb74ad debug info fixes/refactor
This fixes (on my end) #3340, #3117, #2945, #2922, and #2762

A general refactor of debug info generation in order to fix issues and
increase stability.

What I believe is the root cause of a bunch of issues is that we use the
temporary metadata/forward declarations too much (/ hold onto them for
too long). It seems to cause problems with the reference counting inside
LLVM.

This PR reduces the use of these forward declarations to a minimum, it
creates it, fills in the fields, and resolves it, instead of waiting
until the end of generating code.

Some smaller issues I came across have also been solved.
2024-04-01 16:28:44 +02:00
gingerBill bb72ff9c35 Fix nested ERROR_BLOCK bug 2024-04-01 14:28:11 +01:00
gingerBill 397c2aa201 Merge pull request #3318 from flysand7/os2-file-pipe-read
[core/os2]: Fix read_entire_file for reads from pipe and console.
2024-04-01 13:38:10 +01:00
gingerBill b47d73c651 Fix type checking for invalid enum backing type 2024-04-01 13:34:30 +01:00
flysand7 3ee9184537 [core/os2]: Fix memory leak on read_entire_file 2024-04-01 23:32:16 +11:00
gingerBill 2938def707 Remove dead comment 2024-04-01 13:27:51 +01:00
gingerBill 84686c70c5 Error message when RTTI is disabled when iterating over an enum type or a bit_set of enum with for in 2024-04-01 13:16:49 +01:00
gingerBill 3fa02427b3 Unify error message logic for ranges over bit_set 2024-04-01 13:12:09 +01:00
gingerBill b862691d75 Support for in with bit_set 2024-04-01 13:08:07 +01:00
gingerBill f482cc8374 Fix error message 2024-04-01 12:33:56 +01:00
gingerBill 030b8d3f66 Merge pull request #3354 from serberoth/issue-3349
Patch for #3349 to address API concerns for Darwin platform in core:os
2024-04-01 12:25:17 +01:00
Jeroen van Rijn 0d8dadb084 Merge pull request #3357 from DragosPopse/win32-coinit-fix
Fixed windows.COINIT.MULTITHREADED declaration.
2024-03-31 20:41:13 +02:00
Dragos Popescu d0674cb70f Fixed windows.COINIT.MULTITHREADED declaration. It's supposed to be 0 rather than 3 2024-03-31 21:29:49 +03:00
nicola 3975b5e736 :Updated core:os for darwin to include flush function and match close to api documentation returning errno 2024-03-30 16:48:52 -04:00
gingerBill 2bdf5f58ef Enforce error on old style for/switch l-value 2024-03-30 14:23:47 +00:00
gingerBill 8e1a2094a7 Fix debug info for map 2024-03-30 13:46:23 +00:00
gingerBill 9271372fef Fix #field_align issues, by simplifying the LLVM struct type generation 2024-03-30 13:06:51 +00:00
gingerBill 4edcaa6124 Try storing a pointer to a fake metadata type in the debug info for a map 2024-03-30 10:29:20 +00:00
gingerBill d84d65ba45 Merge pull request #3348 from rick-masters/fix_convert_smaller_float_endian
Implement endian conversions for smaller float types.
2024-03-29 22:13:58 +00:00
gingerBill 28fb1ba83d Merge pull request #3350 from laytan/fix-gb.h-for-fsanitize-address
fix gb.h to be able to use -fsanitize=address
2024-03-29 22:13:32 +00:00
gingerBill db95ed7cdd Merge pull request #3353 from laytan/fix-incomplete-types-resize-segfault
fix a segfault when incomplete types array resizes while processing
2024-03-29 22:13:01 +00:00
Laytan Laats e6a552e0ce fix gb.h to be able to use -fsanitize=address 2024-03-29 22:51:58 +01:00
Laytan Laats 915f63b3f9 fix a segfault when incomplete types array resizes while processing 2024-03-29 22:42:12 +01:00
rick-masters e1b545860f Implement endian conversions for smaller float types. 2024-03-29 11:05:27 +00:00
Jeroen van Rijn 8899f42478 Merge pull request #3347 from harold-b/macos-timing-fix
Fix incorrect timings on macOS
2024-03-29 10:15:16 +01:00
Harold Brenes 13b8a5b73d Reverting to calculated frequency timings method on macOS, but fixed 2024-03-29 05:04:40 -04:00
Harold Brenes b84a660806 Fix incorrect timings on macOS 2024-03-28 21:09:37 -04:00
Jeroen van Rijn d325c8ad23 Merge pull request #3346 from laytan/add-macos-14.4.1
add MacOS 14.4.1 to sys/info and odin report
2024-03-28 19:43:57 +01:00
Laytan Laats b7fd51a251 add MacOS 14.4.1 to sys/info and odin report 2024-03-28 19:39:18 +01:00
Jeroen van Rijn 7325120ca9 Merge pull request #3345 from laytan/speed-up-path-to-fullpath
fix not setting ok in cached code path
2024-03-28 19:27:31 +01:00
Laytan Laats 9a5a39c07d fix not setting ok in cached code path 2024-03-28 19:16:55 +01:00
Jeroen van Rijn 9b43aa3c94 Merge pull request #3344 from laytan/speed-up-path-to-fullpath
compiler: speed up path_to_fullpath on Linux/MacOS
2024-03-28 19:02:39 +01:00
Laytan Laats 63f30a8207 speed up path_to_fullpath on Linux/MacOS
We did some profiling for #3343 and this seems to be the biggest
problem. `realpath` is expensive, and we are locking here for no reason
that I can think of.

This improves the "check procedure bodies" timing (of the linked issue)
from 2.4s to .4s on my machine.
2024-03-28 18:44:21 +01:00
gingerBill 308e9112f2 Disable packing on ARM64 and AMD64 2024-03-28 10:58:40 +00:00
gingerBill 58d0635f48 Merge pull request #3342 from FrancisTheCat/master
Json: Improved unmarshalling of `using _: T` fields
2024-03-27 18:57:34 +00:00
FrancisTheCat 2ccb326a41 Merge branch 'odin-lang:master' into master 2024-03-27 17:52:29 +01:00
gingerBill cf9bdc134c Fix #3341 2024-03-27 16:48:51 +00:00
FrancisTheCat 29e4e85152 Merge branch 'odin-lang:master' into master 2024-03-27 16:43:02 +01:00
Franz Höltermann a422aba578 Json: improved unmarshalling of using _: T fields.
`using _: T` fields will now have their members unmarshalled to their
parent types reflecting the new behaviour of json.marshall.

Example:
```go
A :: struct {
    using _: B,
}

B :: struct {
    field: string,
}

data := `{"field": "Hello World"}`

a: A
json.unmarshal_string(data, &a)
```
2024-03-27 15:46:44 +01:00
gingerBill 7b387fd3aa Improve C-like syntax mistakes error messages 2024-03-27 13:10:46 +00:00
gingerBill 12ec9bce7d Fix parsing bug on bit_set[;x] 2024-03-27 13:05:15 +00:00
gingerBill b378eb2df3 Fix free bug not using the correct allocator 2024-03-27 12:54:52 +00:00
gingerBill 6422c090f2 Make hash procedures contextless where possible 2024-03-27 12:54:37 +00:00
gingerBill 3bc7c51325 Merge pull request #3283 from laytan/darwin-new-wait-on-address-api
darwin: use new wait on address API if possible
2024-03-27 11:20:32 +00:00
gingerBill f57cc6beb1 Merge pull request #3331 from laytan/darwin-fix-entrypoint-warning-dylibs
darwin: fix linker warning when building dynamic library
2024-03-27 11:19:11 +00:00
gingerBill 2031d2769a Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-27 00:58:35 +00:00
gingerBill 83c9739a7d Update year 2024-03-27 00:58:21 +00:00
gingerBill d9ba698b7b Merge pull request #3337 from FrancisTheCat/master
Json: Improve marshalling of `using _: T` fields
2024-03-27 00:57:03 +00:00
Laytan Laats 19d566ebc5 darwin: fix linker warning when building dynamic library
Trying to fix all linker warnings that macOS comes up with, when
building a dynamic library it currently emits `ld: warning: ignoring -e, not used for output type`
2024-03-26 21:00:14 +01:00
Franz Höltermann 92a5666c1c Json: fields on structs like 'using _: T' will now have T's fields
marshalled directly into the parent type without an '"_":{ ... }'.
This seems like desirable behavior to me, since you can't access the
fields with 'Parent_Type._.field' either.
2024-03-26 20:22:51 +01:00
gingerBill 63d6b4752b Fix type info layout for wasm64p32 targets 2024-03-26 17:16:12 +00:00
gingerBill 6588fe35b3 Fix wasm abi 2024-03-26 16:47:29 +00:00
gingerBill fa84272d5e Merge pull request #3335 from rick-masters/fix_incomplete_struct_type
Don't add type info for incomplete structs.
2024-03-26 16:08:12 +00:00
rick-masters c90a6ab0d5 Remove accidentally added test binary. 2024-03-26 15:08:32 +00:00
gingerBill 0989eac681 Add extra sanity check for nullptr 2024-03-26 14:57:06 +00:00
rick-masters b8c0a02164 Don't add type info for incomplete structs. 2024-03-26 14:34:56 +00:00
gingerBill df526549e2 Fix min/max for wasm 2024-03-26 14:31:28 +00:00
gingerBill 1009182f7b Fix another #soa race condition bug 2024-03-26 14:13:55 +00:00
gingerBill 620dd2c812 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-26 13:06:46 +00:00
gingerBill 533ba63c82 Fix #3327 #3204 #3200 2024-03-26 13:06:39 +00:00
gingerBill 2165303f5e Merge pull request #3332 from laytan/darwin-be-less-annoying-about-library-versions
darwin: be less annoying about "incompatible" library versions
2024-03-26 11:16:25 +00:00
gingerBill c59f6d548b Merge pull request #3333 from blob1807/change-time-sleep-doc
time.accurate_sleep Windows Doc update
2024-03-26 11:15:55 +00:00
blob1807 8eed65ad4c Update time.odin 2024-03-26 11:20:05 +10:00
blob1807 bc0a2b8d39 Update time.odin 2024-03-26 11:17:44 +10:00
blob1807 a405c72d4d Updated windows time period call
Updated win32.time_begin_period to windows.timeBeginPeriod
Added a note about calling windows.timeEndPeriod once you don't need accurate_sleep, as per MS's docs https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod#remarks
2024-03-26 11:13:27 +10:00
Laytan Laats b26a685b76 darwin: be less annoying about "incompatible" library versions
After #3316 we set a default minimum version, now this will warn if you
link with a library that is targeting later versions.

This might be a bit annoying, especially when the user hasn't actually
given Odin a minimum target.

So this PR makes these warnings only show when you explicitly give a
target version (afaik that is the only thing that -mmacosx-min-version
actually does for us because we don't use it to compile anything, just
to link).
2024-03-25 22:11:20 +01:00
gingerBill 3a8971c260 Merge pull request #3330 from laytan/remove-mistaken-dll
remove mistakenly added dll
2024-03-25 17:14:36 +00:00
Laytan Laats ed742efc33 remove mistakenly added dll
Just noticed I oopsied the glfw dll into the repo
2024-03-25 18:11:41 +01:00
gingerBill 06ee9117d2 Merge pull request #3124 from laytan/update-releases-llvm-version
update LLVM to 17 for CI/releases
2024-03-25 16:36:38 +00:00
gingerBill e100d9264f Merge pull request #3329 from laytan/fix-darwin_amd64-f16-emulation-on-older-microarches
darwin: fix amd64 f16 emulation
2024-03-25 16:35:57 +00:00
gingerBill 53b02c5e6f Fix printing errors issue 2024-03-25 14:46:45 +00:00
gingerBill 50618759a6 Fix error reporting for type cycles 2024-03-25 13:44:00 +00:00
gingerBill 600ca83386 Merge pull request #3326 from rick-masters/fix_fields_wait_signal
Fix fields_wait_signal futex.
2024-03-25 13:29:08 +00:00
gingerBill e5629dafd0 Potentially fix a race condition with parapoly types (related to #3328) 2024-03-25 13:23:43 +00:00
Laytan Laats a2167587ae Merge branch 'fix-darwin_amd64-f16-emulation-on-older-microarches' of github.com:laytan/Odin into update-releases-llvm-version 2024-03-25 14:15:18 +01:00
Laytan Laats b2a35683a4 darwin: fix amd64 f16 emulation
Fixes #3222
2024-03-25 14:07:49 +01:00
Laytan Laats dcc263c618 re-enable core tests on macos arm 2024-03-25 14:05:07 +01:00
Laytan Laats 3b34cf6dbb Also update to LLVM 17 for general CI 2024-03-25 14:05:07 +01:00
Laytan ae9f026f4b llvm 17 on macos arm releases 2024-03-25 14:05:07 +01:00
Laytan Laats 46093bad1e update LLVM to 17 for releases 2024-03-25 14:05:07 +01:00
flysand7 c843002d07 [core/os2]: Move .Broken_Pipe to General_Error enum 2024-03-25 23:35:01 +11:00
rick-masters 6d4f30de1a Fix fields_wait_signal futex. 2024-03-24 16:28:55 +00:00
gingerBill 68ff945419 Remove old error message for #3062 2024-03-24 14:39:42 +00:00
gingerBill cab53e12b7 Add assert message to tell me people to report the bug 2024-03-24 13:53:09 +00:00
gingerBill d92767cb77 Fix #3226 2024-03-24 13:49:51 +00:00
gingerBill 670fc70f1f Remove private attributes for #3227 2024-03-24 13:46:26 +00:00
gingerBill 223a336eb4 Fix #3249 2024-03-24 13:45:10 +00:00
gingerBill 1ea1229516 Fix #3319 2024-03-24 13:42:37 +00:00
gingerBill 6da6393735 Fix #3320 2024-03-24 13:40:10 +00:00
gingerBill 87688936c6 Improve error messages for some wrong constant value attributes 2024-03-24 13:36:50 +00:00
gingerBill 590e52cc05 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-24 13:33:37 +00:00
gingerBill 398af659e5 Fix #3323 2024-03-24 13:33:30 +00:00
gingerBill 2e416c1a48 Merge pull request #3316 from laytan/fix-ld-warnings-and-set-default-minimum-version
darwin: fix ld warnings and set minimum os version
2024-03-24 13:26:35 +00:00
gingerBill 34247b2658 Fix resize bug caused by typo 2024-03-24 12:24:44 +00:00
gingerBill 45d7dd8f27 Fix resize zeroing behaviour 2024-03-24 12:20:39 +00:00
gingerBill 09d7f1337b Merge pull request #3317 from laytan/arm64-abi-fixes
darwin: arm64 abi fixes
2024-03-24 00:41:52 +00:00
flysand7 0f944bc0a1 [core/os2]: Reading from unsized files 2024-03-24 10:52:48 +11:00
Laytan Laats 07a9969a41 darwin: arm64 abi fixes
Since commit b4fe9677a1 some core tests
segfault during build, upon investigation it is because some arg types
were of size 0 and you can't have a 0 sized int.

It also applies the earlier fix for parameters to the return types, this
fixes #3223

Thought I would put this PR up, but I am in no way an expert in abi so
feel free to close for a better fix if there is one.

I am able to run the entire core test suite with `-sanitize:address`
with these changes.
2024-03-23 23:17:01 +01:00
Laytan Laats 15c1e8274d darwin: fix ld warnings and set minimum os version 2024-03-23 22:12:05 +01:00
gingerBill 517d7ae0b0 Add error block around error_line calls 2024-03-23 17:51:56 +00:00
gingerBill efb0933965 Improve error suggestion and propagation for x->y 2024-03-23 16:57:35 +00:00
gingerBill eb51cc6835 Improve error propagation for (*x).y style errors from C-programmers 2024-03-23 16:53:40 +00:00
gingerBill 1d46adb598 Treat *x as an unary operator to improve error messages for common C-programmer mistakes 2024-03-23 16:51:04 +00:00
gingerBill 61aa4558dc Fix #3314 2024-03-23 16:41:50 +00:00
gingerBill 194d3fe6bd Ignore wrong types 2024-03-23 14:59:35 +00:00
gingerBill 624b870f28 Add some basic escape analysis errors for return &x 2024-03-23 14:58:10 +00:00
gingerBill eb61cf6043 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-23 12:56:09 +00:00
gingerBill 7abb459861 Add missing field flags in debug printing 2024-03-23 12:56:04 +00:00
gingerBill e9ac7d5fab Merge pull request #3311 from laytan/fix-simd128-wasm
fix wasm with `-target-features:"simd128"`
2024-03-23 10:42:36 +00:00
Laytan Laats bb5dab342a fix wasm with -target-features:"simd128"
the required procs within wasm would compile to take native v128
arguments in, but the procs are supposed to take in i64's causing bad wasm
modules.

Fixes #3263
2024-03-22 17:04:15 +01:00
gingerBill 7d6e9ef39c Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-21 20:44:00 +00:00
gingerBill d47ba09743 Fix at error printing bug 2024-03-21 20:43:52 +00:00
gingerBill 5c9da66595 Merge pull request #3294 from laytan/update-glfw-from-3.3.8-to-3.4
glfw: update from 3.3.8 to 3.4
2024-03-21 15:43:25 +00:00
gingerBill 9cbb9d8551 Merge pull request #3304 from mailgerigk/master
Remove entry point when compiled with no-entry-point as shared library
2024-03-21 15:19:38 +00:00
gingerBill 6b6f72e7bd Fix #3305 2024-03-21 14:59:47 +00:00
gingerBill c8cdb22f0b Disallow for x in y.(T) and for x in y.? 2024-03-21 14:42:48 +00:00
gingerBill 6e2efce670 Fix missing ERROR_BLOCK(); calls 2024-03-21 12:05:15 +00:00
gingerBill 6d7afd3fa9 Update format version 2024-03-21 11:59:45 +00:00
gingerBill 006ea11c56 Update doc-format for #by_ptr and #no_broadcast parameters 2024-03-21 11:58:32 +00:00
gingerBill 010ffc486c Minor clean up of #no_broadcast handling 2024-03-21 11:54:40 +00:00
gingerBill 29e5f94c2a Add #no_broadcast procedure parameter to disallow automatic array programming broadcasting on procedure arguments 2024-03-21 11:52:48 +00:00
mailgerigk 6a0d2ffcac Merge branch 'odin-lang:master' into master 2024-03-21 00:26:42 +01:00
gingerBill fc587c507a Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-20 22:44:43 +00:00
gingerBill 65cb382135 Fix error handling for type switch statement 2024-03-20 22:44:35 +00:00
Jeroen van Rijn 8f0d74c08d Merge pull request #3292 from Kelimion/rfc3339
Add WiP datetime package and tests.
2024-03-20 23:23:27 +01:00
gerigk 800014e40c Remove entry point when compiled with no-entry-point as shared library 2024-03-20 23:18:08 +01:00
Jeroen van Rijn fda283c55e More better validation. 2024-03-20 23:09:09 +01:00
gingerBill f39b34a8b7 Fix error message 2024-03-20 18:17:06 +00:00
gingerBill 1cc5e23801 Fix #3299 2024-03-20 18:16:46 +00:00
gingerBill 8e0806be2d Fix #3301 2024-03-20 18:09:57 +00:00
gingerBill c17adc98f5 Try doing <{i64, i32}> instead of [i64, i64] for ARM64 12-byte parameters 2024-03-20 17:39:00 +00:00
gingerBill d381b77164 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-20 17:27:12 +00:00
gingerBill e804fbd891 Force packed structs in ABI parameters 2024-03-20 17:27:05 +00:00
Jeroen van Rijn ed5fd15f6e Ensmallen Date struct. 2024-03-20 18:13:29 +01:00
Jeroen van Rijn 89ca15014c Ensmallen Time struct. 2024-03-20 18:09:07 +01:00
gingerBill a5efcfdd78 Merge pull request #3303 from laytan/allow-darwin-cross-arch-linking
darwin: allow cross linking darwin_amd64 from darwin_arm64
2024-03-20 16:56:24 +00:00
Jeroen van Rijn 9c144dd24f Change Ordinal from int to i64 2024-03-20 17:56:22 +01:00
gingerBill c0f9e8d6a3 Merge pull request #3302 from laytan/missing-cmark-api
commonmark: add missing on_exit api
2024-03-20 16:56:11 +00:00
Laytan Laats f312adb26a darwin: allow cross linking darwin_amd64 from darwin_arm64 2024-03-20 17:44:35 +01:00
Laytan Laats 9c879e5e17 commonmark: add missing on_exit api 2024-03-20 17:42:20 +01:00
gingerBill b4fe9677a1 Change ARM64 ABI for integer-like parameters 2024-03-20 16:06:50 +00:00
gingerBill 1951bc45a6 Fix #3133 by show the line of the syntax error 2024-03-20 13:55:47 +00:00
gingerBill ba77a9464c Add suggestion for x: ^T; y = x to be x^ 2024-03-20 13:36:14 +00:00
gingerBill b1dae2d59a Add x: T; y: ^T = x suggestion to do &x 2024-03-20 13:28:17 +00:00
gingerBill 1514d64964 Update ARM64 ABI return type 2024-03-20 11:12:43 +00:00
gingerBill e4c502e79b Add offset (in bytes) field to -json-errors 2024-03-20 11:06:02 +00:00
gingerBill 553a244fec Fix bounds checking 2024-03-20 10:24:39 +00:00
gingerBill 3bff922b6f m[i] on #row_major matrices will reduce the i-th row-vector 2024-03-20 10:23:57 +00:00
gingerBill 56004c56fc Merge pull request #3295 from kavalee/parser-newline-fix
Fixed issue #3264 regarding `core:odin/parser` compound literal not allowing a newline
2024-03-20 10:09:55 +00:00
gingerBill 863ee0b8d1 Merge pull request #3297 from laytan/linker-improvements
linker improvements
2024-03-20 10:09:45 +00:00
Jeroen van Rijn 89ba8b4139 Merge pull request #3298 from laytan/update-macos-releases
update macOS releases for `core:sys/info` and `odin report`
2024-03-20 00:23:53 +01:00
Laytan Laats d0b3b18e26 update macOS releases for core:sys/info and odin report 2024-03-20 00:14:01 +01:00
gingerBill cd7137af60 Update raylib to use #row_major matrices 2024-03-19 21:16:04 +00:00
gingerBill 18fb665bf6 Correct matrix builtins for #row_major 2024-03-19 21:15:47 +00:00
gingerBill a750fc0ba6 Add #row_major matrix[R, C]T
As well as `#column_major matrix[R, C]T` as an alias for just `matrix[R, C]T`.
This is because some libraries require a row_major internal layout but still want to be used with row or major oriented vectors.
2024-03-19 21:05:23 +00:00
Laytan Laats a555862522 linker improvements
`path_to_fullpath` did different things on Windows&Unix, an attempt to
bring them closer together was made here.
This was prompted by the compiler completely ignoring
`foreign import "foo.dylib"` when `foo.dylib` does not exist (because
`path_to_fullpath` returns an empty string).
Causing just unresolved symbol errors, when on Windows it would pass
along the path to the linker and actually say it doesn't exist, which
is now also the case for Unix.
This also fixes some checker errors that relied on the Windows
behaviour, for example: `Error: File name, , cannot be as a library name
as it is not a valid identifier`.

Made `-no-crt` require either `-default-to-nil-allocator` or
`-default-to-panic-allocator` on Unix, the current default allocators
rely on libc and this was not obvious and would immediately bring up
unresolved symbol errors for the linked memory management functions, or
just link with libc anyways because it was foreign imported.

Added a suggestion to install `nasm` with the user's package manager
when assembling using `nasm` fails on Unix, I saw some confusion about
it in the Discord.

Ignore explicit foreign imports of libc. It is already linked in later
on in the linking process and would otherwise (at least on macOS) cause
linker warnings for duplicate libraries. This also makes it so when
using `-no-crt` and importing something that requires libc, linker
errors are given (like I would expect), instead of silently still
linking with libc because it was foreign imported.
2024-03-19 19:57:35 +01:00
gingerBill 433109ff52 Replace gb_exit(1) with exit_with_errors() where appropriate 2024-03-19 16:29:45 +00:00
gingerBill ba428fecdb Add -json-errors 2024-03-19 16:25:09 +00:00
gingerBill 17cc7a2c5e General clean-up for error.cpp 2024-03-19 15:42:59 +00:00
gingerBill 9a2fc6cf4c Serialize errors to make them sortable, deterministic, and generally more control 2024-03-19 15:34:29 +00:00
gingerBill 8ff788f4ff Add better suggestion for [?]T mistake 2024-03-19 13:38:13 +00:00
gingerBill 80ecf5b68a On x: [?]T = {...}, minimize errors by using the [?]T expression as a kind of hint 2024-03-19 13:32:37 +00:00
gingerBill 5159f30c9c Fix error block handling 2024-03-19 13:10:35 +00:00
gingerBill d5daa9fda5 Minimize error propagation of bad array syntax by treating this like a type 2024-03-19 13:05:28 +00:00
gingerBill 89315986d4 Add suggestion when mistyping an array backwards e.g. T[] 2024-03-19 12:37:11 +00:00
Aaron Kavaler 096b4f5454 fixed issue #3264 regarding core:odin/parser not allowing a newline at the end of a compound literal 2024-03-18 20:33:02 -07:00
Laytan Laats 65e68f11f8 glfw: update from 3.3.8 to 3.4 2024-03-18 23:54:26 +01:00
gingerBill ec9ac59323 Unify "Did you mean" strings 2024-03-18 21:37:40 +00:00
gingerBill 97be7feb99 Add list of C identifier suggestions (types and keywords) 2024-03-18 17:32:26 +00:00
gingerBill 00344e1323 Add check to people trying to foreign import C files. 2024-03-18 16:56:01 +00:00
Laytan Laats dd92d3054d add ODIN_ prefix to the new constant 2024-03-18 17:22:58 +01:00
Jeroen van Rijn 07ef969546 Fix test label. 2024-03-18 17:05:40 +01:00
Jeroen van Rijn 72c15d7699 Add WiP datetime package and tests.
A new package `core:time/datetime` has been added which can represent moments much further in the past and future than `core:time`.
It is based on *the* reference work on the subject, Calendrical Calculations Ultimate Edition, Reingold & Dershowitz.

More procedures will be added to it in the future, to for example calculate the 3rd Thursday in March to figure out holidays.
The package has been tested for more than a year and can handle dates 25 quadrillion years into the past and future with 64-bit day ordinals, or 5 million with 32-bit ones.

This also fixes a longstanding bug where converting between YYYY-MM:DD hh:mm:ss and `time.Time` and back could result in a mismatch.

RFC 3339 timestamps can now also be parsed using the `core:time` package.
2024-03-18 16:47:16 +01:00
gingerBill 009b6f44e3 Add loads of checks for common mistakes for C programmer 2024-03-18 15:18:10 +00:00
gingerBill ebd3065aa2 Add error message for C programmers which do Foo{.x = 123} rather than Foo{x = 123} 2024-03-18 14:17:13 +00:00
gingerBill 6cb74b63ec Fix #3286 2024-03-18 12:39:34 +00:00
gingerBill effc71ca43 Fix case: bug with by-ref unions 2024-03-18 12:20:53 +00:00
gingerBill 43d695a990 Fix for x in y where y is an "optional ok" value, but ignores #optional_allocator_error values 2024-03-18 11:21:06 +00:00
gingerBill 19eb2a8890 Fix #3282 2024-03-16 22:24:34 +00:00
gingerBill 3875fb08e8 Fix #3284 2024-03-16 22:12:17 +00:00
Laytan Laats 9c455b2213 darwin: use new wait on address API if possible 2024-03-15 21:43:16 +01:00
gingerBill 04f0fbf23a Merge pull request #3272 from iansimonson/add_getrusage_darwin
Add getrusage syscall for mac/darwin
2024-03-14 19:45:25 +00:00
gingerBill 51d6a254cf Merge pull request #3277 from kavalee/slice-unique-fixes
fixed slice.unique and slice.unique_proc
2024-03-14 19:40:18 +00:00
Ian Simonson 835effdef1 Use c.long rather than int 2024-03-14 09:36:57 -07:00
Ian Simonson c7bec2962e Fix __darwin_suseconds_t definition
__darwin_suseconds_t is defined as long which on macos
64 bit systems is equivalent to 8 bytes. It is equivalent
to Odin int type _not_ i32
2024-03-14 07:21:26 -07:00
Aaron Kavaler ac634acd4b fixed slice.unique and slice.unique_proc 2024-03-13 19:19:31 -07:00
Ian Simonson 34c4389d75 No need for timeval definition
It already existed so lets just use that rather than
redeclaring it
2024-03-13 14:58:56 -07:00
Ian Simonson 8917a7ef88 Make RUsage more inline with macos man page
Swap to tabs to adhere to the Odin Core library standard
and also rename the rusage fields to match the actual
definitions from the macos bsd man pages
2024-03-13 14:51:17 -07:00
gingerBill 45d5066029 Merge pull request #3274 from iansimonson/darwin_munmap_syscall_fix
Fix syscall_munmap in darwin
2024-03-13 20:17:31 +00:00
gingerBill b47eeac414 Merge pull request #3275 from Chickenkeeper/bit-set-error-message-fix
Improve bit_set error message
2024-03-13 20:17:06 +00:00
Chris 5cc936245c Improve bit_set error message 2024-03-13 19:50:41 +00:00
gingerBill e68d3c8bbc Merge pull request #3143 from Pariatech/cgltf-linux-build
Adding linux & darwin makefile for cgltf
2024-03-13 17:47:18 +00:00
Ian Simonson d7b1901b16 Fix syscall_munmap in darwin
this was using the .mmap syscall number when it should be
using the .munmap syscall number
2024-03-13 10:39:50 -07:00
gingerBill 97d6bf6d8f Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-13 16:30:28 +00:00
gingerBill 271f84ab5b Expect stream as a field directly on os2.File 2024-03-13 16:30:22 +00:00
gingerBill 75b60fdb12 Merge pull request #3273 from Chickenkeeper/mutex-allocator-fix
Make Mutex_Allocator use Ada_Case
2024-03-13 15:45:52 +00:00
Chris 55141bdbb1 Make Mutex_Allocator use Ada_Case 2024-03-13 15:29:02 +00:00
gingerBill 5f2496226f Change return to panic 2024-03-13 15:17:21 +00:00
gingerBill dee66b8451 Fix typo due to deletion 2024-03-13 15:14:31 +00:00
gingerBill 4aec2de7bd Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-13 15:12:19 +00:00
gingerBill ac10f504e4 Add infinite loop check and early out in map_insert_hash_dynamic 2024-03-13 15:12:14 +00:00
Ian Simonson 26d107ce64 Add getrusage syscall for mac/darwin
The syscall number existed but the wrapper for calling it
did not. Also adds the RUsage struct to receive the data.

Naming is kept the same as in sys/linux
2024-03-13 08:07:12 -07:00
gingerBill d62c92f5a9 Merge pull request #3267 from harold-b/copy-dirent-path
Retain copies of `dirent->name` for .odin files when using `read_directory`
2024-03-13 12:23:28 +00:00
gingerBill 5c1646a6b3 Merge pull request #3271 from rick-masters/too_many_poly_args
Fix check for too many arguments to a polymorphic record type
2024-03-13 12:23:19 +00:00
Pariatech b539bb2693 Update vendor/cgltf/src/Makefile
missing darwin folder

Co-authored-by: Laytan <laytanlaats@hotmail.com>
2024-03-13 07:37:42 -04:00
rick-masters 7bc962b852 Fix variable used to index polymorphic parameter. 2024-03-12 23:58:20 +00:00
rick-masters f7ec628cb2 Fix check for too many arguments to a polymorphic record type. 2024-03-12 23:57:13 +00:00
Jeroen van Rijn 23f3898b4e Merge pull request #3270 from edyu/master
Expose internal invmod and int_exponent_mod with more consistent naming
2024-03-12 19:06:10 +01:00
Ed Yu dd74a57c44 Expose internal invmod and int_exponent_mod with more consistent naming 2024-03-12 11:01:09 -07:00
gingerBill d6353daf91 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-12 12:11:53 +00:00
gingerBill c7c6852057 Support swizzle selector syntax .xyzw for #simd vectors 2024-03-12 12:11:48 +00:00
Harold Brenes b543be0d15 Copy file names fromdirent into FileInfo during read_directory 2024-03-11 18:09:41 -04:00
gingerBill 47837b206e Merge pull request #3266 from nnym/master
Use a POSIX-compliant equality operator in `build_odin.sh`.
2024-03-11 13:00:01 +00:00
gingerBill 9a41a450e7 Add builtin map_upsert 2024-03-11 12:30:24 +00:00
Muhammad f20d0202fa Use a POSIX-compliant equality operator. 2024-03-11 06:05:42 +00:00
gingerBill 8721d03cfe Merge pull request #3199 from JamesDSource/soa
Add into_dynamic_soa, unordered_remove_soa, and ordered_remove_soa
2024-03-09 16:56:04 +00:00
gingerBill d06575dd49 Merge pull request #3149 from IllusionMan1212/missing-x11-funcs
vendor/x11: added XQueryExtension, XGetEventData, and XFreeEventData
2024-03-09 16:53:11 +00:00
gingerBill 568b07473f Merge pull request #3148 from IllusionMan1212/cookie-struct-fix
vendor/x11: fix "display" with no pointer in XGenericEventCookie
2024-03-09 16:52:53 +00:00
gingerBill 04666746d7 Merge pull request #3261 from spindlebink/fix-orthonormalize
Properly initialize return matrices in linalg.orthonormalize
2024-03-09 15:51:53 +00:00
spindlebink b2e7eb4db4 Properly initialize return matrices in linalg.orthonormalize 2024-03-09 09:10:25 -06:00
gingerBill f88af59372 Merge pull request #3258 from deckarep/patch-1
Base/runtime (darwin): Incorrect write syscall on Darwin - fixes stderr output.
2024-03-09 11:11:09 +00:00
Ralph Caraveo a974c08aff Incorrect write syscall on Darwin - fixes stderr output.
On macos, Odin was previously swallowing errors that would be reported via stderr. 

I've confirmed with @laytan on Discord that this fixes the issue.
2024-03-08 16:19:59 -08:00
Jeroen van Rijn fc9d3ec5a7 Merge branch 'master' of github.com:odin-lang/Odin 2024-03-08 18:06:13 +01:00
Jeroen van Rijn db03c86544 Fix typo in big.internal_invmod
Fixes #3253
2024-03-08 18:05:34 +01:00
gingerBill 51d12acab3 Enforce linalg 2024-03-08 13:28:15 +00:00
gingerBill 032e193d0d Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-08 13:20:33 +00:00
gingerBill 0f83ab466f Minor change to spacing when printing a map 2024-03-08 13:20:27 +00:00
gingerBill 53ce945034 Merge pull request #3230 from avanspector/haiku
Add Haiku OS support
2024-03-08 11:15:13 +00:00
gingerBill 0e168dd292 Merge pull request #3241 from Hyrtwol/sys-windows
Gathered what I made for sys/windows
2024-03-07 15:26:31 +00:00
gingerBill 5ff6a25bdc Improve formatting for %# on maps 2024-03-07 13:45:52 +00:00
gingerBill b6b8e640f5 Replace b with _ as it is not needed 2024-03-07 13:41:54 +00:00
gingerBill 573402c97e Begin work on %w - 'w'rite as valid Odin literals 2024-03-07 13:39:44 +00:00
gingerBill e01701ce0e Fix typo 2024-03-07 12:32:46 +00:00
gingerBill f9b14500be Add default_commit_size to virtual.Arena 2024-03-07 12:30:34 +00:00
gingerBill 4bb7cd5e4b Add bit_field option to core_type in the compiler 2024-03-07 11:31:00 +00:00
gingerBill 6d5b1800fe Fix typo 2024-03-06 16:34:17 +00:00
gingerBill 0f5328973e Add mem.Mutex_Allocator 2024-03-06 16:33:54 +00:00
gingerBill 65277ddd6b Add missing attribute 2024-03-06 16:30:39 +00:00
gingerBill 46718d6d85 Add Query_Info to Buddy_Allocator 2024-03-06 16:26:10 +00:00
gingerBill 2a0543d2f0 Add mem.Buddy_Allocator 2024-03-06 16:14:45 +00:00
gingerBill 792f06a234 Fix indentation 2024-03-06 15:29:07 +00:00
gingerBill 14f7619cdc Merge pull request #3069 from pcleavelin/master
Respect `-lld` CLI arg on non-windows machines
2024-03-06 15:25:16 +00:00
gingerBill ffa3669d02 Add virtual.Arena utilities: new, new_aligned, make 2024-03-06 15:23:56 +00:00
gingerBill b1903b915b Change to IEEE 754-2008 conformance for min/max runtime operations. 2024-03-06 15:16:11 +00:00
gingerBill a7bab89c93 Unify min/max semantics for simd_(min|max) 2024-03-06 15:07:21 +00:00
gingerBill a1ee9e7035 Change min/max runtime behaviour to match IEEE 754-2019 2024-03-06 15:04:46 +00:00
gingerBill c05a92ab3e Merge pull request #3215 from blob1807/json-better-enum-support
core:encoding/json Add support for writing enum value names
2024-03-06 14:56:19 +00:00
gingerBill 5789df5c0c Disable core library tests for ARM (Silly CI) 2024-03-06 14:54:23 +00:00
gingerBill 78c05a49a3 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-06 14:52:04 +00:00
gingerBill 8d496dc3d0 Fix #3173 2024-03-06 14:51:56 +00:00
gingerBill 703eab2f15 Merge pull request #3229 from Yawning/feature/moar-crypto
core/crypto: More improvements/additions
2024-03-06 14:49:15 +00:00
gingerBill 04bfc926ee Merge pull request #3238 from laytan/libc-free-raylib-and-rectpack
makes raylib and stb_rect_pack free of libc
2024-03-06 14:11:41 +00:00
gingerBill 791c9c16ba Merge pull request #3242 from laytan/fix-cgltf-binding
fix minor cgltf binding issue
2024-03-06 14:11:27 +00:00
gingerBill 6d3a57b4ac Zero intrinsics.alloca 2024-03-06 13:48:37 +00:00
gingerBill c7b8f1fb0a Use intrinsics.alloca rather than context.temp_allocator in json.unmarshal_object 2024-03-06 13:42:38 +00:00
gingerBill 462f3f4abd Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-06 11:49:24 +00:00
gingerBill 5eef29290c Fix #3250 2024-03-06 11:49:17 +00:00
gingerBill a944aa406d Merge pull request #3246 from edyu/master
For invmod, b has to be > 1, fix a logic typo
2024-03-05 14:00:52 +00:00
gingerBill 8c6c2543da Merge pull request #2999 from laytan/crypto-random-bytes-on-freebsd-and-darwin
add crypto.rand_bytes for Darwin and BSD
2024-03-05 12:55:37 +00:00
gingerBill 7ae22b7ce5 Update are_types_identical for bit_field 2024-03-04 20:22:49 +00:00
gingerBill 3e295734cb Correct is_type_comparable for bit_field 2024-03-04 20:10:34 +00:00
Ed Yu de41c2256d For invmod, b has to be > 1, fix a logic typo 2024-03-04 10:16:19 -08:00
blob1807 b419615002 Apply suggestions from code review
Fixing spelling mistakes pointed out & fixed by layton.

Co-authored-by: Laytan <laytanlaats@hotmail.com>
2024-03-04 21:20:13 +10:00
Yawning Angel c044e295ce vendor/botan: Remove
This is infrequently maintained, and has been a strict subset of what is
available in `core:crypto` for a while.  Instead of improving the
bindings, it is better to spend resources improving `core:crypto`.
2024-03-04 18:20:56 +09:00
Yawning Angel 87ab3f5dc8 tests/core/crypto: Use the tests/common boilerplate 2024-03-04 18:20:56 +09:00
Yawning Angel fa8dd5a13b core/crypto: Misc cleanups and documentation improvements 2024-03-04 17:50:45 +09:00
Yawning Angel b818ebc02f core/crypto/kmac: Initial import 2024-03-04 17:50:45 +09:00
Yawning Angel c04a53e453 core/crypto/tuplehash: Initial import 2024-03-04 17:50:43 +09:00
Yawning Angel 15287a771f core/crypto/shake: Support cSHAKE 2024-03-04 17:50:24 +09:00
Yawning Angel 2db31cf0d5 core/crypto/_sha3: Cleanups, prepare for cSHAKE 2024-03-04 14:51:38 +09:00
Yawning Angel 550e798c1b core/crypto/hkdf: Initial import 2024-03-04 14:51:38 +09:00
Yawning Angel 290168f862 core/crypto/pbkdf2: Initial import 2024-03-04 14:51:38 +09:00
Yawning Angel 2f2a92866b core/crypto/hmac: Add a clone method 2024-03-04 14:51:38 +09:00
blob1807 116edb9052 convert spaces to tabs 2024-03-04 15:09:17 +10:00
blob1807 9070e613a4 Return underlining value instead of panicing
when no name it found.

Renamed use_enum_value_names to use_enum_names
it get the same point across & inline with the
reflect procs
2024-03-04 15:06:30 +10:00
Thomas la Cour 6243160ecd Cleanup of ShowCursor after Bill pushed a fix 2024-03-03 19:53:34 +01:00
Thomas la Cour 980ee3310f didn't help :/ so removed it to see if it can build again 2024-03-03 19:53:34 +01:00
Thomas la Cour c5d5d055ac Trying to resolve a strange nameclash on ShowCursor seems like raylib is also defining that. Unsure why exactly this is related so for now just tried to change the name here to _ShowCursor :/ 2024-03-03 19:53:34 +01:00
Thomas la Cour a783d4ce5b Callback types 2024-03-03 19:53:33 +01:00
Thomas la Cour a7b09a24b7 Waveform Functions
https://learn.microsoft.com/en-us/windows/win32/multimedia/waveform-functions
2024-03-03 19:53:33 +01:00
Thomas la Cour 602e000379 hid usage flags
https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/hid-usages
2024-03-03 19:53:33 +01:00
Thomas la Cour f93074a082 A few extra gdi procs for icons, cursors and drawing 2024-03-03 19:53:33 +01:00
Thomas la Cour 28f05e8aaa SHChangeNotify + flags
https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shchangenotify
2024-03-03 19:53:33 +01:00
Thomas la Cour 9eb1596939 Flags for GlobalAlloc
https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-globalalloc
2024-03-03 19:53:33 +01:00
Thomas la Cour 8412352e5a bitmap v5 header
https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header
2024-03-03 19:53:33 +01:00
Jeroen van Rijn 4c35633e01 math.big constants were no longer initialized
Fixes #3243
2024-03-03 15:09:38 +01:00
Laytan Laats b1371d5c7a fix minor cgltf binding issue 2024-03-01 20:33:23 +01:00
Laytan Laats 3ada83a503 clean imports 2024-03-01 20:32:07 +01:00
Laytan Laats 50ded324e0 move va_list into core:c 2024-03-01 20:30:19 +01:00
gingerBill 674bd94f72 Add check to see if raylib is imported with ShowCursor on sys/windows 2024-03-01 18:30:23 +00:00
gingerBill ff24cfe314 Fix debug issue with maps 2024-03-01 14:00:14 +00:00
gingerBill bc191d4f84 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-03-01 13:36:12 +00:00
gingerBill 5c20676c76 Fix type for SDL LoadFile(_RW) 2024-03-01 13:36:06 +00:00
gingerBill 11b7be1640 Merge pull request #3203 from listeriaceae/master
port math.round from Golang
2024-03-01 11:33:43 +00:00
gingerBill 6ae8288142 Merge pull request #3240 from laytan/macos-self-contained-releases
reduce dynamic library dependencies for macos release
2024-03-01 11:33:20 +00:00
gingerBill 7f1069cb0b Add Recursive_Mutex procedures to procedure groups 2024-03-01 11:31:13 +00:00
blob1807 41fbaaf1d3 changed to use reflect.enum_name_from_value_any 2024-03-01 14:53:39 +10:00
blob1807 f14babe419 Merge branch 'odin-lang:master' into json-better-enum-support 2024-03-01 14:24:51 +10:00
avanspector 0bb2327d76 Merge branch 'haiku' of https://github.com/avanspector/Odin into haiku 2024-03-01 00:43:13 +01:00
avanspector f92042e7dd Merge branch 'odin-lang:master' into haiku 2024-03-01 00:42:28 +01:00
avanspector 1861ecff86 Merge branch 'haiku' of https://github.com/avanspector/Odin into haiku 2024-03-01 00:42:08 +01:00
avanspector d4d9f55556 Update threading.cpp 2024-03-01 00:41:28 +01:00
Laytan Laats 23cd64ec35 reduce dynamic library dependencies for macos release 2024-02-29 23:39:18 +01:00
Laytan Laats 6734a7096a makes raylib and stb_rect_pack free of libc 2024-02-29 23:33:25 +01:00
gingerBill 3263e54144 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-02-29 20:20:43 +00:00
gingerBill 6805b85f89 Fix leap year bug 2024-02-29 20:20:37 +00:00
gingerBill 88c97cbbd0 Merge pull request #3237 from laytan/macos-self-contained-releases
fix releases being a zip of a zip
2024-02-29 16:16:04 +00:00
Laytan Laats 17b1c8d338 fix releases being a zip of a zip 2024-02-29 17:00:20 +01:00
gingerBill 0a5c85f8e3 Merge pull request #3207 from Tetralux/printfln
Add `fmt.*printfln`
2024-02-29 14:04:38 +00:00
gingerBill 9c20df5b1b Merge pull request #3201 from Yawning/feature/avl
core/container/avl: Add an AVL tree implementation
2024-02-29 14:02:45 +00:00
avanspector 5d6b4eda1e Merge branch 'odin-lang:master' into haiku 2024-02-29 03:16:04 +01:00
avanspector 290ada7f90 add exit to core:os 2024-02-28 22:43:55 +01:00
avanspector bf37bee4f7 improve core:sys 2024-02-28 22:29:06 +01:00
gingerBill 7df7fec6f7 Keep -vet happy 2024-02-28 18:27:49 +00:00
gingerBill e423a6d692 Make types distinct 2024-02-28 18:25:29 +00:00
gingerBill f4c74a9f32 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-02-28 18:25:06 +00:00
gingerBill dce176fa39 Remove unnecessary use of transmute 2024-02-28 18:24:59 +00:00
gingerBill d666ff3744 Merge pull request #3135 from colrdavidson/basic_egl
Add support for basic EGL on Linux
2024-02-28 14:42:49 +00:00
gingerBill 1678391db3 Merge pull request #3224 from laytan/macos-self-contained-releases
Make MacOS releases self contained / bundle LLVM
2024-02-28 13:44:50 +00:00
gingerBill d1174f66bc Fix #3233 2024-02-28 13:41:54 +00:00
gingerBill 84a7e03178 Merge pull request #3220 from laytan/promote-types-in-c-varargs
Promote types in `#c_varargs` according to C rules
2024-02-28 10:49:28 +00:00
gingerBill 1bffc8baac Merge pull request #3228 from EddieWoodley/glfw-raw-mouse-supported
vendor/glfw: Add RawMouseMotionSupported to wrapper
2024-02-28 10:48:08 +00:00
gingerBill c558b694eb Merge pull request #3232 from karl-zylinski/raylib-5-binding-errors
Additional fixes for errors in Raylib bindings since Raylib 4 -> 5 upgrade.
2024-02-28 10:46:41 +00:00
gingerBill a06bde729b Merge pull request #3234 from odin-lang/rtti-table-layout
Change `runtime.type_table` layout to allow RTTI to be constantly initialized
2024-02-27 16:25:47 +00:00
gingerBill 5107bdc06b Make lb_type_info use a procedure to load the global value 2024-02-27 15:45:53 +00:00
gingerBill 5137d12d36 Fix lb_type_info for the new layout 2024-02-27 15:40:45 +00:00
gingerBill c6ee025063 Add type info generation for bit_field 2024-02-27 15:27:51 +00:00
gingerBill 826cf1508b Remove __$startup_type_info procedure 2024-02-27 15:09:33 +00:00
gingerBill 51edf01162 Change type info table to be initializable constantly
[]Type_Info -> []^Type_Info
2024-02-27 15:07:55 +00:00
avanspector 87f6f3a1fe Merge branch 'haiku' of https://github.com/avanspector/Odin into haiku 2024-02-27 02:38:19 +01:00
avanspector fca691a066 fix core:thread and a memory leak
in the future probably native non-pthread implementation for haiku will be required
2024-02-27 02:38:06 +01:00
avanspector 494cac02d7 Merge branch 'odin-lang:master' into haiku 2024-02-27 02:00:19 +01:00
avanspector 38c69b9691 small fixes 2024-02-27 01:59:17 +01:00
Colin Davidson deb8922181 Merge branch 'master' into basic_egl 2024-02-26 12:17:53 -05:00
Karl Zylinski cdda8f0eb9 Additional fixes for errors in Raylib bindings since Raylib 4 -> 5 upgrade. 2024-02-26 17:16:45 +01:00
gingerBill cba8cb2201 Merge pull request #3231 from karl-zylinski/raylib-model-animation-binding-fix
Fix ModelAnimation in raylib.odin missing a field
2024-02-26 12:57:39 +00:00
Karl Zylinski ae67f37fc1 Fix ModelAnimation in raylib.odin missing a field. 2024-02-26 13:32:16 +01:00
avanspector 8d4bb35bcc Update futex_haiku.odin 2024-02-26 08:14:05 +01:00
avanspector 180902468f Revert "fix haiku"
This reverts commit 7290c69257.
2024-02-26 08:09:58 +01:00
avanspector 7290c69257 fix haiku 2024-02-26 08:07:17 +01:00
avanspector 3ebf5dcc0e fix haiku 2024-02-26 07:59:53 +01:00
avanspector 9d4c2ba0d8 fix haiku 2024-02-26 07:43:10 +01:00
avanspector 1d79521e81 fix sys/haiku 2024-02-26 06:18:33 +01:00
avanspector 31d7ef5696 Update os_haiku.odin 2024-02-26 06:16:13 +01:00
avanspector 8c621453ae update sys/haiku 2024-02-26 06:13:54 +01:00
avanspector c712af3bc2 Update os_haiku.odin 2024-02-26 04:54:21 +01:00
avanspector f0a89f8d5d add sys/haiku 2024-02-26 04:41:30 +01:00
avanspector 9b839621a9 Update os_haiku.odin 2024-02-25 18:49:42 +01:00
avanspector 66456714e1 update pthread 2024-02-25 18:45:00 +01:00
avanspector fc8e5b8a61 Update os_haiku.odin 2024-02-25 18:31:14 +01:00
avanspector 05cfc89283 fix core:os 2024-02-25 18:20:03 +01:00
avanspector dc5cf23066 add haiku to unix 2024-02-25 16:50:04 +01:00
avanspector 2e80879e88 Update os_haiku.odin 2024-02-25 16:24:50 +01:00
avanspector dfa0ccf976 Update entry_unix.odin 2024-02-25 03:46:52 +01:00
avanspector 6c16860be6 fix runtime and libc 2024-02-25 03:43:41 +01:00
avanspector d032cff23b Update os_specific_haiku.odin 2024-02-25 03:38:15 +01:00
avanspector c3746d9f56 fix core and libc 2024-02-25 03:32:35 +01:00
avanspector 0fa6ba726f add haiku build token 2024-02-25 03:03:12 +01:00
avanspector b03f17dd8d add haiku to base:runtime and core:c/libc 2024-02-25 02:59:45 +01:00
avanspector 24c8b15409 small fixes 2024-02-25 02:38:35 +01:00
avanspector 028a79e66c Update threading.cpp 2024-02-25 02:34:41 +01:00
avanspector 0a6673220b Merge branch 'haiku' of https://github.com/avanspector/Odin into haiku 2024-02-25 02:24:58 +01:00
avanspector 88add0b6b1 Improve Haiku support 2024-02-25 02:24:52 +01:00
Eddie Woodley 8a78b0d241 vendor/glfw Add RawMouseMotionSupported to wrapper 2024-02-24 23:06:05 +00:00
avanspector 824c831190 Implement futex 2024-02-24 23:46:55 +01:00
Yawning Angel 874d6ccb60 core/container/avl: Initial import 2024-02-24 14:05:15 +09:00
Yawning Angel db3279e7da test/core/container: Refactor for multiple container types 2024-02-24 14:05:15 +09:00
Yawning Angel 9251e06143 tests/core: Bring the Makefile more in-sync with build.bat 2024-02-24 14:05:15 +09:00
gingerBill a642ea0b28 Add intrinsics.type_bit_set_backing_type 2024-02-23 11:38:23 +00:00
gingerBill 00fc4c4e1b Make that warning an error with -vet-style 2024-02-22 20:21:21 +00:00
gingerBill 9ea11da00f Add warning when using bit_field when a bit_set would be a much better idea. 2024-02-22 20:10:56 +00:00
gingerBill a8909f06ae Improve parsing for bit_field 2024-02-22 20:10:38 +00:00
Laytan Laats ee543a304a Zip so compiler keeps executable permissions 2024-02-22 21:04:08 +01:00
gingerBill 5c5b78cbbe Improve bit_field printing 2024-02-22 19:48:44 +00:00
gingerBill 54515af8cc Add field tags to bit_field 2024-02-22 19:41:48 +00:00
Laytan Laats b894df2125 Make MacOS releases self contained / bundle LLVM 2024-02-22 20:38:25 +01:00
gingerBill 3f193d7446 Format change 2024-02-22 19:27:13 +00:00
gingerBill e127d21fed Check for pseudo-fields before bit fields 2024-02-22 19:24:50 +00:00
gingerBill 3060225f46 Simplify usage code 2024-02-22 19:24:16 +00:00
gingerBill c14b9d461a Support using of a bit_field within a struct 2024-02-22 19:14:16 +00:00
gingerBill 8060e3170e Remove debug message 2024-02-22 18:53:02 +00:00
gingerBill 980947b355 Give a better error message when the user uses context as if it was an identifier in a field list. 2024-02-22 18:52:17 +00:00
gingerBill fd987b29ff Improve error handling with assignments to bit_field using constants 2024-02-22 18:47:43 +00:00
gingerBill afcc2889ec Support compound literals for bit_field 2024-02-22 18:41:15 +00:00
gingerBill 5f001f6d51 Allow casting between a bit_field and its backing type 2024-02-22 18:15:13 +00:00
gingerBill fc07211772 Change hack a little 2024-02-22 17:43:37 +00:00
gingerBill c9e37a08be Add backing type of a bit_field to the doc-format 2024-02-22 17:41:22 +00:00
gingerBill dcbcf75269 Add doc-format support for bit_field 2024-02-22 17:37:42 +00:00
gingerBill 59479b2ba6 Fix typo 2024-02-22 17:26:32 +00:00
gingerBill 007bd993a6 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-02-22 17:24:48 +00:00
gingerBill 5a84a08225 Add general support for bit_fields 2024-02-22 17:24:42 +00:00
Laytan Laats bb23648c71 Also convert and promote bit_set types for #c_varargs 2024-02-22 18:17:52 +01:00
gingerBill a4b8c1ea17 Begin work adding bit_field 2024-02-22 15:55:54 +00:00
Jeroen van Rijn 20e75cd463 Merge pull request #3221 from Tetralux/net/reservedports
[net] Better error code for binding a privileged port without root access
2024-02-22 16:23:15 +01:00
Tetralux d74ddb2d91 fixup 2024-02-22 15:01:55 +00:00
Tetralux ec0831da70 [net] Better error code for binding a privileged port without root access on Darwin
This condition results in os.EACCESS, which we were translating to Broadcast_Disabled.
This was the case because binding to the broadcast address on a UDP port, without setting the BROADCAST flag, also results in this error.

Given the fact that reserved ports also produce this error, we now check for this condition in net.bind() and translate it to a custom, clearer error:
Privileged_Port_Without_Root.
2024-02-22 14:55:27 +00:00
gingerBill b12ba1508e Update vulkan bindings for to work with the new keyword bit_field 2024-02-22 14:09:12 +00:00
gingerBill f3b0b82461 Fix futex 2024-02-22 14:04:31 +00:00
gingerBill fea38f6910 Minor changes to futex implementation on Linux 2024-02-22 14:01:39 +00:00
gingerBill 213b2fd0f8 Add bit_field as a keyword 2024-02-22 14:01:23 +00:00
gingerBill 42d595f6a1 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-02-22 13:59:27 +00:00
gingerBill 656de10ba4 Minor changes to sync/chan (HIGHLY EXPERIMENTAL) 2024-02-22 13:59:22 +00:00
Laytan Laats f6f3a760bc Promote types in #c_varargs according to C rules 2024-02-21 22:05:11 +01:00
gingerBill bafc791f1c Merge pull request #3219 from laytan/fix-terminators-in-defer
Fix divergent proc call in `defer`
2024-02-21 18:53:40 +00:00
Laytan Laats 1fc256dd90 Fix divergent proc call in defer
Fixes #3216
Fixes #2985
2024-02-21 19:43:34 +01:00
gingerBill 41549b502b Basic concept of core:sync/chan 2024-02-21 15:24:24 +00:00
gingerBill f989f4df3e Add sync.One_Shot_Event 2024-02-21 13:08:12 +00:00
gingerBill 21d1c0e5a4 Revert change since it is not needed 2024-02-21 12:58:26 +00:00
gingerBill d7b7804215 if -> while in wait_signal_until_available to check for spurious wake-ups 2024-02-21 12:55:26 +00:00
gingerBill 8472338bfa Add reflect.enum_name_from_value and reflect.enum_name_from_value_any 2024-02-21 12:43:09 +00:00
gingerBill 1de1d97429 Merge pull request #3213 from laytan/add-backend-info-to-odin-report
Add backend info to odin report
2024-02-21 12:33:06 +00:00
gingerBill f21ead4f78 Merge pull request #3212 from laytan/improve-net-tests
Improve net tests
2024-02-21 12:32:43 +00:00
blob1807 a95cead8e7 add all inter types to switch 2024-02-21 14:05:50 +10:00
blob1807 c276b1c0bc replace spaces with tabs 2024-02-21 11:51:29 +10:00
blob1807 b39ef29ec6 add missing comma 2024-02-21 11:15:35 +10:00
blob1807 b2b8b14955 Add better support for Enums in json
Can now output enum value's name instead of its underlineing value
2024-02-21 11:07:03 +10:00
blob1807 7e0473dded Revert json union fix 2024-02-21 11:03:15 +10:00
Laytan Laats e6bd79c882 Make sure we are listening before starting the client 2024-02-20 23:54:11 +01:00
Laytan Laats 9e417592e3 Add logs to flaky test 2024-02-20 23:45:15 +01:00
Laytan Laats db87c34613 Add backend info to odin report 2024-02-20 23:23:56 +01:00
Laytan Laats 159257597a Convert weird DOS line-endings to UNIX 2024-02-20 23:22:05 +01:00
Laytan Laats bdd6a86d73 Remove flaky test
It wasn't testing the right thing in the previous
iteration.
And in this iteration the behaviour is a timeout on Unix, and
nothing on Windows.
2024-02-20 23:15:18 +01:00
Laytan Laats 1ab3ec5731 Improve net tests
Watching the sporadic CI failures it seems to come from these tests a
lot of the time, this PR cleans up and simplifies (while testing the
same things):

1. Lots of tests were using threads without a need for it
2. Tests had hardcoded `time.sleep` calls which is never a good idea
3. An unclear abstraction was implemented without a real need
4. They weren't being ran on non-windows
5. The `client_connects_to_open_but_not_accepting_port` was not doing
   what you wanted to test for, the `tcp_server` proc was returning, and
   then `dial` was called, which meant that the server already closed
   and you got a refusal error. Now it correctly listens without
   accepting, which even results in a different error because the kernel
   buffer would have buffered the send
2024-02-20 23:06:18 +01:00
blob1807 004cd4933d Merge branch 'master' of https://github.com/blob1807/Odin 2024-02-21 00:43:27 +10:00
blob1807 14ee2181cb Fix bug https://github.com/odin-lang/Odin/issues/3173 2024-02-21 00:42:11 +10:00
gingerBill b43c1f2b5b Merge pull request #3134 from laytan/non-universal-binaries-raylib
vendor/raylib: change to non-universal binaries
2024-02-19 14:18:44 +00:00
Tetralux b1e608bfba [fmt] Add *printfln()
Adds the following procedures, which just call through to the `printf` versions, with `newline = true`; a new parameter
also added in this commit.
In all cases, `wprintf` is the one that ultimately writes the newline, if requested.

- printfln
- fprintfln
- eprintfln
- aprintfln
- tprintfln
- bprintfln
- caprintfln
- ctprintfln
- sbprintfln
- wprintfln
2024-02-18 16:14:59 +00:00
gingerBill 57c5455827 Merge pull request #3206 from flysand7/linux-fdflags-fix
[sys/linux]: Fix numeric values for socket fd flags
2024-02-18 14:56:31 +00:00
flysand7 cc185d98b4 [sys/linux]: Fix numeric values for socket fd flags 2024-02-18 23:04:41 +11:00
Juan Ignacio Díaz cd61251d39 add Go license 2024-02-17 11:49:07 -03:00
gingerBill 43a199b57b Merge branch 'master' of https://github.com/odin-lang/Odin 2024-02-17 13:42:02 +00:00
gingerBill fa1875a8f1 Minor changes to os2 2024-02-17 13:41:54 +00:00
James Duran 44aae76294 Fix syntax mistake again
Sorry for being blind
2024-02-16 10:55:55 -08:00
James Duran 009cebe8bf Fix Syntax Mistake 2024-02-16 10:50:54 -08:00
James Duran 9399cb53b6 Make into_dynamic_soa more concise and remove builtin tag from it 2024-02-16 10:46:46 -08:00
gingerBill bed81c8829 Merge pull request #3197 from laytan/update-miniaudio
Update miniaudio to 0.11.21
2024-02-16 18:08:29 +00:00
Juan Ignacio Díaz 7a592cbb31 port math.round from Golang 2024-02-16 13:23:44 -03:00
Slendi c178f7199d Get Odin to compile on Haiku
This patch makes Odin to compile on Haiku which is a good first step.
Now, all that's needed to do is to figure out how to do futexes, which
I am blaming for the program crashing.
2024-02-15 15:51:28 +02:00
James Duran a4d3777ab2 Added into_dynamic_soa, unordered_remove_soa, and ordered_remove_soa 2024-02-14 16:51:09 -08:00
Laytan Laats e931c82b9b miniaudio: add version mismatch check 2024-02-14 21:55:15 +01:00
laytan a7f0275093 miniaudio: update .lib to 0.11.21 2024-02-14 21:17:12 +01:00
laytan 9ab2fbea00 miniaudio: update .lib 2024-02-14 21:07:57 +01:00
Laytan Laats 7fe86ed565 miniaudio: fix wrong sizes for ma_device 2024-02-14 21:00:26 +01:00
Laytan Laats 16584779fb miniaudio: update to 0.11.21 2024-02-14 19:57:57 +01:00
gingerBill c5c2a4d09d Fix typo 2024-02-13 17:13:39 +00:00
gingerBill 912c326d8b Fix typo that causes map info debug issues 2024-02-13 17:06:04 +00:00
gingerBill d496dbf3a0 Fix race condition with #soa 2024-02-13 16:54:41 +00:00
Laytan Laats 88e6980b13 fix build tags 2024-02-13 17:34:40 +01:00
Laytan Laats 0d413b8136 implement part of core foundation framework bindings for err message 2024-02-13 17:34:40 +01:00
Laytan Laats 32a4a5e601 remove darwin from bsd filename 2024-02-13 17:34:40 +01:00
Laytan Laats 91cf0826c1 use Security.framework with SecRandomCopyBytes for rand_bytes on darwin 2024-02-13 17:34:40 +01:00
Laytan Laats 3bc172c70b add crypto.rand_bytes for Darwin and FreeBSD 2024-02-13 17:34:00 +01:00
gingerBill cbfb32c34c Fix race condition with regards to #soa arrays by using the fields mutex 2024-02-13 16:21:41 +00:00
gingerBill 5cd57a3a7f Use Raw_Map as the debug information for a map 2024-02-13 15:50:07 +00:00
gingerBill 563ce2bd81 Merge pull request #3193 from Lperlind/master
Add ID3DUserDefinedAnnotation
2024-02-13 14:17:20 +00:00
Lucas Perlind 9469b90b01 Add ID3DUserDefinedAnnotation 2024-02-13 17:13:17 +11:00
gingerBill d2e1ec13f0 Merge pull request #3192 from Lperlind/revert-the-matrices
Revert the matrices
2024-02-12 23:44:36 +00:00
Lucas Perlind 6df07a2c0a Remove bounds checking on a lot of linalg 2024-02-13 10:23:29 +11:00
Lucas Perlind c0d407a2b4 Revert "Change minor/adjoint to be row, col from c, r to be consistent with [row, col] syntax; Add #no_bounds_check were appropriate"
This reverts commit 5dd2e38aff.
2024-02-13 10:13:34 +11:00
gingerBill f378367fe7 Store cell data in map debug metadata 2024-02-12 15:17:06 +00:00
gingerBill c291fffce1 Add metadata type to maps debug information to aid debuggers knowing what is in it it 2024-02-12 12:29:37 +00:00
gingerBill 900fe95ba0 Split out linalg.identity between matrices and array-based matrix 2024-02-12 12:08:43 +00:00
gingerBill 5dd2e38aff Change minor/adjoint to be row, col from c, r to be consistent with [row, col] syntax; Add #no_bounds_check were appropriate 2024-02-12 11:54:24 +00:00
gingerBill 5ad8ebba9f Merge pull request #3188 from Tetralux/fixchdir
[os] Fix os.change_directory(), os.set_current_directory()
2024-02-12 11:24:45 +00:00
gingerBill e7719eed65 Merge pull request #3189 from mothfuzz/json_unmarshal_enumerated_enums_fix
remove return statement from case Type_Info_Enumerated_Array
2024-02-12 11:23:51 +00:00
Kay 5f49b8997a remove return statement from case Type_Info_Enumerated_Array, as it causes early exit in JSON unmarshalling. 2024-02-12 01:45:11 -06:00
Tetralux d6734c85b8 [os] Fix os.change_directory(), os.set_current_directory()
- set_current_directory() was leaking the wstring path string because it wasn't using the temp allocator.
- change_directory() wasn't calling SetCurrentDirectoryW() under the lock!
2024-02-12 06:03:51 +00:00
gingerBill 4ca23499fa Add raymath procedures (with numerous deprecated attributes to suggest to use array programming) 2024-02-10 16:31:03 +00:00
gingerBill e201a2fabb Add rand.choice_enum 2024-02-10 15:13:09 +00:00
gingerBill 5c4485f657 Add #load_directory(path: string) > []runtime.Load_Directory_File 2024-02-09 15:18:29 +00:00
gingerBill bae2a6fc1e Merge pull request #3184 from Lperlind/master
Add more window procedures
2024-02-09 11:59:12 +00:00
Lucas Perlind 8777fa1c04 Add more window procedures 2024-02-09 14:04:16 +11:00
gingerBill d771b3286d Merge pull request #3182 from laytan/fix-instrumentation-in-debug-mode
fix instrumentation in debug mode
2024-02-08 21:36:43 +00:00
Laytan 0e6dd56ac1 fix instrumentation in debug mode 2024-02-08 22:29:45 +01:00
gingerBill 9127e584c5 Merge pull request #3181 from laytan/fix-no-crt-on-linux
fix -no-crt on Linux
2024-02-08 21:07:42 +00:00
Laytan eab0e730a0 fix -no-crt on Linux 2024-02-08 19:48:37 +01:00
gingerBill 7128bc4b34 Add core:relative
This will eventually replace the #relative types
2024-02-08 15:15:16 +00:00
gingerBill 7b672ac72a Disallow mixture of polymorphic $ names and normal identifiers within record parameters 2024-02-08 14:03:03 +00:00
gingerBill 8f4ab3c07d Add check for a procedure 2024-02-08 13:49:27 +00:00
gingerBill dc5cfacc0a Fix edge case for procedures 2024-02-08 13:45:18 +00:00
gingerBill 3aea2e1fff Merge branch 'master' of https://github.com/odin-lang/Odin 2024-02-08 13:41:07 +00:00
gingerBill 59933b244d Allow polymorphic checking with intrinsics.type_is_subtype_of(Derived_Type, Poly_Type) 2024-02-08 13:41:02 +00:00
gingerBill 42aca72d9f Correct the logic for parapoly records to match that of procedures (polymorphic non-typeid parameters) 2024-02-08 13:21:41 +00:00
gingerBill 7034a31745 Merge pull request #3180 from z64/z64/fix
Remove invalid allocator assignment to slice
2024-02-08 12:39:49 +00:00
Zac Nowicki 9d0786ded7 Remove invalid allocator assignment to slice
`T` is `#soa[]$E`, which does not carry an allocator.
2024-02-08 04:14:16 -05:00
gingerBill 4c51706941 Merge pull request #3179 from laytan/fix-ci
fix ci
2024-02-07 19:09:04 +00:00
Laytan Laats a8c4f46747 fix ci 2024-02-07 19:53:52 +01:00
gingerBill 8e367d221b Merge branch 'master' of https://github.com/odin-lang/Odin 2024-02-07 17:16:05 +00:00
gingerBill a08250ac5b Improve error handling for missing library collection provided by the compiler 2024-02-07 17:15:59 +00:00
gingerBill 131c71ea76 Merge pull request #3142 from jay-j/feature/gl-allow-geometry-shaders
Expose OpenGL shader procs to allow creation of pipelines with geometry shaders
2024-02-07 16:09:58 +00:00
gingerBill 9db04fe446 Merge pull request #3156 from laytan/spall-instrumentation
Make spall able to be used in instrumentation procedures
2024-02-07 12:16:20 +00:00
gingerBill abaa906f34 Fix lb_hasher_proc_for_type 2024-02-07 11:05:38 +00:00
gingerBill fa093d9b09 Add runtime.map_insert_and_check_for_previous 2024-02-06 23:58:22 +00:00
gingerBill 4035a226da Fix filepath.rel allocation behaviour 2024-02-06 23:39:20 +00:00
gingerBill df5ee2dd06 Merge pull request #3178 from laytan/fix-virtual-static-reset-logic
mem/virtual: fix arena_static_reset_to inverted logic
2024-02-06 23:31:07 +00:00
Laytan 9b4cd0743c mem/virtual: fix arena_static_reset_to inverted logic 2024-02-06 20:24:38 +01:00
laytan 3f090ed523 fix for instrumenting on Windows 2024-02-06 19:55:09 +01:00
gingerBill 79173ef119 Merge pull request #3136 from Yawning/feature/crypto-hash
core:crypto/hash - Add and refactor
2024-02-06 17:40:45 +00:00
Yawning Angel 44758f2a60 core/crypto: Stop using context.temp_allocator
The max digest size for the foreseeable future will be 512 bits, and the
max block size is currently 1152 bits (SHA3-224).  If people add more
exotic hash algorithms without bumping the constants when required,
tests will fail.

The stream buffer will currently be 576 bytes, which is "fine" to just
stick on the stack, and is a sensible multiple of the more common block
size of 64 bytes.
2024-02-07 02:33:53 +09:00
gingerBill 1f0b24b735 Remove unneeded casts 2024-02-06 17:22:13 +00:00
gingerBill 327853ab92 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-02-06 17:20:13 +00:00
gingerBill a6878fcd91 Clean up code for initialize_symbols 2024-02-06 17:20:07 +00:00
gingerBill 61202b5abd Merge pull request #3174 from thetarnav/webgl2-matrix-flatten-2
Use `intrinsics.matrix_flatten` in `webgl2.odin`
2024-02-06 17:00:33 +00:00
Yawning Angel 56516ee8b2 core/crypto/poly1305: Relax the tag length check on the verify helper 2024-02-07 00:42:01 +09:00
Yawning Angel 1cc639bc93 core/crypto/hmac: Initial import 2024-02-07 00:42:01 +09:00
Yawning Angel 2357293e05 core/crypto/hash: Make the low level interface allocator-less
Just (ab)using reflect to victory is probably fine.
2024-02-07 00:37:18 +09:00
Yawning Angel 7a8b1669b0 core/crypto: Expose the block sizes for every hash algorithm
While I just went and made this private, this information is required
for keying HMAC.
2024-02-07 00:37:18 +09:00
Yawning Angel bc160d2eb7 tests/core/crypto: Reorganize
All of our crypto is modern now unless exiled to the legacy sub-package,
so move the test cases for the currently un-unified algorithms into the
main test driver file, and rename the benchmark driver to reflect
reality.
2024-02-07 00:37:18 +09:00
Yawning Angel b02b85d242 core/crypto/shake: SHAKE is an XOF, not a hash 2024-02-07 00:37:18 +09:00
Yawning Angel 1d151c4c92 tests/core/crypto: Cleanup/modernize a bit 2024-02-07 00:37:18 +09:00
Yawning Angel 899fab64d9 core/crypto: Documentation cleanups 2024-02-07 00:37:18 +09:00
Yawning Angel 00ab3beed9 core:crypto/hash: Add a generic higher level hash interface
There is a lot of code duplicated in convenience methods in each hash
implementation, and having a generic hash type makes implementing
higher-level constructs such as HMAC significantly easier down the road.
2024-02-07 00:37:18 +09:00
Yawning Angel ca10fc2d47 repo: Add yet another test binary to .gitignore 2024-02-07 00:37:18 +09:00
gingerBill 1ed6a484ac Merge pull request #3175 from Murtagy/patch-1
Update raylib.odin
2024-02-06 14:59:35 +00:00
gingerBill b0675358c3 Add angle_between and matrix2_rotate 2024-02-06 14:09:05 +00:00
Murtagy 456dd22dc4 Update raylib.odin 2024-02-05 21:38:17 +03:00
Laytan 02b8fefa30 indent doc.odin, makes it render nicely on the documentation site 2024-02-05 18:59:08 +01:00
Laytan 95808fd2e7 remove unneeded no instrumentation comment, are all foreign procs anyway 2024-02-05 18:54:37 +01:00
Laytan e63d71c23b implement bsd's, same as darwin 2024-02-05 18:48:17 +01:00
Damian Tarnawski 23a49ce65b USe intrinsics.matrix_flatten in webgl2.odin 2024-02-05 18:28:03 +01:00
Laytan Laats ca6300c860 spall instrumentation 2024-02-05 16:54:22 +01:00
gingerBill 1ebb7f8e9d Merge pull request #3170 from flysand7/run-args-fix
Make run args take the first '--' as the start of the args, instead of the last '--'
2024-02-05 13:58:32 +00:00
gingerBill 4685cf1085 Merge pull request #3140 from JustinRyanH/master
Implement `last_write_time` and `last_write_time_by_name` for Darwin
2024-02-05 13:57:28 +00:00
gingerBill 80a0b161b0 Force 128-bit calls on wasm 2024-02-05 13:51:16 +00:00
gingerBill 27feb5998c Add require to 128-bit and f16 casts 2024-02-05 13:49:10 +00:00
gingerBill e88db2818b force requiring the 128-bit calls on WASM targets 2024-02-05 13:48:08 +00:00
gingerBill 19535d8721 Add require flags to 128-bit integer procedures 2024-02-05 11:11:28 +00:00
flysand7 8ea8fbeccb Make run args take the first '--' as the start of the args, instead of the last '--' 2024-02-04 14:25:53 +11:00
Jeroen van Rijn 4e300ff90a Merge pull request #3167 from Murtagy/patch-1
Typo in comment in raylib
2024-02-03 17:04:10 +01:00
Murtagy 7bf25a4cf2 typo in comment 2024-02-03 18:43:34 +03:00
gingerBill 5f0f9f477e Merge pull request #3161 from laytan/fix-no-crt-darwin
fix -no-crt on Darwin
2024-02-03 12:30:00 +00:00
gingerBill dd7c65a89c Merge pull request #2998 from laytan/os-improvements
OS improvements
2024-02-03 12:29:39 +00:00
gingerBill 8620281191 Merge pull request #3163 from laytan/arm-releases-and-ci
Add MacOS ARM releases and CI
2024-02-03 12:25:49 +00:00
gingerBill c311a60aaf Merge pull request #3164 from FourteenBrush/master
Fix compiler panic: "src/parser.cpp(128): Panic: Unhandled Ast or branch expression"
2024-02-03 12:24:54 +00:00
gingerBill 2993490c75 Merge pull request #3165 from korvahkh/fix-dyn-array-init
Fix #3155
2024-02-03 12:24:40 +00:00
gingerBill 60b324d4ed Merge pull request #3166 from mothfuzz/patch-2
Change ClearMask to ColorMask in webgl.odin
2024-02-03 12:24:10 +00:00
Kay 89a2fdd106 Change ClearMask to ColorMask in webgl.odin
https://registry.khronos.org/OpenGL-Refpages/gl4/html/glColorMask.xhtml
2024-02-02 23:42:37 -06:00
korvahkh 8530829ca4 Fix dynamic array index = value with const value initializing to 0 2024-02-02 15:27:39 -06:00
FourteenBrush 0e5d7801dd Fix code style 2024-02-02 21:17:10 +01:00
Laytan Laats d5db49a3b0 name the workflows 2024-02-02 21:10:35 +01:00
Laytan Laats 606f11ebe8 fix hxa test use after free, skip weird pow test failure 2024-02-02 21:10:11 +01:00
Laytan Laats 07a4f4d017 add MacOS ARM target to CI and releases 2024-02-02 21:09:57 +01:00
FourteenBrush d463adfe4f Merge remote-tracking branch 'upstream/master' 2024-02-02 21:01:52 +01:00
FourteenBrush 16bd19ed43 Fix Unhandled Ast_OrBranchExpr 2024-02-02 21:00:00 +01:00
Laytan Laats eb811e8341 fix -no-crt on Darwin 2024-02-02 19:49:40 +01:00
Justin Hurstwright 8a63b6dff7 fix: remove returns ErrNo for Windows, FreeBSD, Unix 2024-02-02 09:10:25 -06:00
Justin Hurstwright 829654e3a2 steal the last_write from freebsd implementation 2024-02-02 09:10:25 -06:00
gingerBill cec08114fd Mock out comments 2024-02-02 14:57:22 +00:00
gingerBill bf90b61908 Fix type_elem_type for complex32 and quaternion64 2024-02-02 14:52:42 +00:00
gingerBill ae0a5b1a87 RUNTIME_REQUIRE :: false 2024-02-02 12:03:14 +00:00
gingerBill 55ff9b857e Add contextless where missing 2024-02-02 11:51:26 +00:00
gingerBill e7122a0950 Minimize extra dependencies if u128/i128 and f16 are not used 2024-02-02 11:42:22 +00:00
IllusionMan1212 301b9eef31 vendor/x11: added XQueryExtension, XGetEventData, and XFreeEventData 2024-01-29 06:22:33 +02:00
IllusionMan1212 36ed8fe55d vendor/x11: change display to be a ^Display in XGenericEventCookie struct 2024-01-29 05:54:14 +02:00
Patrick Cleavelin 7b9ea9eca0 Merge branch 'odin-lang:master' into master 2024-01-27 14:12:19 -06:00
Gabriel Pariat 89404147f6 fix whitespace 2024-01-27 14:10:32 -05:00
Gabriel Pariat 29d4bdc80b Revert "fix whitespace"
This reverts commit 594078cc1d.
2024-01-27 14:09:08 -05:00
Gabriel Pariat 594078cc1d fix whitespace 2024-01-27 14:08:14 -05:00
Gabriel Pariat ab68e4c6c4 change ordering 2024-01-27 14:06:58 -05:00
Gabriel Pariat 4956f9dad3 fix typo 2024-01-27 14:04:43 -05:00
Gabriel Pariat fd090c6672 adding linux + unix makefile for cgltf 2024-01-27 14:00:02 -05:00
jay-j b46b3010ec Expose OpenGL shader procedures to allow creation of shader pipelines with geometry shaders 2024-01-26 19:43:47 -08:00
Colin Davidson f93f2dfd5c Add support for basic EGL on Linux 2024-01-24 13:24:25 -08:00
Laytan Laats 9ac619f4a2 vendor/raylib: change to non-universal binaries
We already have 2 different folders for the different architectures, so
this does should not have any usage impact.

Main motivation for doing this is hombrew requiring non-universal
binaries in their installation, so this is a step to getting homebrew
and vendor to play nice.
2024-01-24 20:58:14 +01:00
Rikard Petré 239d4e1076 odin/tokenizer: Reset insert_semicolon to false in tokenizer.init to fix bug when tokenizing multiple files. 2024-01-20 16:09:41 +01:00
Rikard Petré 99825a28d7 odin/parser: Allow semicolon after return statement for the case:
if x do return y;
else do return z;
2024-01-20 16:00:41 +01:00
Rikard Petré 144504a752 odin/parser: Fix parsing of struct literal/call expression when closing brace/paren is on a new line without a comma after the last argument. 2024-01-20 15:57:14 +01:00
Patrick Cleavelin 59aa05170d respect -lld CLI arg 2024-01-04 13:55:36 -06:00
Laytan Laats 0b2f357bbe fix eof logic 2023-12-14 18:05:06 +01:00
Laytan Laats 9a1c4dc56d implement a max read/write at a time for non-windows (windows already has this) 2023-12-14 18:05:06 +01:00
Laytan Laats 7eabeda870 make sure an eof is also returned from the file streams on non-windows 2023-12-14 18:05:06 +01:00
383 changed files with 44027 additions and 29056 deletions
+52 -11
View File
@@ -3,11 +3,16 @@ on: [push, pull_request, workflow_dispatch]
jobs:
build_linux:
name: Ubuntu Build, Check, and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Download LLVM, botan
run: sudo apt-get install llvm-11 clang-11 libbotan-2-dev botan
- name: Download LLVM
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 17
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
- name: build odin
run: ./build_odin.sh release
- name: Odin version
@@ -46,6 +51,9 @@ jobs:
- name: Odin check examples/all for Linux i386
run: ./odin check examples/all -vet -strict-style -target:linux_i386
timeout-minutes: 10
- name: Odin check examples/all for Linux arm64
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
timeout-minutes: 10
- name: Odin check examples/all for FreeBSD amd64
run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64
timeout-minutes: 10
@@ -53,15 +61,14 @@ jobs:
run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
timeout-minutes: 10
build_macOS:
name: MacOS Build, Check, and Test
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Download LLVM, botan and setup PATH
- name: Download LLVM, and setup PATH
run: |
brew install llvm@13 botan
echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH
TMP_PATH=$(xcrun --show-sdk-path)/user/include
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
brew install llvm@17
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
- name: build odin
run: ./build_odin.sh release
- name: Odin version
@@ -92,13 +99,47 @@ jobs:
cd tests/internal
make
timeout-minutes: 10
- name: Odin check examples/all for Darwin arm64
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
build_macOS_arm:
name: MacOS ARM Build, Check, and Test
runs-on: macos-14 # This is an arm/m1 runner.
steps:
- uses: actions/checkout@v1
- name: Download LLVM and setup PATH
run: |
brew install llvm@17
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
- name: build odin
run: ./build_odin.sh release
- name: Odin version
run: ./odin version
timeout-minutes: 1
- name: Odin report
run: ./odin report
timeout-minutes: 1
- name: Odin check
run: ./odin check examples/demo -vet
timeout-minutes: 10
- name: Odin check examples/all for Linux arm64
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
- name: Odin run
run: ./odin run examples/demo
timeout-minutes: 10
- name: Odin run -debug
run: ./odin run examples/demo -debug
timeout-minutes: 10
- name: Odin check examples/all
run: ./odin check examples/all -strict-style
timeout-minutes: 10
- name: Core library tests
run: |
cd tests/core
make
timeout-minutes: 10
- name: Odin internals tests
run: |
cd tests/internal
make
timeout-minutes: 10
build_windows:
name: Windows Build, Check, and Test
runs-on: windows-2022
steps:
- uses: actions/checkout@v1
+67 -15
View File
@@ -7,6 +7,7 @@ on:
jobs:
build_windows:
name: Windows Build
if: github.repository == 'odin-lang/Odin'
runs-on: windows-2022
steps:
@@ -40,12 +41,17 @@ jobs:
name: windows_artifacts
path: dist
build_ubuntu:
name: Ubuntu Build
if: github.repository == 'odin-lang/Odin'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: (Linux) Download LLVM
run: sudo apt-get install llvm-11 clang-11
run: |
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 17
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
- name: build odin
run: make nightly
- name: Odin run
@@ -61,27 +67,28 @@ jobs:
cp -r core dist
cp -r vendor dist
cp -r examples dist
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
zip -r dist.zip dist
- name: Upload artifact
uses: actions/upload-artifact@v1
with:
name: ubuntu_artifacts
path: dist
path: dist.zip
build_macos:
name: MacOS Build
if: github.repository == 'odin-lang/Odin'
runs-on: macOS-latest
runs-on: macos-latest
steps:
- uses: actions/checkout@v1
- name: Download LLVM and setup PATH
run: |
brew install llvm@13
echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH
TMP_PATH=$(xcrun --show-sdk-path)/user/include
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
brew install llvm@17 dylibbundler
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
- name: build odin
run: make nightly
- name: Odin run
run: ./odin run examples/demo
- name: Copy artifacts
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
# not link with libunwind bundled with LLVM but link with libunwind on the system.
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
- name: Bundle
run: |
mkdir dist
cp odin dist
@@ -91,14 +98,53 @@ jobs:
cp -r core dist
cp -r vendor dist
cp -r examples dist
dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
zip -r dist.zip dist
- name: Odin run
run: ./dist/odin run examples/demo
- name: Upload artifact
uses: actions/upload-artifact@v1
with:
name: macos_artifacts
path: dist
path: dist.zip
build_macos_arm:
name: MacOS ARM Build
if: github.repository == 'odin-lang/Odin'
runs-on: macos-14 # ARM machine
steps:
- uses: actions/checkout@v1
- name: Download LLVM and setup PATH
run: |
brew install llvm@17 dylibbundler
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
- name: build odin
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
# not link with libunwind bundled with LLVM but link with libunwind on the system.
run: CXXFLAGS="-L/usr/lib/system -L/usr/lib" make nightly
- name: Bundle
run: |
mkdir dist
cp odin dist
cp LICENSE dist
cp -r shared dist
cp -r base dist
cp -r core dist
cp -r vendor dist
cp -r examples dist
dylibbundler -b -x dist/odin -d dist/libs -od -p @executable_path/libs
# Zipping so executable permissions are retained, see https://github.com/actions/upload-artifact/issues/38
zip -r dist.zip dist
- name: Odin run
run: ./dist/odin run examples/demo
- name: Upload artifact
uses: actions/upload-artifact@v1
with:
name: macos_arm_artifacts
path: dist.zip
upload_b2:
runs-on: [ubuntu-latest]
needs: [build_windows, build_macos, build_ubuntu]
needs: [build_windows, build_macos, build_macos_arm, build_ubuntu]
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v2
@@ -129,6 +175,11 @@ jobs:
with:
name: macos_artifacts
- name: Download macOS arm artifacts
uses: actions/download-artifact@v1
with:
name: macos_arm_artifacts
- name: Create archives and upload
shell: bash
env:
@@ -143,8 +194,9 @@ jobs:
echo Uploading artifcates to B2
chmod +x ./ci/upload_create_nightly.sh
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/dist.zip
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/dist.zip
./ci/upload_create_nightly.sh "$BUCKET" macos-arm64 macos_arm_artifacts/dist.zip
echo Deleting old artifacts in B2
python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
+3 -1
View File
@@ -28,6 +28,7 @@ tests/internal/test_map
tests/internal/test_pow
tests/internal/test_rtti
tests/core/test_core_compress
tests/core/test_core_container
tests/core/test_core_filepath
tests/core/test_core_fmt
tests/core/test_core_i18n
@@ -39,7 +40,7 @@ tests/core/test_core_net
tests/core/test_core_os_exit
tests/core/test_core_reflect
tests/core/test_core_strings
tests/core/test_crypto_hash
tests/core/test_crypto
tests/core/test_hash
tests/core/test_hxa
tests/core/test_json
@@ -49,6 +50,7 @@ tests/core/test_varint
tests/core/test_xml
tests/core/test_core_slice
tests/core/test_core_thread
tests/core/test_core_runtime
tests/vendor/vendor_botan
# Visual Studio 2015 cache/options directory
.vs/
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2016-2022 Ginger Bill. All rights reserved.
Copyright (c) 2016-2024 Ginger Bill. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
+5 -3
View File
@@ -38,9 +38,9 @@ count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_sim
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) ---
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) ---
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) ---
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
@@ -293,7 +293,9 @@ wasm_memory_size :: proc(index: uintptr) -> int ---
// 0 - indicates that the thread blocked and then was woken up
// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
// 2 - the thread blocked, but the timeout
@(enable_target_feature="atomics")
wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
@(enable_target_feature="atomics")
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
// x86 Targets (i386, amd64)
+27 -4
View File
@@ -177,10 +177,22 @@ Type_Info_Matrix :: struct {
row_count: int,
column_count: int,
// Total element count = column_count * elem_stride
layout: enum u8 {
Column_Major, // array of column vectors
Row_Major, // array of row vectors
},
}
Type_Info_Soa_Pointer :: struct {
elem: ^Type_Info,
}
Type_Info_Bit_Field :: struct {
backing_type: ^Type_Info,
names: []string,
types: []^Type_Info,
bit_sizes: []uintptr,
bit_offsets: []uintptr,
tags: []string,
}
Type_Info_Flag :: enum u8 {
Comparable = 0,
@@ -223,6 +235,7 @@ Type_Info :: struct {
Type_Info_Relative_Multi_Pointer,
Type_Info_Matrix,
Type_Info_Soa_Pointer,
Type_Info_Bit_Field,
},
}
@@ -256,6 +269,7 @@ Typeid_Kind :: enum u8 {
Relative_Multi_Pointer,
Matrix,
Soa_Pointer,
Bit_Field,
}
#assert(len(Typeid_Kind) < 32)
@@ -270,7 +284,7 @@ Typeid_Kind :: enum u8 {
// NOTE(bill): only the ones that are needed (not all types)
// This will be set by the compiler
type_table: []Type_Info
type_table: []^Type_Info
args__: []cstring
@@ -296,6 +310,14 @@ Source_Code_Location :: struct {
procedure: string,
}
/*
Used by the built-in directory `#load_directory(path: string) -> []Load_Directory_File`
*/
Load_Directory_File :: struct {
name: string,
data: []byte, // immutable data
}
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location) -> !
// Allocation Stuff
@@ -575,8 +597,9 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
base := info
loop: for {
#partial switch i in base.variant {
case Type_Info_Named: base = i.base
case Type_Info_Enum: base = i.base
case Type_Info_Named: base = i.base
case Type_Info_Enum: base = i.base
case Type_Info_Bit_Field: base = i.backing_type
case: break loop
}
}
@@ -591,7 +614,7 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
if n < 0 || n >= len(type_table) {
n = 0
}
return &type_table[n]
return type_table[n]
}
when !ODIN_NO_RTTI {
+28 -9
View File
@@ -122,7 +122,7 @@ pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bou
// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
// If the operation is not possible, it will return false.
@builtin
pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
pop_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
if len(array) == 0 {
return
}
@@ -148,7 +148,7 @@ pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #
// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
// If the operation is not possible, it will return false.
@builtin
pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
pop_front_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
if len(array) == 0 {
return
}
@@ -447,12 +447,12 @@ _append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero:
}
@builtin
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_elem(array, arg, true, loc=loc)
}
@builtin
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_elem(array, arg, false, loc=loc)
}
@@ -496,12 +496,12 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l
}
@builtin
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_elems(array, true, loc, ..args)
}
@builtin
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_elems(array, false, loc, ..args)
}
@@ -556,7 +556,7 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
@builtin
inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
if array == nil {
return
}
@@ -574,7 +574,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
}
@builtin
inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
if array == nil {
return
}
@@ -740,6 +740,9 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int,
a := (^Raw_Dynamic_Array)(array)
if length <= a.cap {
if should_zero && a.len < length {
intrinsics.mem_zero(([^]E)(a.data)[a.len:], (length-a.len)*size_of(E))
}
a.len = max(length, 0)
return nil
}
@@ -824,9 +827,25 @@ map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location)
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
}
// Explicitly inserts a key and value into a map `m`, the same as `map_insert`, but the return values differ.
// - `prev_key` will return the previous pointer of a key if it exists, check `found_previous` if was previously found
// - `value_ptr` will return the pointer of the memory where the insertion happens, and `nil` if the map failed to resize
// - `found_previous` will be true a previous key was found
@(builtin, require_results)
map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (prev_key: K, value_ptr: ^V, found_previous: bool) {
key, value := key, value
kp, vp := __dynamic_map_set_extra_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc)
if kp != nil {
prev_key = (^K)(kp)^
found_previous = true
}
value_ptr = (^V)(vp)
return
}
@builtin
card :: proc(s: $S/bit_set[$E; $U]) -> int {
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
when size_of(S) == 1 {
return int(intrinsics.count_ones(transmute(u8)s))
} else when size_of(S) == 2 {
+94 -2
View File
@@ -86,7 +86,6 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
return
}
array.allocator = allocator
footer := raw_soa_footer(&array)
if size_of(E) == 0 {
footer.len = length
@@ -426,4 +425,97 @@ clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
@builtin
clear_soa :: proc{
clear_soa_dynamic_array,
}
}
// Converts soa slice into a soa dynamic array without cloning or allocating memory
@(require_results)
into_dynamic_soa :: proc(array: $T/#soa[]$E) -> #soa[dynamic]E {
d: #soa[dynamic]E
footer := raw_soa_footer_dynamic_array(&d)
footer^ = {
cap = len(array),
len = 0,
allocator = nil_allocator(),
}
field_count: uintptr
when intrinsics.type_is_array(E) {
field_count = len(E)
} else {
field_count = uintptr(intrinsics.type_struct_field_count(E))
}
array := array
dynamic_data := ([^]rawptr)(&d)[:field_count]
slice_data := ([^]rawptr)(&array)[:field_count]
copy(dynamic_data, slice_data)
return d
}
// `unordered_remove_soa` removed the element at the specified `index`. It does so by replacing the current end value
// with the old value, and reducing the length of the dynamic array by 1.
//
// Note: This is an O(1) operation.
// Note: If you the elements to remain in their order, use `ordered_remove_soa`.
// Note: If the index is out of bounds, this procedure will panic.
@builtin
unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #caller_location) #no_bounds_check {
bounds_check_error_loc(loc, index, len(array))
if index+1 < len(array) {
ti := type_info_of(typeid_of(T))
ti = type_info_base(ti)
si := &ti.variant.(Type_Info_Struct)
field_count: uintptr
when intrinsics.type_is_array(E) {
field_count = len(E)
} else {
field_count = uintptr(intrinsics.type_struct_field_count(E))
}
data := uintptr(array)
for i in 0..<field_count {
type := si.types[i].variant.(Type_Info_Pointer).elem
offset := rawptr((^uintptr)(data)^ + uintptr(index*type.size))
final := rawptr((^uintptr)(data)^ + uintptr((len(array)-1)*type.size))
mem_copy(offset, final, type.size)
data += size_of(rawptr)
}
}
raw_soa_footer_dynamic_array(array).len -= 1
}
// `ordered_remove_soa` removed the element at the specified `index` whilst keeping the order of the other elements.
//
// Note: This is an O(N) operation.
// Note: If you the elements do not have to remain in their order, prefer `unordered_remove_soa`.
// Note: If the index is out of bounds, this procedure will panic.
@builtin
ordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #caller_location) #no_bounds_check {
bounds_check_error_loc(loc, index, len(array))
if index+1 < len(array) {
ti := type_info_of(typeid_of(T))
ti = type_info_base(ti)
si := &ti.variant.(Type_Info_Struct)
field_count: uintptr
when intrinsics.type_is_array(E) {
field_count = len(E)
} else {
field_count = uintptr(intrinsics.type_struct_field_count(E))
}
data := uintptr(array)
for i in 0..<field_count {
type := si.types[i].variant.(Type_Info_Pointer).elem
offset := (^uintptr)(data)^ + uintptr(index*type.size)
length := type.size*(len(array) - index - 1)
mem_copy(rawptr(offset), rawptr(offset + uintptr(type.size)), length)
data += size_of(rawptr)
}
}
raw_soa_footer_dynamic_array(array).len -= 1
}
+3 -2
View File
@@ -44,7 +44,7 @@ memcpy
memove
## Procedures required by the LLVM backend
## Procedures required by the LLVM backend if u128/i128 is used
umodti3
udivti3
modti3
@@ -59,11 +59,12 @@ truncdfhf2
gnu_h2f_ieee
gnu_f2h_ieee
extendhfsf2
## Procedures required by the LLVM backend if f16 is used
__ashlti3 // wasm specific
__multi3 // wasm specific
## Required an entry point is defined (i.e. 'main')
args__
+70 -3
View File
@@ -333,7 +333,7 @@ map_kvh_data_values_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^
}
@(private, require_results)
@(require_results)
map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr, info: ^Map_Info) -> uintptr {
round :: #force_inline proc "contextless" (value: uintptr) -> uintptr {
CACHE_MASK :: MAP_CACHE_LINE_SIZE - 1
@@ -350,6 +350,12 @@ map_total_allocation_size :: #force_inline proc "contextless" (capacity: uintptr
return size
}
@(require_results)
map_total_allocation_size_from_value :: #force_inline proc "contextless" (m: $M/map[$K]$V) -> uintptr {
return map_total_allocation_size(uintptr(cap(m)), map_info(M))
}
// The only procedure which needs access to the context is the one which allocates the map.
@(require_results)
map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, allocator := context.allocator, loc := #caller_location) -> (result: Raw_Map, err: Allocator_Error) {
@@ -391,7 +397,8 @@ map_alloc_dynamic :: proc "odin" (info: ^Map_Info, log2_capacity: uintptr, alloc
// arrays to reduce variance. This swapping can only be done with memcpy since
// there is no type information.
//
// This procedure returns the address of the just inserted value.
// This procedure returns the address of the just inserted value, and will
// return 'nil' if there was no room to insert the entry
@(require_results)
map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, ik: uintptr, iv: uintptr) -> (result: uintptr) {
h := h
@@ -415,6 +422,11 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
tv := map_cell_index_dynamic(sv, info.vs, 1)
swap_loop: for {
if distance > mask {
// Failed to find an empty slot and prevent infinite loop
panic("unable to insert into a map")
}
element_hash := hs[pos]
if map_hash_is_empty(element_hash) {
@@ -841,6 +853,33 @@ __dynamic_map_get :: proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info:
}
}
__dynamic_map_get_key_and_value :: proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, h: Map_Hash, key: rawptr) -> (key_ptr, value_ptr: rawptr) {
if m.len == 0 {
return nil, nil
}
pos := map_desired_position(m^, h)
distance := uintptr(0)
mask := (uintptr(1) << map_log2_cap(m^)) - 1
ks, vs, hs, _, _ := map_kvh_data_dynamic(m^, info)
for {
element_hash := hs[pos]
if map_hash_is_empty(element_hash) {
return nil, nil
} else if distance > map_probe_distance(m^, element_hash, pos) {
return nil, nil
} else if element_hash == h {
other_key := rawptr(map_cell_index_dynamic(ks, info.ks, pos))
if info.key_equal(key, other_key) {
key_ptr = other_key
value_ptr = rawptr(map_cell_index_dynamic(vs, info.vs, pos))
return
}
}
pos = (pos + 1) & mask
distance += 1
}
}
// IMPORTANT: USED WITHIN THE COMPILER
__dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (err: Allocator_Error, has_grown: bool) {
if m.len >= map_resize_threshold(m^) {
@@ -871,9 +910,37 @@ __dynamic_map_set :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_In
}
result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
m.len += 1
if result != 0 {
m.len += 1
}
return rawptr(result)
}
__dynamic_map_set_extra_without_hash :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key, value: rawptr, loc := #caller_location) -> (prev_key_ptr, value_ptr: rawptr) {
return __dynamic_map_set_extra(m, info, info.key_hasher(key, map_seed(m^)), key, value, loc)
}
__dynamic_map_set_extra :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, hash: Map_Hash, key, value: rawptr, loc := #caller_location) -> (prev_key_ptr, value_ptr: rawptr) {
if prev_key_ptr, value_ptr = __dynamic_map_get_key_and_value(m, info, hash, key); value_ptr != nil {
intrinsics.mem_copy_non_overlapping(value_ptr, value, info.vs.size_of_type)
return
}
hash := hash
err, has_grown := __dynamic_map_check_grow(m, info, loc)
if err != nil {
return nil, nil
}
if has_grown {
hash = info.key_hasher(key, map_seed(m^))
}
result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
if result != 0 {
m.len += 1
}
return nil, rawptr(result)
}
// IMPORTANT: USED WITHIN THE COMPILER
@(private)
+1 -1
View File
@@ -1,5 +1,5 @@
//+private
//+build linux, darwin, freebsd, openbsd
//+build linux, darwin, freebsd, openbsd, haiku
//+no-instrumentation
package runtime
+2 -2
View File
@@ -1,4 +1,4 @@
//+build linux, darwin, freebsd, openbsd
//+build linux, darwin, freebsd, openbsd, haiku
//+private
package runtime
@@ -35,4 +35,4 @@ _heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
_heap_free :: proc(ptr: rawptr) {
_unix_free(ptr)
}
}
+30 -6
View File
@@ -11,7 +11,7 @@ RUNTIME_LINKAGE :: "strong" when (
ODIN_BUILD_MODE == .Dynamic ||
!ODIN_NO_CRT) &&
!IS_WASM) else "internal"
RUNTIME_REQUIRE :: !ODIN_TILDE
RUNTIME_REQUIRE :: false // !ODIN_TILDE
@(private)
__float16 :: f16 when __ODIN_LLVM_F16_SUPPORTED else u16
@@ -22,7 +22,7 @@ byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byt
return ([^]byte)(data)[:max(len, 0)]
}
is_power_of_two_int :: #force_inline proc(x: int) -> bool {
is_power_of_two_int :: #force_inline proc "contextless" (x: int) -> bool {
if x <= 0 {
return false
}
@@ -40,7 +40,7 @@ align_forward_int :: #force_inline proc(ptr, align: int) -> int {
return p
}
is_power_of_two_uintptr :: #force_inline proc(x: uintptr) -> bool {
is_power_of_two_uintptr :: #force_inline proc "contextless" (x: uintptr) -> bool {
if x <= 0 {
return false
}
@@ -962,9 +962,11 @@ udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
return udivmod128(a, b, rem)
}
@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
udivti3 :: proc "c" (a, b: u128) -> u128 {
return udivmodti4(a, b, nil)
when !IS_WASM {
@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
udivti3 :: proc "c" (a, b: u128) -> u128 {
return udivmodti4(a, b, nil)
}
}
@@ -1034,3 +1036,25 @@ fixdfti :: proc(a: u64) -> i128 {
}
}
__write_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
for i in 0..<size {
j := offset+i
the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
b := the_bit<<(j&7)
dst[j/8] &~= b
dst[j/8] |= b
}
}
__read_bits :: proc "contextless" (dst, src: [^]byte, offset: uintptr, size: uintptr) {
for j in 0..<size {
i := offset+j
the_bit := byte((src[i/8]) & (1<<(i&7)) != 0)
b := the_bit<<(j&7)
dst[j/8] &~= b
dst[j/8] |= b
}
}
+6 -17
View File
@@ -2,25 +2,14 @@
//+private
package runtime
foreign import libc "system:System.framework"
@(default_calling_convention="c")
foreign libc {
@(link_name="__stderrp")
_stderr: rawptr
@(link_name="fwrite")
_fwrite :: proc(ptr: rawptr, size: uint, nmemb: uint, stream: rawptr) -> uint ---
@(link_name="__error")
_get_errno :: proc() -> ^i32 ---
}
import "base:intrinsics"
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
ret := _fwrite(raw_data(data), 1, len(data), _stderr)
if ret < len(data) {
err := _get_errno()
return int(ret), _OS_Errno(err^ if err != nil else 0)
WRITE :: 0x2000004
STDERR :: 2
ret := intrinsics.syscall(WRITE, STDERR, uintptr(raw_data(data)), uintptr(len(data)))
if ret < 0 {
return 0, _OS_Errno(-ret)
}
return int(ret), 0
}
+21
View File
@@ -0,0 +1,21 @@
//+build haiku
//+private
package runtime
foreign import libc "system:c"
foreign libc {
@(link_name="write")
_unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
_errnop :: proc() -> ^i32 ---
}
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
ret := _unix_write(2, raw_data(data), len(data))
if ret < len(data) {
err := _errnop()
return int(ret), _OS_Errno(err^ if err != nil else 0)
}
return int(ret), 0
}
+14
View File
@@ -459,6 +459,20 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
}
print_byte(']')
case Type_Info_Bit_Field:
print_string("bit_field ")
print_type(info.backing_type)
print_string(" {")
for name, i in info.names {
if i > 0 { print_string(", ") }
print_string(name)
print_string(": ")
print_type(info.types[i])
print_string(" | ")
print_u64(u64(info.bit_sizes[i]))
}
print_byte('}')
case Type_Info_Simd_Vector:
print_string("#simd[")
+22 -8
View File
@@ -7,19 +7,25 @@ ti_int :: struct #raw_union {
all: i128,
}
@(private="file")
ti_uint :: struct #raw_union {
using s: struct { lo, hi: u64 },
all: u128,
}
@(link_name="__ashlti3", linkage="strong")
__ashlti3 :: proc "contextless" (a: i128, b_: u32) -> i128 {
__ashlti3 :: proc "contextless" (la, ha: u64, b_: u32) -> i128 {
bits_in_dword :: size_of(u32)*8
b := u32(b_)
input, result: ti_int
input.all = a
input.lo, input.hi = la, ha
if b & bits_in_dword != 0 {
result.lo = 0
result.hi = input.lo << (b-bits_in_dword)
} else {
if b == 0 {
return a
return input.all
}
result.lo = input.lo<<b
result.hi = (input.hi<<b) | (input.lo>>(bits_in_dword-b))
@@ -29,12 +35,20 @@ __ashlti3 :: proc "contextless" (a: i128, b_: u32) -> i128 {
@(link_name="__multi3", linkage="strong")
__multi3 :: proc "contextless" (a, b: i128) -> i128 {
__multi3 :: proc "contextless" (la, ha, lb, hb: u64) -> i128 {
x, y, r: ti_int
x.all = a
y.all = b
x.lo, x.hi = la, ha
y.lo, y.hi = lb, hb
r.all = i128(x.lo * y.lo) // TODO this is incorrect
r.hi += x.hi*y.lo + x.lo*y.hi
return r.all
}
}
@(link_name="__udivti3", linkage="strong")
udivti3 :: proc "c" (la, ha, lb, hb: u64) -> u128 {
a, b: ti_uint
a.lo, a.hi = la, ha
b.lo, b.hi = lb, hb
return udivmodti4(a.all, b.all, nil)
}
+8 -4
View File
@@ -56,15 +56,14 @@ fi
case "$OS_NAME" in
Darwin)
if [ "$OS_ARCH" == "arm64" ]; then
if [ "$OS_ARCH" = "arm64" ]; then
if [ $LLVM_VERSION_MAJOR -lt 13 ] || [ $LLVM_VERSION_MAJOR -gt 17 ]; then
error "Darwin Arm64 requires LLVM 13, 14 or 17"
fi
fi
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS -liconv -ldl -framework System"
LDFLAGS="$LDFLAGS -lLLVM-C"
LDFLAGS="$LDFLAGS -liconv -ldl -framework System -lLLVM"
;;
FreeBSD)
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
@@ -83,6 +82,11 @@ OpenBSD)
LDFLAGS="$LDFLAGS -liconv"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
;;
Haiku)
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags) -I/system/develop/headers/private/shared -I/system/develop/headers/private/kernel"
LDFLAGS="$LDFLAGS -liconv"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
;;
*)
error "Platform \"$OS_NAME\" unsupported"
;;
@@ -97,7 +101,7 @@ build_odin() {
EXTRAFLAGS="-O3"
;;
release-native)
if [ "$OS_ARCH" == "arm64" ]; then
if [ "$OS_ARCH" = "arm64" ]; then
# Use preferred flag for Arm (ie arm64 / aarch64 / etc)
EXTRAFLAGS="-O3 -mcpu=native"
else
Regular → Executable
+14 -2
View File
@@ -1,5 +1,7 @@
#!/bin/bash
set -e
bucket=$1
platform=$2
artifact=$3
@@ -9,5 +11,15 @@ filename="odin-$platform-nightly+$now.zip"
echo "Creating archive $filename from $artifact and uploading to $bucket"
7z a -bd "output/$filename" -r "$artifact"
b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"
# If this is already zipped up (done before artifact upload to keep permissions in tact), just move it.
if [ "${artifact: -4}" == ".zip" ]
then
echo "Artifact already a zip"
mkdir -p "output"
mv "$artifact" "output/$filename"
else
echo "Artifact needs to be zipped"
7z a -bd "output/$filename" -r "$artifact"
fi
b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"
-1
View File
@@ -226,7 +226,6 @@ writer_to_writer :: proc(b: ^Writer) -> (s: io.Writer) {
@(private)
_writer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
b := (^Writer)(stream_data)
#partial switch mode {
+10
View File
@@ -104,3 +104,13 @@ NULL :: rawptr(uintptr(0))
NDEBUG :: !ODIN_DEBUG
CHAR_BIT :: 8
// Since there are no types in C with an alignment larger than that of
// max_align_t, which cannot be larger than sizeof(long double) as any other
// exposed type wouldn't be valid C, the maximum alignment possible in a
// strictly conformant C implementation is 16 on the platforms we care about.
// The choice of 4096 bytes for storage of this type is more than enough on all
// relevant platforms.
va_list :: struct #align(16) {
_: [4096]u8,
}
+18
View File
@@ -80,6 +80,24 @@ when ODIN_OS == .Darwin {
ERANGE :: 34
}
when ODIN_OS == .Haiku {
@(private="file")
@(default_calling_convention="c")
foreign libc {
@(link_name="_errnop")
_get_errno :: proc() -> ^int ---
}
@(private="file")
B_GENERAL_ERROR_BASE :: min(i32)
@(private="file")
B_POSIX_ERROR_BASE :: B_GENERAL_ERROR_BASE + 0x7000
EDOM :: B_POSIX_ERROR_BASE + 16
EILSEQ :: B_POSIX_ERROR_BASE + 38
ERANGE :: B_POSIX_ERROR_BASE + 17
}
// Odin has no way to make an identifier "errno" behave as a function call to
// read the value, or to produce an lvalue such that you can assign a different
// error value to errno. To work around this, just expose it as a function like
+3 -9
View File
@@ -4,6 +4,8 @@ package libc
import "base:intrinsics"
import "core:c"
@(private="file")
@(default_calling_convention="none")
foreign _ {
@@ -12,15 +14,7 @@ foreign _ {
@(link_name="llvm.va_copy") _va_copy :: proc(dst, src: ^i8) ---
}
// Since there are no types in C with an alignment larger than that of
// max_align_t, which cannot be larger than sizeof(long double) as any other
// exposed type wouldn't be valid C, the maximum alignment possible in a
// strictly conformant C implementation is 16 on the platforms we care about.
// The choice of 4096 bytes for storage of this type is more than enough on all
// relevant platforms.
va_list :: struct #align(16) {
_: [4096]u8,
}
va_list :: c.va_list
va_start :: #force_inline proc(ap: ^va_list, _: any) {
_va_start(cast(^i8)ap)
+30
View File
@@ -163,6 +163,36 @@ when ODIN_OS == .Darwin {
}
}
when ODIN_OS == .Haiku {
fpos_t :: distinct i64
_IOFBF :: 0
_IOLBF :: 1
_IONBF :: 2
BUFSIZ :: 8192
EOF :: int(-1)
FOPEN_MAX :: 128
FILENAME_MAX :: 256
L_tmpnam :: 512
SEEK_SET :: 0
SEEK_CUR :: 1
SEEK_END :: 2
TMP_MAX :: 32768
foreign libc {
stderr: ^FILE
stdin: ^FILE
stdout: ^FILE
}
}
@(default_calling_convention="c")
foreign libc {
// 7.21.4 Operations on files
+1 -1
View File
@@ -45,7 +45,7 @@ when ODIN_OS == .Windows {
}
}
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku {
@(default_calling_convention="c")
foreign libc {
// 7.27.2 Time manipulation functions
+5 -1
View File
@@ -29,7 +29,11 @@ when ODIN_OS == .Windows {
} else when ODIN_OS == .FreeBSD {
wctrans_t :: distinct int
wctype_t :: distinct ulong
} else when ODIN_OS == .Haiku {
wctrans_t :: distinct i32
wctype_t :: distinct i32
}
@(default_calling_convention="c")
+678
View File
@@ -0,0 +1,678 @@
/*
package avl implements an AVL tree.
The implementation is non-intrusive, and non-recursive.
*/
package container_avl
import "base:intrinsics"
import "base:runtime"
import "core:slice"
_ :: intrinsics
_ :: runtime
// Originally based on the CC0 implementation by Eric Biggers
// See: https://github.com/ebiggers/avl_tree/
// Direction specifies the traversal direction for a tree iterator.
Direction :: enum i8 {
// Backward is the in-order backwards direction.
Backward = -1,
// Forward is the in-order forwards direction.
Forward = 1,
}
// Ordering specifies order when inserting/finding values into the tree.
Ordering :: slice.Ordering
// Tree is an AVL tree.
Tree :: struct($Value: typeid) {
// user_data is a parameter that will be passed to the on_remove
// callback.
user_data: rawptr,
// on_remove is an optional callback that can be called immediately
// after a node is removed from the tree.
on_remove: proc(value: Value, user_data: rawptr),
_root: ^Node(Value),
_node_allocator: runtime.Allocator,
_cmp_fn: proc(a, b: Value) -> Ordering,
_size: int,
}
// Node is an AVL tree node.
//
// WARNING: It is unsafe to mutate value if the node is part of a tree
// if doing so will alter the Node's sort position relative to other
// elements in the tree.
Node :: struct($Value: typeid) {
value: Value,
_parent: ^Node(Value),
_left: ^Node(Value),
_right: ^Node(Value),
_balance: i8,
}
// Iterator is a tree iterator.
//
// WARNING: It is unsafe to modify the tree while iterating, except via
// the iterator_remove method.
Iterator :: struct($Value: typeid) {
_tree: ^Tree(Value),
_cur: ^Node(Value),
_next: ^Node(Value),
_direction: Direction,
_called_next: bool,
}
// init initializes a tree.
init :: proc {
init_ordered,
init_cmp,
}
// init_cmp initializes a tree.
init_cmp :: proc(
t: ^$T/Tree($Value),
cmp_fn: proc(a, b: Value) -> Ordering,
node_allocator := context.allocator,
) {
t._root = nil
t._node_allocator = node_allocator
t._cmp_fn = cmp_fn
t._size = 0
}
// init_ordered initializes a tree containing ordered items, with
// a comparison function that results in an ascending order sort.
init_ordered :: proc(
t: ^$T/Tree($Value),
node_allocator := context.allocator,
) where intrinsics.type_is_ordered_numeric(Value) {
init_cmp(t, slice.cmp_proc(Value), node_allocator)
}
// destroy de-initializes a tree.
destroy :: proc(t: ^$T/Tree($Value), call_on_remove: bool = true) {
iter := iterator(t, Direction.Forward)
for _ in iterator_next(&iter) {
iterator_remove(&iter, call_on_remove)
}
}
// len returns the number of elements in the tree.
len :: proc "contextless" (t: ^$T/Tree($Value)) -> int {
return t._size
}
// first returns the first node in the tree (in-order) or nil iff
// the tree is empty.
first :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
return tree_first_or_last_in_order(t, Direction.Backward)
}
// last returns the last element in the tree (in-order) or nil iff
// the tree is empty.
last :: proc "contextless" (t: ^$T/Tree($Value)) -> ^Node(Value) {
return tree_first_or_last_in_order(t, Direction.Forward)
}
// find finds the value in the tree, and returns the corresponding
// node or nil iff the value is not present.
find :: proc(t: ^$T/Tree($Value), value: Value) -> ^Node(Value) {
cur := t._root
descend_loop: for cur != nil {
switch t._cmp_fn(value, cur.value) {
case .Less:
cur = cur._left
case .Greater:
cur = cur._right
case .Equal:
break descend_loop
}
}
return cur
}
// find_or_insert attempts to insert the value into the tree, and returns
// the node, a boolean indicating if the value was inserted, and the
// node allocator error if relevant. If the value is already
// present, the existing node is returned un-altered.
find_or_insert :: proc(
t: ^$T/Tree($Value),
value: Value,
) -> (
n: ^Node(Value),
inserted: bool,
err: runtime.Allocator_Error,
) {
n_ptr := &t._root
for n_ptr^ != nil {
n = n_ptr^
switch t._cmp_fn(value, n.value) {
case .Less:
n_ptr = &n._left
case .Greater:
n_ptr = &n._right
case .Equal:
return
}
}
parent := n
n = new(Node(Value), t._node_allocator) or_return
n.value = value
n._parent = parent
n_ptr^ = n
tree_rebalance_after_insert(t, n)
t._size += 1
inserted = true
return
}
// remove removes a node or value from the tree, and returns true iff the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove :: proc {
remove_value,
remove_node,
}
// remove_value removes a value from the tree, and returns true iff the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_value :: proc(t: ^$T/Tree($Value), value: Value, call_on_remove: bool = true) -> bool {
n := find(t, value)
if n == nil {
return false
}
return remove_node(t, n, call_on_remove)
}
// remove_node removes a node from the tree, and returns true iff the
// removal was successful. While the node's value will be left intact,
// the node itself will be freed via the tree's node allocator.
remove_node :: proc(t: ^$T/Tree($Value), node: ^Node(Value), call_on_remove: bool = true) -> bool {
if node._parent == node || (node._parent == nil && t._root != node) {
return false
}
defer {
if call_on_remove && t.on_remove != nil {
t.on_remove(node.value, t.user_data)
}
free(node, t._node_allocator)
}
parent: ^Node(Value)
left_deleted: bool
t._size -= 1
if node._left != nil && node._right != nil {
parent, left_deleted = tree_swap_with_successor(t, node)
} else {
child := node._left
if child == nil {
child = node._right
}
parent = node._parent
if parent != nil {
if node == parent._left {
parent._left = child
left_deleted = true
} else {
parent._right = child
left_deleted = false
}
if child != nil {
child._parent = parent
}
} else {
if child != nil {
child._parent = parent
}
t._root = child
node_reset(node)
return true
}
}
for {
if left_deleted {
parent = tree_handle_subtree_shrink(t, parent, +1, &left_deleted)
} else {
parent = tree_handle_subtree_shrink(t, parent, -1, &left_deleted)
}
if parent == nil {
break
}
}
node_reset(node)
return true
}
// iterator returns a tree iterator in the specified direction.
iterator :: proc "contextless" (t: ^$T/Tree($Value), direction: Direction) -> Iterator(Value) {
it: Iterator(Value)
it._tree = transmute(^Tree(Value))t
it._direction = direction
iterator_first(&it)
return it
}
// iterator_from_pos returns a tree iterator in the specified direction,
// spanning the range [pos, last] (inclusive).
iterator_from_pos :: proc "contextless" (
t: ^$T/Tree($Value),
pos: ^Node(Value),
direction: Direction,
) -> Iterator(Value) {
it: Iterator(Value)
it._tree = transmute(^Tree(Value))t
it._direction = direction
it._next = nil
it._called_next = false
if it._cur = pos; pos != nil {
it._next = node_next_or_prev_in_order(it._cur, it._direction)
}
return it
}
// iterator_get returns the node currently pointed to by the iterator,
// or nil iff the node has been removed, the tree is empty, or the end
// of the tree has been reached.
iterator_get :: proc "contextless" (it: ^$I/Iterator($Value)) -> ^Node(Value) {
return it._cur
}
// iterator_remove removes the node currently pointed to by the iterator,
// and returns true iff the removal was successful. Semantics are the
// same as the Tree remove.
iterator_remove :: proc(it: ^$I/Iterator($Value), call_on_remove: bool = true) -> bool {
if it._cur == nil {
return false
}
ok := remove_node(it._tree, it._cur, call_on_remove)
if ok {
it._cur = nil
}
return ok
}
// iterator_next advances the iterator and returns the (node, true) or
// or (nil, false) iff the end of the tree has been reached.
//
// Note: The first call to iterator_next will return the first node instead
// of advancing the iterator.
iterator_next :: proc "contextless" (it: ^$I/Iterator($Value)) -> (^Node(Value), bool) {
// This check is needed so that the first element gets returned from
// a brand-new iterator, and so that the somewhat contrived case where
// iterator_remove is called before the first call to iterator_next
// returns the correct value.
if !it._called_next {
it._called_next = true
// There can be the contrived case where iterator_remove is
// called before ever calling iterator_next, which needs to be
// handled as an actual call to next.
//
// If this happens it._cur will be nil, so only return the
// first value, if it._cur is valid.
if it._cur != nil {
return it._cur, true
}
}
if it._next == nil {
return nil, false
}
it._cur = it._next
it._next = node_next_or_prev_in_order(it._cur, it._direction)
return it._cur, true
}
@(private)
tree_first_or_last_in_order :: proc "contextless" (
t: ^$T/Tree($Value),
direction: Direction,
) -> ^Node(Value) {
first, sign := t._root, i8(direction)
if first != nil {
for {
tmp := node_get_child(first, +sign)
if tmp == nil {
break
}
first = tmp
}
}
return first
}
@(private)
tree_replace_child :: proc "contextless" (
t: ^$T/Tree($Value),
parent, old_child, new_child: ^Node(Value),
) {
if parent != nil {
if old_child == parent._left {
parent._left = new_child
} else {
parent._right = new_child
}
} else {
t._root = new_child
}
}
@(private)
tree_rotate :: proc "contextless" (t: ^$T/Tree($Value), a: ^Node(Value), sign: i8) {
b := node_get_child(a, -sign)
e := node_get_child(b, +sign)
p := a._parent
node_set_child(a, -sign, e)
a._parent = b
node_set_child(b, +sign, a)
b._parent = p
if e != nil {
e._parent = a
}
tree_replace_child(t, p, a, b)
}
@(private)
tree_double_rotate :: proc "contextless" (
t: ^$T/Tree($Value),
b, a: ^Node(Value),
sign: i8,
) -> ^Node(Value) {
e := node_get_child(b, +sign)
f := node_get_child(e, -sign)
g := node_get_child(e, +sign)
p := a._parent
e_bal := e._balance
node_set_child(a, -sign, g)
a_bal := -e_bal
if sign * e_bal >= 0 {
a_bal = 0
}
node_set_parent_balance(a, e, a_bal)
node_set_child(b, +sign, f)
b_bal := -e_bal
if sign * e_bal <= 0 {
b_bal = 0
}
node_set_parent_balance(b, e, b_bal)
node_set_child(e, +sign, a)
node_set_child(e, -sign, b)
node_set_parent_balance(e, p, 0)
if g != nil {
g._parent = a
}
if f != nil {
f._parent = b
}
tree_replace_child(t, p, a, e)
return e
}
@(private)
tree_handle_subtree_growth :: proc "contextless" (
t: ^$T/Tree($Value),
node, parent: ^Node(Value),
sign: i8,
) -> bool {
old_balance_factor := parent._balance
if old_balance_factor == 0 {
node_adjust_balance_factor(parent, sign)
return false
}
new_balance_factor := old_balance_factor + sign
if new_balance_factor == 0 {
node_adjust_balance_factor(parent, sign)
return true
}
if sign * node._balance > 0 {
tree_rotate(t, parent, -sign)
node_adjust_balance_factor(parent, -sign)
node_adjust_balance_factor(node, -sign)
} else {
tree_double_rotate(t, node, parent, -sign)
}
return true
}
@(private)
tree_rebalance_after_insert :: proc "contextless" (t: ^$T/Tree($Value), inserted: ^Node(Value)) {
node, parent := inserted, inserted._parent
switch {
case parent == nil:
return
case node == parent._left:
node_adjust_balance_factor(parent, -1)
case:
node_adjust_balance_factor(parent, +1)
}
if parent._balance == 0 {
return
}
for done := false; !done; {
node = parent
if parent = node._parent; parent == nil {
return
}
if node == parent._left {
done = tree_handle_subtree_growth(t, node, parent, -1)
} else {
done = tree_handle_subtree_growth(t, node, parent, +1)
}
}
}
@(private)
tree_swap_with_successor :: proc "contextless" (
t: ^$T/Tree($Value),
x: ^Node(Value),
) -> (
^Node(Value),
bool,
) {
ret: ^Node(Value)
left_deleted: bool
y := x._right
if y._left == nil {
ret = y
} else {
q: ^Node(Value)
for {
q = y
if y = y._left; y._left == nil {
break
}
}
if q._left = y._right; q._left != nil {
q._left._parent = q
}
y._right = x._right
x._right._parent = y
ret = q
left_deleted = true
}
y._left = x._left
x._left._parent = y
y._parent = x._parent
y._balance = x._balance
tree_replace_child(t, x._parent, x, y)
return ret, left_deleted
}
@(private)
tree_handle_subtree_shrink :: proc "contextless" (
t: ^$T/Tree($Value),
parent: ^Node(Value),
sign: i8,
left_deleted: ^bool,
) -> ^Node(Value) {
old_balance_factor := parent._balance
if old_balance_factor == 0 {
node_adjust_balance_factor(parent, sign)
return nil
}
node: ^Node(Value)
new_balance_factor := old_balance_factor + sign
if new_balance_factor == 0 {
node_adjust_balance_factor(parent, sign)
node = parent
} else {
node = node_get_child(parent, sign)
if sign * node._balance >= 0 {
tree_rotate(t, parent, -sign)
if node._balance == 0 {
node_adjust_balance_factor(node, -sign)
return nil
}
node_adjust_balance_factor(parent, -sign)
node_adjust_balance_factor(node, -sign)
} else {
node = tree_double_rotate(t, node, parent, -sign)
}
}
parent := parent
if parent = node._parent; parent != nil {
left_deleted^ = node == parent._left
}
return parent
}
@(private)
node_reset :: proc "contextless" (n: ^Node($Value)) {
// Mostly pointless as n will be deleted after this is called, but
// attempt to be able to catch cases of n not being in the tree.
n._parent = n
n._left = nil
n._right = nil
n._balance = 0
}
@(private)
node_set_parent_balance :: #force_inline proc "contextless" (
n, parent: ^Node($Value),
balance: i8,
) {
n._parent = parent
n._balance = balance
}
@(private)
node_get_child :: #force_inline proc "contextless" (n: ^Node($Value), sign: i8) -> ^Node(Value) {
if sign < 0 {
return n._left
}
return n._right
}
@(private)
node_next_or_prev_in_order :: proc "contextless" (
n: ^Node($Value),
direction: Direction,
) -> ^Node(Value) {
next, tmp: ^Node(Value)
sign := i8(direction)
if next = node_get_child(n, +sign); next != nil {
for {
tmp = node_get_child(next, -sign)
if tmp == nil {
break
}
next = tmp
}
} else {
tmp, next = n, n._parent
for next != nil && tmp == node_get_child(next, +sign) {
tmp, next = next, next._parent
}
}
return next
}
@(private)
node_set_child :: #force_inline proc "contextless" (
n: ^Node($Value),
sign: i8,
child: ^Node(Value),
) {
if sign < 0 {
n._left = child
} else {
n._right = child
}
}
@(private)
node_adjust_balance_factor :: #force_inline proc "contextless" (n: ^Node($Value), amount: i8) {
n._balance += amount
}
@(private)
iterator_first :: proc "contextless" (it: ^Iterator($Value)) {
// This is private because behavior when the user manually calls
// iterator_first followed by iterator_next is unintuitive, since
// the first call to iterator_next MUST return the first node
// instead of advancing so that `for node in iterator_next(&next)`
// works as expected.
switch it._direction {
case .Forward:
it._cur = tree_first_or_last_in_order(it._tree, .Backward)
case .Backward:
it._cur = tree_first_or_last_in_order(it._tree, .Forward)
}
it._next = nil
it._called_next = false
if it._cur != nil {
it._next = node_next_or_prev_in_order(it._cur, it._direction)
}
}
+16 -70
View File
@@ -1,84 +1,30 @@
# crypto
A cryptography library for the Odin language
A cryptography library for the Odin language.
## Supported
This library offers various algorithms implemented in Odin.
Please see the chart below for some of the options.
## Hashing algorithms
| Algorithm | |
|:-------------------------------------------------------------------------------------------------------------|:-----------------|
| [BLAKE2B](https://datatracker.ietf.org/doc/html/rfc7693) | &#10004;&#65039; |
| [BLAKE2S](https://datatracker.ietf.org/doc/html/rfc7693) | &#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; |
| legacy/[Keccak](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf) | &#10004;&#65039; |
| legacy/[MD5](https://datatracker.ietf.org/doc/html/rfc1321) | &#10004;&#65039; |
| legacy/[SHA-1](https://datatracker.ietf.org/doc/html/rfc3174) | &#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 six procedures.
- `hash_string` - Hash a given string and return the computed hash. Just calls `hash_bytes` internally
- `hash_bytes` - Hash a given byte slice and return the computed hash
- `hash_string_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. Just calls `hash_bytes_to_buffer` internally
- `hash_bytes_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. The destination buffer has to be at least as big as the digest size of the hash
- `hash_stream` - Takes a stream from io.Stream and returns the computed hash from it
- `hash_file` - Takes a file handle and returns the computed hash from it. A second optional boolean parameter controls if the file is streamed (this is the default) or read at once (set to true)
\* On some algorithms there is another part to the name, since they might offer control about additional parameters.
For instance, `SHA-2` offers different sizes.
Computing a 512-bit hash is therefore achieved by calling `sha2.hash_512(...)`.
#### Low level API
The above mentioned procedures internally call three procedures: `init`, `update` and `final`.
You may also directly call them, if you wish.
#### Example
```odin
package crypto_example
// Import the desired package
import "core:crypto/blake2b"
main :: proc() {
input := "foo"
// Compute the hash, using the high level API
computed_hash := blake2b.hash(input)
// Variant that takes a destination buffer, instead of returning the computed hash
hash := make([]byte, sha2.DIGEST_SIZE) // @note: Destination buffer has to be at least as big as the digest size of the hash
blake2b.hash(input, hash[:])
// Compute the hash, using the low level API
ctx: blake2b.Context
computed_hash_low: [blake2b.DIGEST_SIZE]byte
blake2b.init(&ctx)
blake2b.update(&ctx, transmute([]byte)input)
blake2b.final(&ctx, computed_hash_low[:])
}
```
For example uses of all available algorithms, please see the tests within `tests/core/crypto`.
This package offers various algorithms implemented in Odin, along with
useful helpers such as access to the system entropy source, and a
constant-time byte comparison.
## Implementation considerations
- The crypto packages are not thread-safe.
- Best-effort is make to mitigate timing side-channels on reasonable
architectures. Architectures that are known to be unreasonable include
architectures. Architectures that are known to be unreasonable include
but are not limited to i386, i486, and WebAssembly.
- Some but not all of the packages attempt to santize sensitive data,
however this is not done consistently through the library at the moment.
As Thomas Pornin puts it "In general, such memory cleansing is a fool's
quest."
- Implementations assume a 64-bit architecture (64-bit integer arithmetic
is fast, and includes add-with-carry, sub-with-borrow, and full-result
multiply).
- Hardware sidechannels are explicitly out of scope for this package.
Notable examples include but are not limited to:
- Power/RF side-channels etc.
- Fault injection attacks etc.
- Hardware vulnerabilities ("apply mitigations or buy a new CPU").
- The packages attempt to santize sensitive data, however this is, and
will remain a "best-effort" implementation decision. As Thomas Pornin
puts it "In general, such memory cleansing is a fool's quest."
- All of these packages have not received independent third party review.
## License
+58 -39
View File
@@ -11,6 +11,7 @@ package _blake2
*/
import "core:encoding/endian"
import "core:mem"
BLAKE2S_BLOCK_SIZE :: 64
BLAKE2S_SIZE :: 32
@@ -28,7 +29,6 @@ Blake2s_Context :: struct {
is_keyed: bool,
size: byte,
is_last_node: bool,
cfg: Blake2_Config,
is_initialized: bool,
}
@@ -44,7 +44,6 @@ Blake2b_Context :: struct {
is_keyed: bool,
size: byte,
is_last_node: bool,
cfg: Blake2_Config,
is_initialized: bool,
}
@@ -83,62 +82,61 @@ BLAKE2B_IV := [8]u64 {
0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
}
init :: proc(ctx: ^$T) {
init :: proc(ctx: ^$T, cfg: ^Blake2_Config) {
when T == Blake2s_Context {
block_size :: BLAKE2S_BLOCK_SIZE
max_size :: BLAKE2S_SIZE
} else when T == Blake2b_Context {
block_size :: BLAKE2B_BLOCK_SIZE
max_size :: BLAKE2B_SIZE
}
if ctx.cfg.size > max_size {
if cfg.size > max_size {
panic("blake2: requested output size exceeeds algorithm max")
}
p := make([]byte, block_size)
defer delete(p)
// To save having to allocate a scratch buffer, use the internal
// data buffer (`ctx.x`), as it is exactly the correct size.
p := ctx.x[:]
p[0] = ctx.cfg.size
p[1] = byte(len(ctx.cfg.key))
p[0] = cfg.size
p[1] = byte(len(cfg.key))
if ctx.cfg.salt != nil {
if cfg.salt != nil {
when T == Blake2s_Context {
copy(p[16:], ctx.cfg.salt)
copy(p[16:], cfg.salt)
} else when T == Blake2b_Context {
copy(p[32:], ctx.cfg.salt)
copy(p[32:], cfg.salt)
}
}
if ctx.cfg.person != nil {
if cfg.person != nil {
when T == Blake2s_Context {
copy(p[24:], ctx.cfg.person)
copy(p[24:], cfg.person)
} else when T == Blake2b_Context {
copy(p[48:], ctx.cfg.person)
copy(p[48:], cfg.person)
}
}
if ctx.cfg.tree != nil {
p[2] = ctx.cfg.tree.(Blake2_Tree).fanout
p[3] = ctx.cfg.tree.(Blake2_Tree).max_depth
endian.unchecked_put_u32le(p[4:], ctx.cfg.tree.(Blake2_Tree).leaf_size)
if cfg.tree != nil {
p[2] = cfg.tree.(Blake2_Tree).fanout
p[3] = cfg.tree.(Blake2_Tree).max_depth
endian.unchecked_put_u32le(p[4:], cfg.tree.(Blake2_Tree).leaf_size)
when T == Blake2s_Context {
p[8] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset)
p[9] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 8)
p[10] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 16)
p[11] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 24)
p[12] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 32)
p[13] = byte(ctx.cfg.tree.(Blake2_Tree).node_offset >> 40)
p[14] = ctx.cfg.tree.(Blake2_Tree).node_depth
p[15] = ctx.cfg.tree.(Blake2_Tree).inner_hash_size
p[8] = byte(cfg.tree.(Blake2_Tree).node_offset)
p[9] = byte(cfg.tree.(Blake2_Tree).node_offset >> 8)
p[10] = byte(cfg.tree.(Blake2_Tree).node_offset >> 16)
p[11] = byte(cfg.tree.(Blake2_Tree).node_offset >> 24)
p[12] = byte(cfg.tree.(Blake2_Tree).node_offset >> 32)
p[13] = byte(cfg.tree.(Blake2_Tree).node_offset >> 40)
p[14] = cfg.tree.(Blake2_Tree).node_depth
p[15] = cfg.tree.(Blake2_Tree).inner_hash_size
} else when T == Blake2b_Context {
endian.unchecked_put_u64le(p[8:], ctx.cfg.tree.(Blake2_Tree).node_offset)
p[16] = ctx.cfg.tree.(Blake2_Tree).node_depth
p[17] = ctx.cfg.tree.(Blake2_Tree).inner_hash_size
endian.unchecked_put_u64le(p[8:], cfg.tree.(Blake2_Tree).node_offset)
p[16] = cfg.tree.(Blake2_Tree).node_depth
p[17] = cfg.tree.(Blake2_Tree).inner_hash_size
}
} else {
p[2], p[3] = 1, 1
}
ctx.size = ctx.cfg.size
ctx.size = cfg.size
for i := 0; i < 8; i += 1 {
when T == Blake2s_Context {
ctx.h[i] = BLAKE2S_IV[i] ~ endian.unchecked_get_u32le(p[i * 4:])
@@ -147,11 +145,14 @@ init :: proc(ctx: ^$T) {
ctx.h[i] = BLAKE2B_IV[i] ~ endian.unchecked_get_u64le(p[i * 8:])
}
}
if ctx.cfg.tree != nil && ctx.cfg.tree.(Blake2_Tree).is_last_node {
mem.zero(&ctx.x, size_of(ctx.x)) // Done with the scratch space, no barrier.
if cfg.tree != nil && cfg.tree.(Blake2_Tree).is_last_node {
ctx.is_last_node = true
}
if len(ctx.cfg.key) > 0 {
copy(ctx.padded_key[:], ctx.cfg.key)
if len(cfg.key) > 0 {
copy(ctx.padded_key[:], cfg.key)
update(ctx, ctx.padded_key[:])
ctx.is_keyed = true
}
@@ -194,22 +195,40 @@ update :: proc(ctx: ^$T, p: []byte) {
ctx.nx += copy(ctx.x[ctx.nx:], p)
}
final :: proc(ctx: ^$T, hash: []byte) {
final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
assert(ctx.is_initialized)
ctx := ctx
if finalize_clone {
tmp_ctx: T
clone(&tmp_ctx, ctx)
ctx = &tmp_ctx
}
defer(reset(ctx))
when T == Blake2s_Context {
if len(hash) < int(ctx.cfg.size) {
if len(hash) < int(ctx.size) {
panic("crypto/blake2s: invalid destination digest size")
}
blake2s_final(ctx, hash)
} else when T == Blake2b_Context {
if len(hash) < int(ctx.cfg.size) {
if len(hash) < int(ctx.size) {
panic("crypto/blake2b: invalid destination digest size")
}
blake2b_final(ctx, hash)
}
}
ctx.is_initialized = false
clone :: proc(ctx, other: ^$T) {
ctx^ = other^
}
reset :: proc(ctx: ^$T) {
if !ctx.is_initialized {
return
}
mem.zero_explicit(ctx, size_of(ctx^))
}
@(private)
+428
View File
@@ -0,0 +1,428 @@
package _edwards25519
/*
This implements the edwards25519 composite-order group, primarily for
the purpose of implementing X25519, Ed25519, and ristretto255. Use of
this package for other purposes is NOT RECOMMENDED.
See:
- https://eprint.iacr.org/2011/368.pdf
- https://datatracker.ietf.org/doc/html/rfc8032
- https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
*/
import "base:intrinsics"
import "core:crypto"
import field "core:crypto/_fiat/field_curve25519"
import "core:mem"
// Group_Element is an edwards25519 group element, as extended homogenous
// coordinates, which represents the affine point `(x, y)` as `(X, Y, Z, T)`,
// with the relations `x = X/Z`, `y = Y/Z`, and `x * y = T/Z`.
//
// d = -121665/121666 = 37095705934669439343138083508754565189542113879843219016388785533085940283555
// a = -1
//
// Notes:
// - There is considerable scope for optimization, however that
// will not change the external API, and this is simple and reasonably
// performant.
// - The API delibarately makes it hard to create arbitrary group
// elements that are not on the curve.
// - The group element decoding routine takes the opinionated stance of
// rejecting non-canonical encodings.
FE_D := field.Tight_Field_Element {
929955233495203,
466365720129213,
1662059464998953,
2033849074728123,
1442794654840575,
}
@(private)
FE_A := field.Tight_Field_Element {
2251799813685228,
2251799813685247,
2251799813685247,
2251799813685247,
2251799813685247,
}
@(private)
FE_D2 := field.Tight_Field_Element {
1859910466990425,
932731440258426,
1072319116312658,
1815898335770999,
633789495995903,
}
@(private)
GE_BASEPOINT := Group_Element {
field.Tight_Field_Element {
1738742601995546,
1146398526822698,
2070867633025821,
562264141797630,
587772402128613,
},
field.Tight_Field_Element {
1801439850948184,
1351079888211148,
450359962737049,
900719925474099,
1801439850948198,
},
field.Tight_Field_Element{1, 0, 0, 0, 0},
field.Tight_Field_Element {
1841354044333475,
16398895984059,
755974180946558,
900171276175154,
1821297809914039,
},
}
GE_IDENTITY := Group_Element {
field.Tight_Field_Element{0, 0, 0, 0, 0},
field.Tight_Field_Element{1, 0, 0, 0, 0},
field.Tight_Field_Element{1, 0, 0, 0, 0},
field.Tight_Field_Element{0, 0, 0, 0, 0},
}
Group_Element :: struct {
x: field.Tight_Field_Element,
y: field.Tight_Field_Element,
z: field.Tight_Field_Element,
t: field.Tight_Field_Element,
}
ge_clear :: proc "contextless" (ge: ^Group_Element) {
mem.zero_explicit(ge, size_of(Group_Element))
}
ge_set :: proc "contextless" (ge, a: ^Group_Element) {
field.fe_set(&ge.x, &a.x)
field.fe_set(&ge.y, &a.y)
field.fe_set(&ge.z, &a.z)
field.fe_set(&ge.t, &a.t)
}
@(require_results)
ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
if len(b) != 32 {
intrinsics.trap()
}
b_ := transmute(^[32]byte)(raw_data(b))
// Do the work in a scratch element, so that ge is unchanged on
// failure.
tmp: Group_Element = ---
defer ge_clear(&tmp)
field.fe_one(&tmp.z) // Z = 1
// The encoding is the y-coordinate, with the x-coordinate polarity
// (odd/even) encoded in the MSB.
field.fe_from_bytes(&tmp.y, b_) // ignores high bit
// Recover the candidate x-coordinate via the curve equation:
// x^2 = (y^2 - 1) / (d * y^2 + 1) (mod p)
fe_tmp := &tmp.t // Use this to store intermediaries.
fe_one := &tmp.z
// x = num = y^2 - 1
field.fe_carry_square(fe_tmp, field.fe_relax_cast(&tmp.y)) // fe_tmp = y^2
field.fe_carry_sub(&tmp.x, fe_tmp, fe_one)
// den = d * y^2 + 1
field.fe_carry_mul(fe_tmp, field.fe_relax_cast(fe_tmp), field.fe_relax_cast(&FE_D))
field.fe_carry_add(fe_tmp, fe_tmp, fe_one)
// x = invsqrt(den/num)
is_square := field.fe_carry_sqrt_ratio_m1(
&tmp.x,
field.fe_relax_cast(&tmp.x),
field.fe_relax_cast(fe_tmp),
)
if is_square == 0 {
return false
}
// Pick the right x-coordinate.
field.fe_cond_negate(&tmp.x, &tmp.x, int(b[31] >> 7))
// t = x * y
field.fe_carry_mul(&tmp.t, field.fe_relax_cast(&tmp.x), field.fe_relax_cast(&tmp.y))
// Reject non-canonical encodings of ge.
buf: [32]byte = ---
field.fe_to_bytes(&buf, &tmp.y)
buf[31] |= byte(field.fe_is_negative(&tmp.x)) << 7
is_canonical := crypto.compare_constant_time(b, buf[:])
ge_cond_assign(ge, &tmp, is_canonical)
mem.zero_explicit(&buf, size_of(buf))
return is_canonical == 1
}
ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
if len(dst) != 32 {
intrinsics.trap()
}
dst_ := transmute(^[32]byte)(raw_data(dst))
// Convert the element to affine (x, y) representation.
x, y, z_inv: field.Tight_Field_Element = ---, ---, ---
field.fe_carry_inv(&z_inv, field.fe_relax_cast(&ge.z))
field.fe_carry_mul(&x, field.fe_relax_cast(&ge.x), field.fe_relax_cast(&z_inv))
field.fe_carry_mul(&y, field.fe_relax_cast(&ge.y), field.fe_relax_cast(&z_inv))
// Encode the y-coordinate.
field.fe_to_bytes(dst_, &y)
// Copy the least significant bit of the x-coordinate to the most
// significant bit of the encoded y-coordinate.
dst_[31] |= byte((x[0] & 1) << 7)
field.fe_clear_vec([]^field.Tight_Field_Element{&x, &y, &z_inv})
}
ge_identity :: proc "contextless" (ge: ^Group_Element) {
field.fe_zero(&ge.x)
field.fe_one(&ge.y)
field.fe_one(&ge.z)
field.fe_zero(&ge.t)
}
ge_generator :: proc "contextless" (ge: ^Group_Element) {
ge_set(ge, &GE_BASEPOINT)
}
@(private)
Addend_Group_Element :: struct {
y2_minus_x2: field.Loose_Field_Element, // t1
y2_plus_x2: field.Loose_Field_Element, // t3
k_times_t2: field.Tight_Field_Element, // t4
two_times_z2: field.Loose_Field_Element, // t5
}
@(private)
ge_addend_set :: proc "contextless" (ge_a: ^Addend_Group_Element, ge: ^Group_Element) {
field.fe_sub(&ge_a.y2_minus_x2, &ge.y, &ge.x)
field.fe_add(&ge_a.y2_plus_x2, &ge.y, &ge.x)
field.fe_carry_mul(&ge_a.k_times_t2, field.fe_relax_cast(&FE_D2), field.fe_relax_cast(&ge.t))
field.fe_add(&ge_a.two_times_z2, &ge.z, &ge.z)
}
@(private)
ge_addend_conditional_assign :: proc "contextless" (ge_a, a: ^Addend_Group_Element, ctrl: int) {
field.fe_cond_select(&ge_a.y2_minus_x2, &ge_a.y2_minus_x2, &a.y2_minus_x2, ctrl)
field.fe_cond_select(&ge_a.y2_plus_x2, &ge_a.y2_plus_x2, &a.y2_plus_x2, ctrl)
field.fe_cond_select(&ge_a.k_times_t2, &ge_a.k_times_t2, &a.k_times_t2, ctrl)
field.fe_cond_select(&ge_a.two_times_z2, &ge_a.two_times_z2, &a.two_times_z2, ctrl)
}
@(private)
Add_Scratch :: struct {
A, B, C, D: field.Tight_Field_Element,
E, F, G, H: field.Loose_Field_Element,
t0, t2: field.Loose_Field_Element,
}
ge_add :: proc "contextless" (ge, a, b: ^Group_Element) {
b_: Addend_Group_Element = ---
ge_addend_set(&b_, b)
scratch: Add_Scratch = ---
ge_add_addend(ge, a, &b_, &scratch)
mem.zero_explicit(&b_, size_of(Addend_Group_Element))
mem.zero_explicit(&scratch, size_of(Add_Scratch))
}
@(private)
ge_add_addend :: proc "contextless" (
ge, a: ^Group_Element,
b: ^Addend_Group_Element,
scratch: ^Add_Scratch,
) {
// https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
// Assumptions: k=2*d.
//
// t0 = Y1-X1
// t1 = Y2-X2
// A = t0*t1
// t2 = Y1+X1
// t3 = Y2+X2
// B = t2*t3
// t4 = k*T2
// C = T1*t4
// t5 = 2*Z2
// D = Z1*t5
// E = B-A
// F = D-C
// G = D+C
// H = B+A
// X3 = E*F
// Y3 = G*H
// T3 = E*H
// Z3 = F*G
//
// In order to make the scalar multiply faster, the addend is provided
// as a `Addend_Group_Element` with t1, t3, t4, and t5 precomputed, as
// it is trivially obvious that those are the only values used by the
// formula that are directly dependent on `b`, and are only dependent
// on `b` and constants. This saves 1 sub, 2 adds, and 1 multiply,
// each time the intermediate representation can be reused.
A, B, C, D := &scratch.A, &scratch.B, &scratch.C, &scratch.D
E, F, G, H := &scratch.E, &scratch.F, &scratch.G, &scratch.H
t0, t2 := &scratch.t0, &scratch.t2
field.fe_sub(t0, &a.y, &a.x)
t1 := &b.y2_minus_x2
field.fe_carry_mul(A, t0, t1)
field.fe_add(t2, &a.y, &a.x)
t3 := &b.y2_plus_x2
field.fe_carry_mul(B, t2, t3)
t4 := &b.k_times_t2
field.fe_carry_mul(C, field.fe_relax_cast(&a.t), field.fe_relax_cast(t4))
t5 := &b.two_times_z2
field.fe_carry_mul(D, field.fe_relax_cast(&a.z), t5)
field.fe_sub(E, B, A)
field.fe_sub(F, D, C)
field.fe_add(G, D, C)
field.fe_add(H, B, A)
field.fe_carry_mul(&ge.x, E, F)
field.fe_carry_mul(&ge.y, G, H)
field.fe_carry_mul(&ge.t, E, H)
field.fe_carry_mul(&ge.z, F, G)
}
@(private)
Double_Scratch :: struct {
A, B, C, D, G: field.Tight_Field_Element,
t0, t2, t3: field.Tight_Field_Element,
E, F, H: field.Loose_Field_Element,
t1: field.Loose_Field_Element,
}
ge_double :: proc "contextless" (ge, a: ^Group_Element, scratch: ^Double_Scratch = nil) {
// https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#doubling-dbl-2008-hwcd
//
// A = X1^2
// B = Y1^2
// t0 = Z1^2
// C = 2*t0
// D = a*A
// t1 = X1+Y1
// t2 = t1^2
// t3 = t2-A
// E = t3-B
// G = D+B
// F = G-C
// H = D-B
// X3 = E*F
// Y3 = G*H
// T3 = E*H
// Z3 = F*G
sanitize, scratch := scratch == nil, scratch
if sanitize {
tmp: Double_Scratch = ---
scratch = &tmp
}
A, B, C, D, G := &scratch.A, &scratch.B, &scratch.C, &scratch.D, &scratch.G
t0, t2, t3 := &scratch.t0, &scratch.t2, &scratch.t3
E, F, H := &scratch.E, &scratch.F, &scratch.H
t1 := &scratch.t1
field.fe_carry_square(A, field.fe_relax_cast(&a.x))
field.fe_carry_square(B, field.fe_relax_cast(&a.y))
field.fe_carry_square(t0, field.fe_relax_cast(&a.z))
field.fe_carry_add(C, t0, t0)
field.fe_carry_mul(D, field.fe_relax_cast(&FE_A), field.fe_relax_cast(A))
field.fe_add(t1, &a.x, &a.y)
field.fe_carry_square(t2, t1)
field.fe_carry_sub(t3, t2, A)
field.fe_sub(E, t3, B)
field.fe_carry_add(G, D, B)
field.fe_sub(F, G, C)
field.fe_sub(H, D, B)
G_ := field.fe_relax_cast(G)
field.fe_carry_mul(&ge.x, E, F)
field.fe_carry_mul(&ge.y, G_, H)
field.fe_carry_mul(&ge.t, E, H)
field.fe_carry_mul(&ge.z, F, G_)
if sanitize {
mem.zero_explicit(scratch, size_of(Double_Scratch))
}
}
ge_negate :: proc "contextless" (ge, a: ^Group_Element) {
field.fe_carry_opp(&ge.x, &a.x)
field.fe_set(&ge.y, &a.y)
field.fe_set(&ge.z, &a.z)
field.fe_carry_opp(&ge.t, &a.t)
}
ge_cond_negate :: proc "contextless" (ge, a: ^Group_Element, ctrl: int) {
tmp: Group_Element = ---
ge_negate(&tmp, a)
ge_cond_assign(ge, &tmp, ctrl)
ge_clear(&tmp)
}
ge_cond_assign :: proc "contextless" (ge, a: ^Group_Element, ctrl: int) {
field.fe_cond_assign(&ge.x, &a.x, ctrl)
field.fe_cond_assign(&ge.y, &a.y, ctrl)
field.fe_cond_assign(&ge.z, &a.z, ctrl)
field.fe_cond_assign(&ge.t, &a.t, ctrl)
}
ge_cond_select :: proc "contextless" (ge, a, b: ^Group_Element, ctrl: int) {
field.fe_cond_select(&ge.x, &a.x, &b.x, ctrl)
field.fe_cond_select(&ge.y, &a.y, &b.y, ctrl)
field.fe_cond_select(&ge.z, &a.z, &b.z, ctrl)
field.fe_cond_select(&ge.t, &a.t, &b.t, ctrl)
}
@(require_results)
ge_equal :: proc "contextless" (a, b: ^Group_Element) -> int {
// (x, y) ?= (x', y') -> (X/Z, Y/Z) ?= (X'/Z', Y'/Z')
// X/Z ?= X'/Z', Y/Z ?= Y'/Z' -> X*Z' ?= X'*Z, Y*Z' ?= Y'*Z
ax_bz, bx_az, ay_bz, by_az: field.Tight_Field_Element = ---, ---, ---, ---
field.fe_carry_mul(&ax_bz, field.fe_relax_cast(&a.x), field.fe_relax_cast(&b.z))
field.fe_carry_mul(&bx_az, field.fe_relax_cast(&b.x), field.fe_relax_cast(&a.z))
field.fe_carry_mul(&ay_bz, field.fe_relax_cast(&a.y), field.fe_relax_cast(&b.z))
field.fe_carry_mul(&by_az, field.fe_relax_cast(&b.y), field.fe_relax_cast(&a.z))
ret := field.fe_equal(&ax_bz, &bx_az) & field.fe_equal(&ay_bz, &by_az)
field.fe_clear_vec([]^field.Tight_Field_Element{&ax_bz, &ay_bz, &bx_az, &by_az})
return ret
}
@(require_results)
ge_is_small_order :: proc "contextless" (ge: ^Group_Element) -> bool {
tmp: Group_Element = ---
ge_double(&tmp, ge)
ge_double(&tmp, &tmp)
ge_double(&tmp, &tmp)
return ge_equal(&tmp, &GE_IDENTITY) == 1
}
@(require_results)
ge_in_prime_order_subgroup_vartime :: proc "contextless" (ge: ^Group_Element) -> bool {
// This is currently *very* expensive. The faster method would be
// something like (https://eprint.iacr.org/2022/1164.pdf), however
// that is a ~50% speedup, and a lot of added complexity for something
// that is better solved by "just use ristretto255".
tmp: Group_Element = ---
_ge_scalarmult(&tmp, ge, &SC_ELL, true)
return ge_equal(&tmp, &GE_IDENTITY) == 1
}
@@ -0,0 +1,61 @@
package _edwards25519
import "base:intrinsics"
import field "core:crypto/_fiat/field_scalar25519"
import "core:mem"
Scalar :: field.Montgomery_Domain_Field_Element
// WARNING: This is non-canonical and only to be used when checking if
// a group element is on the prime-order subgroup.
@(private)
SC_ELL := field.Non_Montgomery_Domain_Field_Element {
field.ELL[0],
field.ELL[1],
field.ELL[2],
field.ELL[3],
}
sc_set_u64 :: proc "contextless" (sc: ^Scalar, i: u64) {
tmp := field.Non_Montgomery_Domain_Field_Element{i, 0, 0, 0}
field.fe_to_montgomery(sc, &tmp)
mem.zero_explicit(&tmp, size_of(tmp))
}
@(require_results)
sc_set_bytes :: proc "contextless" (sc: ^Scalar, b: []byte) -> bool {
if len(b) != 32 {
intrinsics.trap()
}
b_ := transmute(^[32]byte)(raw_data(b))
return field.fe_from_bytes(sc, b_)
}
sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
if len(b) != 32 {
intrinsics.trap()
}
b_ := transmute(^[32]byte)(raw_data(b))
field.fe_from_bytes_rfc8032(sc, b_)
}
sc_clear :: proc "contextless" (sc: ^Scalar) {
mem.zero_explicit(sc, size_of(Scalar))
}
sc_set :: field.fe_set
sc_set_bytes_wide :: field.fe_from_bytes_wide
sc_bytes :: field.fe_to_bytes
sc_zero :: field.fe_zero
sc_one :: field.fe_one
sc_add :: field.fe_add
sc_sub :: field.fe_sub
sc_negate :: field.fe_opp
sc_mul :: field.fe_mul
sc_square :: field.fe_square
sc_cond_assign :: field.fe_cond_assign
sc_equal :: field.fe_equal
@@ -0,0 +1,288 @@
package _edwards25519
import field "core:crypto/_fiat/field_scalar25519"
import "core:math/bits"
import "core:mem"
// GE_BASEPOINT_TABLE is 1 * G, ... 15 * G, in precomputed format.
//
// Note: When generating, the values were reduced to Tight_Field_Element
// ranges, even though that is not required.
@(private)
GE_BASEPOINT_TABLE := Multiply_Table {
{
{62697248952638, 204681361388450, 631292143396476, 338455783676468, 1213667448819585},
{1288382639258501, 245678601348599, 269427782077623, 1462984067271730, 137412439391563},
{301289933810280, 1259582250014073, 1422107436869536, 796239922652654, 1953934009299142},
{2, 0, 0, 0, 0},
},
{
{1519297034332653, 1098796920435767, 1823476547744119, 808144629470969, 2110930855619772},
{338005982828284, 1667856962156925, 100399270107451, 1604566703601691, 1950338038771369},
{1920505767731247, 1443759578976892, 1659852098357048, 1484431291070208, 275018744912646},
{763163817085987, 2195095074806923, 2167883174351839, 1868059999999762, 911071066608705},
},
{
{960627541894068, 1314966688943942, 1126875971034044, 2059608312958945, 605975666152586},
{1714478358025626, 2209607666607510, 1600912834284834, 496072478982142, 481970031861896},
{851735079403194, 1088965826757164, 141569479297499, 602804610059257, 2004026468601520},
{197585529552380, 324719066578543, 564481854250498, 1173818332764578, 35452976395676},
},
{
{1152980410747203, 2196804280851952, 25745194962557, 1915167295473129, 1266299690309224},
{809905889679060, 979732230071345, 1509972345538142, 188492426534402, 818965583123815},
{997685409185036, 1451818320876327, 2126681166774509, 2000509606057528, 235432372486854},
{887734189279642, 1460338685162044, 877378220074262, 102436391401299, 153369156847490},
},
{
{2056621900836770, 1821657694132497, 1627986892909426, 1163363868678833, 1108873376459226},
{1187697490593623, 1066539945237335, 885654531892000, 1357534489491782, 359370291392448},
{1509033452137525, 1305318174298508, 613642471748944, 1987256352550234, 1044283663101541},
{220105720697037, 387661783287620, 328296827867762, 360035589590664, 795213236824054},
},
{
{1820794733038396, 1612235121681074, 757405923441402, 1094031020892801, 231025333128907},
{1639067873254194, 1484176557946322, 300800382144789, 1329915446659183, 1211704578730455},
{641900794791527, 1711751746971612, 179044712319955, 576455585963824, 1852617592509865},
{743549047192397, 685091042550147, 1952415336873496, 1965124675654685, 513364998442917},
},
{
{1004557076870448, 1762911374844520, 1330807633622723, 384072910939787, 953849032243810},
{2178275058221458, 257933183722891, 376684351537894, 2010189102001786, 1981824297484148},
{1332915663881114, 1286540505502549, 1741691283561518, 977214932156314, 1764059494778091},
{429702949064027, 1368332611650677, 2019867176450999, 2212258376161746, 526160996742554},
},
{
{2098932988258576, 2203688382075948, 2120400160059479, 1748488020948146, 1203264167282624},
{677131386735829, 1850249298025188, 672782146532031, 2144145693078904, 2088656272813787},
{1065622343976192, 1573853211848116, 223560413590068, 333846833073379, 27832122205830},
{1781008836504573, 917619542051793, 544322748939913, 882577394308384, 1720521246471195},
},
{
{660120928379860, 2081944024858618, 1878411111349191, 424587356517195, 2111317439894005},
{1834193977811532, 1864164086863319, 797334633289424, 150410812403062, 2085177078466389},
{1438117271371866, 783915531014482, 388731514584658, 292113935417795, 1945855002546714},
{1678140823166658, 679103239148744, 614102761596238, 1052962498997885, 1863983323810390},
},
{
{1690309392496233, 1116333140326275, 1377242323631039, 717196888780674, 82724646713353},
{1722370213432106, 74265192976253, 264239578448472, 1714909985012994, 2216984958602173},
{2010482366920922, 1294036471886319, 566466395005815, 1631955803657320, 1751698647538458},
{1073230604155753, 1159087041338551, 1664057985455483, 127472702826203, 1339591128522371},
},
{
{478053307175577, 2179515791720985, 21146535423512, 1831683844029536, 462805561553981},
{1945267486565588, 1298536818409655, 2214511796262989, 1904981051429012, 252904800782086},
{268945954671210, 222740425595395, 1208025911856230, 1080418823003555, 75929831922483},
{1884784014268948, 643868448202966, 978736549726821, 46385971089796, 1296884812292320},
},
{
{1861159462859103, 7077532564710, 963010365896826, 1938780006785270, 766241051941647},
{1778966986051906, 1713995999765361, 1394565822271816, 1366699246468722, 1213407027149475},
{1978989286560907, 2135084162045594, 1951565508865477, 671788336314416, 293123929458176},
{902608944504080, 2167765718046481, 1285718473078022, 1222562171329269, 492109027844479},
},
{
{1820807832746213, 1029220580458586, 1101997555432203, 1039081975563572, 202477981158221},
{1866134980680205, 2222325502763386, 1830284629571201, 1046966214478970, 418381946936795},
{1783460633291322, 1719505443254998, 1810489639976220, 877049370713018, 2187801198742619},
{197118243000763, 305493867565736, 518814410156522, 1656246186645170, 901894734874934},
},
{
{225454942125915, 478410476654509, 600524586037746, 643450007230715, 1018615928259319},
{1733330584845708, 881092297970296, 507039890129464, 496397090721598, 2230888519577628},
{690155664737246, 1010454785646677, 753170144375012, 1651277613844874, 1622648796364156},
{1321310321891618, 1089655277873603, 235891750867089, 815878279563688, 1709264240047556},
},
{
{805027036551342, 1387174275567452, 1156538511461704, 1465897486692171, 1208567094120903},
{2228417017817483, 202885584970535, 2182114782271881, 2077405042592934, 1029684358182774},
{460447547653983, 627817697755692, 524899434670834, 1228019344939427, 740684787777653},
{849757462467675, 447476306919899, 422618957298818, 302134659227815, 675831828440895},
},
}
ge_scalarmult :: proc "contextless" (ge, p: ^Group_Element, sc: ^Scalar) {
tmp: field.Non_Montgomery_Domain_Field_Element
field.fe_from_montgomery(&tmp, sc)
_ge_scalarmult(ge, p, &tmp)
mem.zero_explicit(&tmp, size_of(tmp))
}
ge_scalarmult_basepoint :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar) {
// Something like the comb method from "Fast and compact elliptic-curve
// cryptography" Section 3.3, would be more performant, but more
// complex.
//
// - https://eprint.iacr.org/2012/309
ge_scalarmult(ge, &GE_BASEPOINT, sc)
}
ge_scalarmult_vartime :: proc "contextless" (ge, p: ^Group_Element, sc: ^Scalar) {
tmp: field.Non_Montgomery_Domain_Field_Element
field.fe_from_montgomery(&tmp, sc)
_ge_scalarmult(ge, p, &tmp, true)
}
ge_double_scalarmult_basepoint_vartime :: proc "contextless" (
ge: ^Group_Element,
a: ^Scalar,
A: ^Group_Element,
b: ^Scalar,
) {
// Strauss-Shamir, commonly referred to as the "Shamir trick",
// saves half the doublings, relative to doing this the naive way.
//
// ABGLSV-Pornin (https://eprint.iacr.org/2020/454) is faster,
// but significantly more complex, and has incompatibilities with
// mixed-order group elements.
tmp_add: Add_Scratch = ---
tmp_addend: Addend_Group_Element = ---
tmp_dbl: Double_Scratch = ---
tmp: Group_Element = ---
A_tbl: Multiply_Table = ---
mul_tbl_set(&A_tbl, A, &tmp_add)
sc_a, sc_b: field.Non_Montgomery_Domain_Field_Element
field.fe_from_montgomery(&sc_a, a)
field.fe_from_montgomery(&sc_b, b)
ge_identity(&tmp)
for i := 31; i >= 0; i = i - 1 {
limb := i / 8
shift := uint(i & 7) * 8
limb_byte_a := sc_a[limb] >> shift
limb_byte_b := sc_b[limb] >> shift
hi_a, lo_a := (limb_byte_a >> 4) & 0x0f, limb_byte_a & 0x0f
hi_b, lo_b := (limb_byte_b >> 4) & 0x0f, limb_byte_b & 0x0f
if i != 31 {
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
}
mul_tbl_add(&tmp, &A_tbl, hi_a, &tmp_add, &tmp_addend, true)
mul_tbl_add(&tmp, &GE_BASEPOINT_TABLE, hi_b, &tmp_add, &tmp_addend, true)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
mul_tbl_add(&tmp, &A_tbl, lo_a, &tmp_add, &tmp_addend, true)
mul_tbl_add(&tmp, &GE_BASEPOINT_TABLE, lo_b, &tmp_add, &tmp_addend, true)
}
ge_set(ge, &tmp)
}
@(private)
_ge_scalarmult :: proc "contextless" (
ge, p: ^Group_Element,
sc: ^field.Non_Montgomery_Domain_Field_Element,
unsafe_is_vartime := false,
) {
// Do the simplest possible thing that works and provides adequate,
// performance, which is windowed add-then-multiply.
tmp_add: Add_Scratch = ---
tmp_addend: Addend_Group_Element = ---
tmp_dbl: Double_Scratch = ---
tmp: Group_Element = ---
p_tbl: Multiply_Table = ---
mul_tbl_set(&p_tbl, p, &tmp_add)
ge_identity(&tmp)
for i := 31; i >= 0; i = i - 1 {
limb := i / 8
shift := uint(i & 7) * 8
limb_byte := sc[limb] >> shift
hi, lo := (limb_byte >> 4) & 0x0f, limb_byte & 0x0f
if i != 31 {
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
}
mul_tbl_add(&tmp, &p_tbl, hi, &tmp_add, &tmp_addend, unsafe_is_vartime)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
ge_double(&tmp, &tmp, &tmp_dbl)
mul_tbl_add(&tmp, &p_tbl, lo, &tmp_add, &tmp_addend, unsafe_is_vartime)
}
ge_set(ge, &tmp)
if !unsafe_is_vartime {
ge_clear(&tmp)
mem.zero_explicit(&tmp_add, size_of(Add_Scratch))
mem.zero_explicit(&tmp_addend, size_of(Addend_Group_Element))
mem.zero_explicit(&tmp_dbl, size_of(Double_Scratch))
}
}
@(private)
Multiply_Table :: [15]Addend_Group_Element // 0 = inf, which is implicit.
@(private)
mul_tbl_set :: proc "contextless" (
tbl: ^Multiply_Table,
ge: ^Group_Element,
tmp_add: ^Add_Scratch,
) {
tmp: Group_Element = ---
ge_set(&tmp, ge)
ge_addend_set(&tbl[0], ge)
for i := 1; i < 15; i = i + 1 {
ge_add_addend(&tmp, &tmp, &tbl[0], tmp_add)
ge_addend_set(&tbl[i], &tmp)
}
ge_clear(&tmp)
}
@(private)
mul_tbl_add :: proc "contextless" (
ge: ^Group_Element,
tbl: ^Multiply_Table,
idx: u64,
tmp_add: ^Add_Scratch,
tmp_addend: ^Addend_Group_Element,
unsafe_is_vartime: bool,
) {
// Variable time lookup, with the addition omitted entirely if idx == 0.
if unsafe_is_vartime {
// Skip adding the point at infinity.
if idx != 0 {
ge_add_addend(ge, ge, &tbl[idx - 1], tmp_add)
}
return
}
// Constant time lookup.
tmp_addend^ = {
// Point at infinity (0, 1, 1, 0) in precomputed form
{1, 0, 0, 0, 0}, // y - x
{1, 0, 0, 0, 0}, // y + x
{0, 0, 0, 0, 0}, // t * 2d
{2, 0, 0, 0, 0}, // z * 2
}
for i := u64(1); i < 16; i = i + 1 {
_, ctrl := bits.sub_u64(0, (i ~ idx), 0)
ge_addend_conditional_assign(tmp_addend, &tbl[i - 1], int(~ctrl) & 1)
}
ge_add_addend(ge, ge, tmp_addend, tmp_add)
}
+2 -2
View File
@@ -9,7 +9,7 @@ package fiat
u1 :: distinct u8
i1 :: distinct i8
@(optimization_mode="none")
@(optimization_mode = "none")
cmovznz_u64 :: proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) {
x1 := (u64(arg1) * 0xffffffffffffffff)
x2 := ((x1 & arg3) | ((~x1) & arg2))
@@ -17,7 +17,7 @@ cmovznz_u64 :: proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) {
return
}
@(optimization_mode="none")
@(optimization_mode = "none")
cmovznz_u32 :: proc "contextless" (arg1: u1, arg2, arg3: u32) -> (out1: u32) {
x1 := (u32(arg1) * 0xffffffff)
x2 := ((x1 & arg3) | ((~x1) & arg2))
+171 -42
View File
@@ -3,14 +3,32 @@ package field_curve25519
import "core:crypto"
import "core:mem"
fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element {
fe_relax_cast :: #force_inline proc "contextless" (
arg1: ^Tight_Field_Element,
) -> ^Loose_Field_Element {
return transmute(^Loose_Field_Element)(arg1)
}
fe_tighten_cast :: #force_inline proc "contextless" (arg1: ^Loose_Field_Element) -> ^Tight_Field_Element {
fe_tighten_cast :: #force_inline proc "contextless" (
arg1: ^Loose_Field_Element,
) -> ^Tight_Field_Element {
return transmute(^Tight_Field_Element)(arg1)
}
fe_clear :: proc "contextless" (
arg1: $T,
) where T == ^Tight_Field_Element || T == ^Loose_Field_Element {
mem.zero_explicit(arg1, size_of(arg1^))
}
fe_clear_vec :: proc "contextless" (
arg1: $T,
) where T == []^Tight_Field_Element || T == []^Loose_Field_Element {
for fe in arg1 {
fe_clear(fe)
}
}
fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte) {
// Ignore the unused bit by copying the input and masking the bit off
// prior to deserialization.
@@ -23,12 +41,25 @@ fe_from_bytes :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^[32]byte
mem.zero_explicit(&tmp1, size_of(tmp1))
}
fe_is_negative :: proc "contextless" (arg1: ^Tight_Field_Element) -> int {
tmp1: [32]byte = ---
fe_to_bytes(&tmp1, arg1)
ret := tmp1[0] & 1
mem.zero_explicit(&tmp1, size_of(tmp1))
return int(ret)
}
fe_equal :: proc "contextless" (arg1, arg2: ^Tight_Field_Element) -> int {
tmp2: [32]byte = ---
tmp1, tmp2: [32]byte = ---, ---
fe_to_bytes(&tmp1, arg1)
fe_to_bytes(&tmp2, arg2)
ret := fe_equal_bytes(arg1, &tmp2)
ret := crypto.compare_constant_time(tmp1[:], tmp2[:])
mem.zero_explicit(&tmp1, size_of(tmp1))
mem.zero_explicit(&tmp2, size_of(tmp2))
return ret
@@ -46,7 +77,11 @@ fe_equal_bytes :: proc "contextless" (arg1: ^Tight_Field_Element, arg2: ^[32]byt
return ret
}
fe_carry_pow2k :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element, arg2: uint) {
fe_carry_pow2k :: proc "contextless" (
out1: ^Tight_Field_Element,
arg1: ^Loose_Field_Element,
arg2: uint,
) {
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
if arg2 == 0 {
fe_one(out1)
@@ -54,27 +89,46 @@ fe_carry_pow2k :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element,
}
fe_carry_square(out1, arg1)
for _ in 1..<arg2 {
for _ in 1 ..< arg2 {
fe_carry_square(out1, fe_relax_cast(out1))
}
}
fe_carry_add :: #force_inline proc "contextless" (out1, arg1, arg2: ^Tight_Field_Element) {
fe_add(fe_relax_cast(out1), arg1, arg2)
fe_carry(out1, fe_relax_cast(out1))
}
fe_carry_sub :: #force_inline proc "contextless" (out1, arg1, arg2: ^Tight_Field_Element) {
fe_sub(fe_relax_cast(out1), arg1, arg2)
fe_carry(out1, fe_relax_cast(out1))
}
fe_carry_opp :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
fe_opp(fe_relax_cast(out1), arg1)
fe_carry(out1, fe_relax_cast(out1))
}
fe_carry_invsqrt :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) -> int {
// Inverse square root taken from Monocypher.
fe_carry_abs :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
fe_cond_negate(out1, arg1, fe_is_negative(arg1))
}
fe_carry_sqrt_ratio_m1 :: proc "contextless" (
out1: ^Tight_Field_Element,
arg1: ^Loose_Field_Element, // u
arg2: ^Loose_Field_Element, // v
) -> int {
// SQRT_RATIO_M1(u, v) from RFC 9496 - 4.2, based on the inverse
// square root from Monocypher.
w: Tight_Field_Element = ---
fe_carry_mul(&w, arg1, arg2) // u * v
// r = tmp1 = u * w^((p-5)/8)
tmp1, tmp2, tmp3: Tight_Field_Element = ---, ---, ---
// t0 = x^((p-5)/8)
// Can be achieved with a simple double & add ladder,
// but it would be slower.
fe_carry_pow2k(&tmp1, arg1, 1)
fe_carry_pow2k(&tmp1, fe_relax_cast(&w), 1)
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp1), 2)
fe_carry_mul(&tmp2, arg1, fe_relax_cast(&tmp2))
fe_carry_mul(&tmp2, fe_relax_cast(&w), fe_relax_cast(&tmp2))
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), fe_relax_cast(&tmp2))
fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 1)
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
@@ -93,46 +147,121 @@ fe_carry_invsqrt :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element
fe_carry_pow2k(&tmp2, fe_relax_cast(&tmp2), 50)
fe_carry_mul(&tmp1, fe_relax_cast(&tmp2), fe_relax_cast(&tmp1))
fe_carry_pow2k(&tmp1, fe_relax_cast(&tmp1), 2)
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), arg1)
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), fe_relax_cast(&w)) // w^((p-5)/8)
// quartic = x^((p-1)/4)
quartic := &tmp2
fe_carry_square(quartic, fe_relax_cast(&tmp1))
fe_carry_mul(quartic, fe_relax_cast(quartic), arg1)
fe_carry_mul(&tmp1, fe_relax_cast(&tmp1), arg1) // u * w^((p-5)/8)
// Serialize quartic once to save on repeated serialization/sanitization.
quartic_buf: [32]byte = ---
fe_to_bytes(&quartic_buf, quartic)
check := &tmp3
// Serialize `check` once to save on repeated serialization.
r, check := &tmp1, &tmp2
b: [32]byte = ---
fe_carry_square(check, fe_relax_cast(r))
fe_carry_mul(check, fe_relax_cast(check), arg2) // check * v
fe_to_bytes(&b, check)
fe_one(check)
p1 := fe_equal_bytes(check, &quartic_buf)
fe_carry_opp(check, check)
m1 := fe_equal_bytes(check, &quartic_buf)
fe_carry_opp(check, &SQRT_M1)
ms := fe_equal_bytes(check, &quartic_buf)
u, neg_u, neg_u_i := &tmp3, &w, check
fe_carry(u, arg1)
fe_carry_opp(neg_u, u)
fe_carry_mul(neg_u_i, fe_relax_cast(neg_u), fe_relax_cast(&FE_SQRT_M1))
// if quartic == -1 or sqrt(-1)
// then isr = x^((p-1)/4) * sqrt(-1)
// else isr = x^((p-1)/4)
fe_carry_mul(out1, fe_relax_cast(&tmp1), fe_relax_cast(&SQRT_M1))
fe_cond_assign(out1, &tmp1, (m1|ms) ~ 1)
correct_sign_sqrt := fe_equal_bytes(u, &b)
flipped_sign_sqrt := fe_equal_bytes(neg_u, &b)
flipped_sign_sqrt_i := fe_equal_bytes(neg_u_i, &b)
mem.zero_explicit(&tmp1, size_of(tmp1))
mem.zero_explicit(&tmp2, size_of(tmp2))
mem.zero_explicit(&tmp3, size_of(tmp3))
mem.zero_explicit(&quartic_buf, size_of(quartic_buf))
r_prime := check
fe_carry_mul(r_prime, fe_relax_cast(r), fe_relax_cast(&FE_SQRT_M1))
fe_cond_assign(r, r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i)
return p1 | m1
// Pick the non-negative square root.
fe_carry_abs(out1, r)
fe_clear_vec([]^Tight_Field_Element{&w, &tmp1, &tmp2, &tmp3})
mem.zero_explicit(&b, size_of(b))
return correct_sign_sqrt | flipped_sign_sqrt
}
fe_carry_inv :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
fe_carry_inv :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
tmp1: Tight_Field_Element
fe_carry_square(&tmp1, arg1)
_ = fe_carry_invsqrt(&tmp1, fe_relax_cast(&tmp1))
_ = fe_carry_sqrt_ratio_m1(&tmp1, fe_relax_cast(&FE_ONE), fe_relax_cast(&tmp1))
fe_carry_square(&tmp1, fe_relax_cast(&tmp1))
fe_carry_mul(out1, fe_relax_cast(&tmp1), arg1)
mem.zero_explicit(&tmp1, size_of(tmp1))
fe_clear(&tmp1)
}
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 0
out1[1] = 0
out1[2] = 0
out1[3] = 0
out1[4] = 0
}
fe_one :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 1
out1[1] = 0
out1[2] = 0
out1[3] = 0
out1[4] = 0
}
fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) {
x1 := arg1[0]
x2 := arg1[1]
x3 := arg1[2]
x4 := arg1[3]
x5 := arg1[4]
out1[0] = x1
out1[1] = x2
out1[2] = x3
out1[3] = x4
out1[4] = x5
}
@(optimization_mode = "none")
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) {
mask := (u64(arg1) * 0xffffffffffffffff)
x := (out1[0] ~ out2[0]) & mask
x1, y1 := out1[0] ~ x, out2[0] ~ x
x = (out1[1] ~ out2[1]) & mask
x2, y2 := out1[1] ~ x, out2[1] ~ x
x = (out1[2] ~ out2[2]) & mask
x3, y3 := out1[2] ~ x, out2[2] ~ x
x = (out1[3] ~ out2[3]) & mask
x4, y4 := out1[3] ~ x, out2[3] ~ x
x = (out1[4] ~ out2[4]) & mask
x5, y5 := out1[4] ~ x, out2[4] ~ x
out1[0], out2[0] = x1, y1
out1[1], out2[1] = x2, y2
out1[2], out2[2] = x3, y3
out1[3], out2[3] = x4, y4
out1[4], out2[4] = x5, y5
}
@(optimization_mode = "none")
fe_cond_select :: #force_no_inline proc "contextless" (
out1, arg1, arg2: $T,
arg3: int,
) where T == ^Tight_Field_Element || T == ^Loose_Field_Element {
mask := (u64(arg3) * 0xffffffffffffffff)
x1 := ((mask & arg2[0]) | ((~mask) & arg1[0]))
x2 := ((mask & arg2[1]) | ((~mask) & arg1[1]))
x3 := ((mask & arg2[2]) | ((~mask) & arg1[2]))
x4 := ((mask & arg2[3]) | ((~mask) & arg1[3]))
x5 := ((mask & arg2[4]) | ((~mask) & arg1[4]))
out1[0] = x1
out1[1] = x2
out1[2] = x3
out1[3] = x4
out1[4] = x5
}
fe_cond_negate :: proc "contextless" (out1, arg1: ^Tight_Field_Element, ctrl: int) {
tmp1: Tight_Field_Element = ---
fe_carry_opp(&tmp1, arg1)
fe_cond_select(out1, arg1, &tmp1, ctrl)
fe_clear(&tmp1)
}
+29 -61
View File
@@ -30,8 +30,6 @@ package field_curve25519
//
// While the base implementation is provably correct, this implementation
// makes no such claims as the port and optimizations were done by hand.
// At some point, it may be worth adding support to fiat-crypto for
// generating Odin output.
//
// TODO:
// * When fiat-crypto supports it, using a saturated 64-bit limbs
@@ -44,7 +42,10 @@ import "core:math/bits"
Loose_Field_Element :: distinct [5]u64
Tight_Field_Element :: distinct [5]u64
SQRT_M1 := Tight_Field_Element{
FE_ZERO := Tight_Field_Element{0, 0, 0, 0, 0}
FE_ONE := Tight_Field_Element{1, 0, 0, 0, 0}
FE_SQRT_M1 := Tight_Field_Element {
1718705420411056,
234908883556509,
2233514472574048,
@@ -52,7 +53,13 @@ SQRT_M1 := Tight_Field_Element{
765476049583133,
}
_addcarryx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
_addcarryx_u51 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((u64(arg1) + arg2) + arg3)
x2 := (x1 & 0x7ffffffffffff)
x3 := fiat.u1((x1 >> 51))
@@ -61,7 +68,13 @@ _addcarryx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u
return
}
_subborrowx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
_subborrowx_u51 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
x2 := fiat.i1((x1 >> 51))
x3 := (u64(x1) & 0x7ffffffffffff)
@@ -70,7 +83,7 @@ _subborrowx_u51 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3:
return
}
fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
fe_carry_mul :: proc "contextless" (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
x2, x1 := bits.mul_u64(arg1[4], (arg2[4] * 0x13))
x4, x3 := bits.mul_u64(arg1[4], (arg2[3] * 0x13))
x6, x5 := bits.mul_u64(arg1[4], (arg2[2] * 0x13))
@@ -169,7 +182,7 @@ fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Eleme
out1[4] = x152
}
fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
fe_carry_square :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
x1 := (arg1[4] * 0x13)
x2 := (x1 * 0x2)
x3 := (arg1[4] * 0x2)
@@ -305,8 +318,11 @@ fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Ele
out1[4] = x5
}
@(optimization_mode="none")
fe_cond_assign :: #force_no_inline proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: int) {
@(optimization_mode = "none")
fe_cond_assign :: #force_no_inline proc "contextless" (
out1, arg1: ^Tight_Field_Element,
arg2: int,
) {
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
@@ -527,7 +543,10 @@ fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_E
out1[4] = x5
}
fe_carry_scmul_121666 :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
fe_carry_scmul_121666 :: proc "contextless" (
out1: ^Tight_Field_Element,
arg1: ^Loose_Field_Element,
) {
x2, x1 := bits.mul_u64(0x1db42, arg1[4])
x4, x3 := bits.mul_u64(0x1db42, arg1[3])
x6, x5 := bits.mul_u64(0x1db42, arg1[2])
@@ -565,54 +584,3 @@ fe_carry_scmul_121666 :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_El
out1[3] = x27
out1[4] = x32
}
// The following routines were added by hand, and do not come from fiat-crypto.
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 0
out1[1] = 0
out1[2] = 0
out1[3] = 0
out1[4] = 0
}
fe_one :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 1
out1[1] = 0
out1[2] = 0
out1[3] = 0
out1[4] = 0
}
fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) {
x1 := arg1[0]
x2 := arg1[1]
x3 := arg1[2]
x4 := arg1[3]
x5 := arg1[4]
out1[0] = x1
out1[1] = x2
out1[2] = x3
out1[3] = x4
out1[4] = x5
}
@(optimization_mode="none")
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) {
mask := -u64(arg1)
x := (out1[0] ~ out2[0]) & mask
x1, y1 := out1[0] ~ x, out2[0] ~ x
x = (out1[1] ~ out2[1]) & mask
x2, y2 := out1[1] ~ x, out2[1] ~ x
x = (out1[2] ~ out2[2]) & mask
x3, y3 := out1[2] ~ x, out2[2] ~ x
x = (out1[3] ~ out2[3]) & mask
x4, y4 := out1[3] ~ x, out2[3] ~ x
x = (out1[4] ~ out2[4]) & mask
x5, y5 := out1[4] ~ x, out2[4] ~ x
out1[0], out2[0] = x1, y1
out1[1], out2[1] = x2, y2
out1[2], out2[2] = x3, y3
out1[3], out2[3] = x4, y4
out1[4], out2[4] = x5, y5
}
+47 -4
View File
@@ -1,17 +1,26 @@
package field_poly1305
import "base:intrinsics"
import "core:encoding/endian"
import "core:mem"
fe_relax_cast :: #force_inline proc "contextless" (arg1: ^Tight_Field_Element) -> ^Loose_Field_Element {
fe_relax_cast :: #force_inline proc "contextless" (
arg1: ^Tight_Field_Element,
) -> ^Loose_Field_Element {
return transmute(^Loose_Field_Element)(arg1)
}
fe_tighten_cast :: #force_inline proc "contextless" (arg1: ^Loose_Field_Element) -> ^Tight_Field_Element {
fe_tighten_cast :: #force_inline proc "contextless" (
arg1: ^Loose_Field_Element,
) -> ^Tight_Field_Element {
return transmute(^Tight_Field_Element)(arg1)
}
fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, arg2: byte) {
fe_from_bytes :: #force_inline proc "contextless" (
out1: ^Tight_Field_Element,
arg1: []byte,
arg2: byte,
) {
// fiat-crypto's deserialization routine effectively processes a
// single byte at a time, and wants 256-bits of input for a value
// that will be 128-bits or 129-bits.
@@ -20,7 +29,9 @@ fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, a
// makes implementing the actual MAC block processing considerably
// neater.
assert(len(arg1) == 16)
if len(arg1) != 16 {
intrinsics.trap()
}
// While it may be unwise to do deserialization here on our
// own when fiat-crypto provides equivalent functionality,
@@ -51,3 +62,35 @@ fe_from_u64s :: proc "contextless" (out1: ^Tight_Field_Element, lo, hi: u64) {
// This routine is only used to deserialize `r` which is confidential.
mem.zero_explicit(&tmp, size_of(tmp))
}
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 0
out1[1] = 0
out1[2] = 0
}
fe_set :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
x1 := arg1[0]
x2 := arg1[1]
x3 := arg1[2]
out1[0] = x1
out1[1] = x2
out1[2] = x3
}
@(optimization_mode = "none")
fe_cond_swap :: #force_no_inline proc "contextless" (
out1, out2: ^Tight_Field_Element,
arg1: bool,
) {
mask := (u64(arg1) * 0xffffffffffffffff)
x := (out1[0] ~ out2[0]) & mask
x1, y1 := out1[0] ~ x, out2[0] ~ x
x = (out1[1] ~ out2[1]) & mask
x2, y2 := out1[1] ~ x, out2[1] ~ x
x = (out1[2] ~ out2[2]) & mask
x3, y3 := out1[2] ~ x, out2[2] ~ x
out1[0], out2[0] = x1, y1
out1[1], out2[1] = x2, y2
out1[2], out2[2] = x3, y3
}
+35 -39
View File
@@ -39,7 +39,13 @@ import "core:math/bits"
Loose_Field_Element :: distinct [3]u64
Tight_Field_Element :: distinct [3]u64
_addcarryx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
_addcarryx_u44 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((u64(arg1) + arg2) + arg3)
x2 := (x1 & 0xfffffffffff)
x3 := fiat.u1((x1 >> 44))
@@ -48,7 +54,13 @@ _addcarryx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u
return
}
_subborrowx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
_subborrowx_u44 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
x2 := fiat.i1((x1 >> 44))
x3 := (u64(x1) & 0xfffffffffff)
@@ -57,7 +69,13 @@ _subborrowx_u44 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3:
return
}
_addcarryx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
_addcarryx_u43 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((u64(arg1) + arg2) + arg3)
x2 := (x1 & 0x7ffffffffff)
x3 := fiat.u1((x1 >> 43))
@@ -66,7 +84,13 @@ _addcarryx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u
return
}
_subborrowx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3: u64) -> (out1: u64, out2: fiat.u1) {
_subborrowx_u43 :: #force_inline proc "contextless" (
arg1: fiat.u1,
arg2, arg3: u64,
) -> (
out1: u64,
out2: fiat.u1,
) {
x1 := ((i64(arg2) - i64(arg1)) - i64(arg3))
x2 := fiat.i1((x1 >> 43))
x3 := (u64(x1) & 0x7ffffffffff)
@@ -75,7 +99,7 @@ _subborrowx_u43 :: #force_inline proc "contextless" (arg1: fiat.u1, arg2, arg3:
return
}
fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
fe_carry_mul :: proc "contextless" (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Element) {
x2, x1 := bits.mul_u64(arg1[2], (arg2[2] * 0x5))
x4, x3 := bits.mul_u64(arg1[2], (arg2[1] * 0xa))
x6, x5 := bits.mul_u64(arg1[1], (arg2[2] * 0xa))
@@ -120,7 +144,7 @@ fe_carry_mul :: proc (out1: ^Tight_Field_Element, arg1, arg2: ^Loose_Field_Eleme
out1[2] = x62
}
fe_carry_square :: proc (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
fe_carry_square :: proc "contextless" (out1: ^Tight_Field_Element, arg1: ^Loose_Field_Element) {
x1 := (arg1[2] * 0x5)
x2 := (x1 * 0x2)
x3 := (arg1[2] * 0x2)
@@ -201,8 +225,11 @@ fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Ele
out1[2] = x3
}
@(optimization_mode="none")
fe_cond_assign :: #force_no_inline proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: bool) {
@(optimization_mode = "none")
fe_cond_assign :: #force_no_inline proc "contextless" (
out1, arg1: ^Tight_Field_Element,
arg2: bool,
) {
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
@@ -325,34 +352,3 @@ fe_relax :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_E
out1[1] = x2
out1[2] = x3
}
// The following routines were added by hand, and do not come from fiat-crypto.
fe_zero :: proc "contextless" (out1: ^Tight_Field_Element) {
out1[0] = 0
out1[1] = 0
out1[2] = 0
}
fe_set :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
x1 := arg1[0]
x2 := arg1[1]
x3 := arg1[2]
out1[0] = x1
out1[1] = x2
out1[2] = x3
}
@(optimization_mode="none")
fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: bool) {
mask := -u64(arg1)
x := (out1[0] ~ out2[0]) & mask
x1, y1 := out1[0] ~ x, out2[0] ~ x
x = (out1[1] ~ out2[1]) & mask
x2, y2 := out1[1] ~ x, out2[1] ~ x
x = (out1[2] ~ out2[2]) & mask
x3, y3 := out1[2] ~ x, out2[2] ~ x
out1[0], out2[0] = x1, y1
out1[1], out2[1] = x2, y2
out1[2], out2[2] = x3, y3
}
@@ -0,0 +1,153 @@
package field_scalar25519
import "base:intrinsics"
import "core:encoding/endian"
import "core:math/bits"
import "core:mem"
@(private)
_TWO_168 := Montgomery_Domain_Field_Element {
0x5b8ab432eac74798,
0x38afddd6de59d5d7,
0xa2c131b399411b7c,
0x6329a7ed9ce5a30,
}
@(private)
_TWO_336 := Montgomery_Domain_Field_Element {
0xbd3d108e2b35ecc5,
0x5c3a3718bdf9c90b,
0x63aa97a331b4f2ee,
0x3d217f5be65cb5c,
}
fe_clear :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) {
mem.zero_explicit(arg1, size_of(Montgomery_Domain_Field_Element))
}
fe_from_bytes :: proc "contextless" (
out1: ^Montgomery_Domain_Field_Element,
arg1: ^[32]byte,
unsafe_assume_canonical := false,
) -> bool {
tmp := Non_Montgomery_Domain_Field_Element {
endian.unchecked_get_u64le(arg1[0:]),
endian.unchecked_get_u64le(arg1[8:]),
endian.unchecked_get_u64le(arg1[16:]),
endian.unchecked_get_u64le(arg1[24:]),
}
defer mem.zero_explicit(&tmp, size_of(tmp))
// Check that tmp is in the the range [0, ELL).
if !unsafe_assume_canonical {
_, borrow := bits.sub_u64(ELL[0] - 1, tmp[0], 0)
_, borrow = bits.sub_u64(ELL[1], tmp[1], borrow)
_, borrow = bits.sub_u64(ELL[2], tmp[2], borrow)
_, borrow = bits.sub_u64(ELL[3], tmp[3], borrow)
if borrow != 0 {
return false
}
}
fe_to_montgomery(out1, &tmp)
return true
}
fe_from_bytes_rfc8032 :: proc "contextless" (
out1: ^Montgomery_Domain_Field_Element,
arg1: ^[32]byte,
) {
tmp: [64]byte
copy(tmp[:], arg1[:])
// Apply "clamping" as in RFC 8032.
tmp[0] &= 248
tmp[31] &= 127
tmp[31] |= 64 // Sets the 254th bit, so the encoding is non-canonical.
fe_from_bytes_wide(out1, &tmp)
mem.zero_explicit(&tmp, size_of(tmp))
}
fe_from_bytes_wide :: proc "contextless" (
out1: ^Montgomery_Domain_Field_Element,
arg1: ^[64]byte,
) {
tmp: Montgomery_Domain_Field_Element
// Use Frank Denis' trick, as documented by Filippo Valsorda
// at https://words.filippo.io/dispatches/wide-reduction/
//
// x = c * 2^336 + b * 2^168 + a mod l
_fe_from_bytes_short(out1, arg1[:21]) // a
_fe_from_bytes_short(&tmp, arg1[21:42]) // b
fe_mul(&tmp, &tmp, &_TWO_168) // b * 2^168
fe_add(out1, out1, &tmp) // a + b * 2^168
_fe_from_bytes_short(&tmp, arg1[42:]) // c
fe_mul(&tmp, &tmp, &_TWO_336) // c * 2^336
fe_add(out1, out1, &tmp) // a + b * 2^168 + c * 2^336
fe_clear(&tmp)
}
@(private)
_fe_from_bytes_short :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element, arg1: []byte) {
// INVARIANT: len(arg1) < 32.
if len(arg1) >= 32 {
intrinsics.trap()
}
tmp: [32]byte
copy(tmp[:], arg1)
_ = fe_from_bytes(out1, &tmp, true)
mem.zero_explicit(&tmp, size_of(tmp))
}
fe_to_bytes :: proc "contextless" (out1: []byte, arg1: ^Montgomery_Domain_Field_Element) {
if len(out1) != 32 {
intrinsics.trap()
}
tmp: Non_Montgomery_Domain_Field_Element
fe_from_montgomery(&tmp, arg1)
endian.unchecked_put_u64le(out1[0:], tmp[0])
endian.unchecked_put_u64le(out1[8:], tmp[1])
endian.unchecked_put_u64le(out1[16:], tmp[2])
endian.unchecked_put_u64le(out1[24:], tmp[3])
mem.zero_explicit(&tmp, size_of(tmp))
}
fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> int {
tmp: Montgomery_Domain_Field_Element
fe_sub(&tmp, arg1, arg2)
// This will only underflow iff arg1 == arg2, and we return the borrow,
// which will be 1.
_, borrow := bits.sub_u64(fe_non_zero(&tmp), 1, 0)
fe_clear(&tmp)
return int(borrow)
}
fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
out1[0] = 0
out1[1] = 0
out1[2] = 0
out1[3] = 0
}
fe_set :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
x1 := arg1[0]
x2 := arg1[1]
x3 := arg1[2]
x4 := arg1[3]
out1[0] = x1
out1[1] = x2
out1[2] = x3
out1[3] = x4
}
@@ -0,0 +1,535 @@
// The BSD 1-Clause License (BSD-1-Clause)
//
// Copyright (c) 2015-2020 the fiat-crypto authors (see the AUTHORS file)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// THIS SOFTWARE IS PROVIDED BY the fiat-crypto authors "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design,
// Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package field_scalar25519
// The file provides arithmetic on the field Z/(2^252+27742317777372353535851937790883648493)
// using a 64-bit Montgomery form internal representation. It is derived
// primarily from the machine generated Golang output from the fiat-crypto
// project.
//
// While the base implementation is provably correct, this implementation
// makes no such claims as the port and optimizations were done by hand.
import fiat "core:crypto/_fiat"
import "core:math/bits"
// ELL is the saturated representation of the field order, least-significant
// limb first.
ELL :: [4]u64{0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0x0, 0x1000000000000000}
Montgomery_Domain_Field_Element :: distinct [4]u64
Non_Montgomery_Domain_Field_Element :: distinct [4]u64
fe_mul :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
x1 := arg1[1]
x2 := arg1[2]
x3 := arg1[3]
x4 := arg1[0]
x6, x5 := bits.mul_u64(x4, arg2[3])
x8, x7 := bits.mul_u64(x4, arg2[2])
x10, x9 := bits.mul_u64(x4, arg2[1])
x12, x11 := bits.mul_u64(x4, arg2[0])
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
x19 := (u64(fiat.u1(x18)) + x6)
_, x20 := bits.mul_u64(x11, 0xd2b51da312547e1b)
x23, x22 := bits.mul_u64(x20, 0x1000000000000000)
x25, x24 := bits.mul_u64(x20, 0x14def9dea2f79cd6)
x27, x26 := bits.mul_u64(x20, 0x5812631a5cf5d3ed)
x28, x29 := bits.add_u64(x27, x24, u64(0x0))
x30 := (u64(fiat.u1(x29)) + x25)
_, x32 := bits.add_u64(x11, x26, u64(0x0))
x33, x34 := bits.add_u64(x13, x28, u64(fiat.u1(x32)))
x35, x36 := bits.add_u64(x15, x30, u64(fiat.u1(x34)))
x37, x38 := bits.add_u64(x17, x22, u64(fiat.u1(x36)))
x39, x40 := bits.add_u64(x19, x23, u64(fiat.u1(x38)))
x42, x41 := bits.mul_u64(x1, arg2[3])
x44, x43 := bits.mul_u64(x1, arg2[2])
x46, x45 := bits.mul_u64(x1, arg2[1])
x48, x47 := bits.mul_u64(x1, arg2[0])
x49, x50 := bits.add_u64(x48, x45, u64(0x0))
x51, x52 := bits.add_u64(x46, x43, u64(fiat.u1(x50)))
x53, x54 := bits.add_u64(x44, x41, u64(fiat.u1(x52)))
x55 := (u64(fiat.u1(x54)) + x42)
x56, x57 := bits.add_u64(x33, x47, u64(0x0))
x58, x59 := bits.add_u64(x35, x49, u64(fiat.u1(x57)))
x60, x61 := bits.add_u64(x37, x51, u64(fiat.u1(x59)))
x62, x63 := bits.add_u64(x39, x53, u64(fiat.u1(x61)))
x64, x65 := bits.add_u64(u64(fiat.u1(x40)), x55, u64(fiat.u1(x63)))
_, x66 := bits.mul_u64(x56, 0xd2b51da312547e1b)
x69, x68 := bits.mul_u64(x66, 0x1000000000000000)
x71, x70 := bits.mul_u64(x66, 0x14def9dea2f79cd6)
x73, x72 := bits.mul_u64(x66, 0x5812631a5cf5d3ed)
x74, x75 := bits.add_u64(x73, x70, u64(0x0))
x76 := (u64(fiat.u1(x75)) + x71)
_, x78 := bits.add_u64(x56, x72, u64(0x0))
x79, x80 := bits.add_u64(x58, x74, u64(fiat.u1(x78)))
x81, x82 := bits.add_u64(x60, x76, u64(fiat.u1(x80)))
x83, x84 := bits.add_u64(x62, x68, u64(fiat.u1(x82)))
x85, x86 := bits.add_u64(x64, x69, u64(fiat.u1(x84)))
x87 := (u64(fiat.u1(x86)) + u64(fiat.u1(x65)))
x89, x88 := bits.mul_u64(x2, arg2[3])
x91, x90 := bits.mul_u64(x2, arg2[2])
x93, x92 := bits.mul_u64(x2, arg2[1])
x95, x94 := bits.mul_u64(x2, arg2[0])
x96, x97 := bits.add_u64(x95, x92, u64(0x0))
x98, x99 := bits.add_u64(x93, x90, u64(fiat.u1(x97)))
x100, x101 := bits.add_u64(x91, x88, u64(fiat.u1(x99)))
x102 := (u64(fiat.u1(x101)) + x89)
x103, x104 := bits.add_u64(x79, x94, u64(0x0))
x105, x106 := bits.add_u64(x81, x96, u64(fiat.u1(x104)))
x107, x108 := bits.add_u64(x83, x98, u64(fiat.u1(x106)))
x109, x110 := bits.add_u64(x85, x100, u64(fiat.u1(x108)))
x111, x112 := bits.add_u64(x87, x102, u64(fiat.u1(x110)))
_, x113 := bits.mul_u64(x103, 0xd2b51da312547e1b)
x116, x115 := bits.mul_u64(x113, 0x1000000000000000)
x118, x117 := bits.mul_u64(x113, 0x14def9dea2f79cd6)
x120, x119 := bits.mul_u64(x113, 0x5812631a5cf5d3ed)
x121, x122 := bits.add_u64(x120, x117, u64(0x0))
x123 := (u64(fiat.u1(x122)) + x118)
_, x125 := bits.add_u64(x103, x119, u64(0x0))
x126, x127 := bits.add_u64(x105, x121, u64(fiat.u1(x125)))
x128, x129 := bits.add_u64(x107, x123, u64(fiat.u1(x127)))
x130, x131 := bits.add_u64(x109, x115, u64(fiat.u1(x129)))
x132, x133 := bits.add_u64(x111, x116, u64(fiat.u1(x131)))
x134 := (u64(fiat.u1(x133)) + u64(fiat.u1(x112)))
x136, x135 := bits.mul_u64(x3, arg2[3])
x138, x137 := bits.mul_u64(x3, arg2[2])
x140, x139 := bits.mul_u64(x3, arg2[1])
x142, x141 := bits.mul_u64(x3, arg2[0])
x143, x144 := bits.add_u64(x142, x139, u64(0x0))
x145, x146 := bits.add_u64(x140, x137, u64(fiat.u1(x144)))
x147, x148 := bits.add_u64(x138, x135, u64(fiat.u1(x146)))
x149 := (u64(fiat.u1(x148)) + x136)
x150, x151 := bits.add_u64(x126, x141, u64(0x0))
x152, x153 := bits.add_u64(x128, x143, u64(fiat.u1(x151)))
x154, x155 := bits.add_u64(x130, x145, u64(fiat.u1(x153)))
x156, x157 := bits.add_u64(x132, x147, u64(fiat.u1(x155)))
x158, x159 := bits.add_u64(x134, x149, u64(fiat.u1(x157)))
_, x160 := bits.mul_u64(x150, 0xd2b51da312547e1b)
x163, x162 := bits.mul_u64(x160, 0x1000000000000000)
x165, x164 := bits.mul_u64(x160, 0x14def9dea2f79cd6)
x167, x166 := bits.mul_u64(x160, 0x5812631a5cf5d3ed)
x168, x169 := bits.add_u64(x167, x164, u64(0x0))
x170 := (u64(fiat.u1(x169)) + x165)
_, x172 := bits.add_u64(x150, x166, u64(0x0))
x173, x174 := bits.add_u64(x152, x168, u64(fiat.u1(x172)))
x175, x176 := bits.add_u64(x154, x170, u64(fiat.u1(x174)))
x177, x178 := bits.add_u64(x156, x162, u64(fiat.u1(x176)))
x179, x180 := bits.add_u64(x158, x163, u64(fiat.u1(x178)))
x181 := (u64(fiat.u1(x180)) + u64(fiat.u1(x159)))
x182, x183 := bits.sub_u64(x173, 0x5812631a5cf5d3ed, u64(0x0))
x184, x185 := bits.sub_u64(x175, 0x14def9dea2f79cd6, u64(fiat.u1(x183)))
x186, x187 := bits.sub_u64(x177, u64(0x0), u64(fiat.u1(x185)))
x188, x189 := bits.sub_u64(x179, 0x1000000000000000, u64(fiat.u1(x187)))
_, x191 := bits.sub_u64(x181, u64(0x0), u64(fiat.u1(x189)))
x192 := fiat.cmovznz_u64(fiat.u1(x191), x182, x173)
x193 := fiat.cmovznz_u64(fiat.u1(x191), x184, x175)
x194 := fiat.cmovznz_u64(fiat.u1(x191), x186, x177)
x195 := fiat.cmovznz_u64(fiat.u1(x191), x188, x179)
out1[0] = x192
out1[1] = x193
out1[2] = x194
out1[3] = x195
}
fe_square :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
x1 := arg1[1]
x2 := arg1[2]
x3 := arg1[3]
x4 := arg1[0]
x6, x5 := bits.mul_u64(x4, arg1[3])
x8, x7 := bits.mul_u64(x4, arg1[2])
x10, x9 := bits.mul_u64(x4, arg1[1])
x12, x11 := bits.mul_u64(x4, arg1[0])
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
x19 := (u64(fiat.u1(x18)) + x6)
_, x20 := bits.mul_u64(x11, 0xd2b51da312547e1b)
x23, x22 := bits.mul_u64(x20, 0x1000000000000000)
x25, x24 := bits.mul_u64(x20, 0x14def9dea2f79cd6)
x27, x26 := bits.mul_u64(x20, 0x5812631a5cf5d3ed)
x28, x29 := bits.add_u64(x27, x24, u64(0x0))
x30 := (u64(fiat.u1(x29)) + x25)
_, x32 := bits.add_u64(x11, x26, u64(0x0))
x33, x34 := bits.add_u64(x13, x28, u64(fiat.u1(x32)))
x35, x36 := bits.add_u64(x15, x30, u64(fiat.u1(x34)))
x37, x38 := bits.add_u64(x17, x22, u64(fiat.u1(x36)))
x39, x40 := bits.add_u64(x19, x23, u64(fiat.u1(x38)))
x42, x41 := bits.mul_u64(x1, arg1[3])
x44, x43 := bits.mul_u64(x1, arg1[2])
x46, x45 := bits.mul_u64(x1, arg1[1])
x48, x47 := bits.mul_u64(x1, arg1[0])
x49, x50 := bits.add_u64(x48, x45, u64(0x0))
x51, x52 := bits.add_u64(x46, x43, u64(fiat.u1(x50)))
x53, x54 := bits.add_u64(x44, x41, u64(fiat.u1(x52)))
x55 := (u64(fiat.u1(x54)) + x42)
x56, x57 := bits.add_u64(x33, x47, u64(0x0))
x58, x59 := bits.add_u64(x35, x49, u64(fiat.u1(x57)))
x60, x61 := bits.add_u64(x37, x51, u64(fiat.u1(x59)))
x62, x63 := bits.add_u64(x39, x53, u64(fiat.u1(x61)))
x64, x65 := bits.add_u64(u64(fiat.u1(x40)), x55, u64(fiat.u1(x63)))
_, x66 := bits.mul_u64(x56, 0xd2b51da312547e1b)
x69, x68 := bits.mul_u64(x66, 0x1000000000000000)
x71, x70 := bits.mul_u64(x66, 0x14def9dea2f79cd6)
x73, x72 := bits.mul_u64(x66, 0x5812631a5cf5d3ed)
x74, x75 := bits.add_u64(x73, x70, u64(0x0))
x76 := (u64(fiat.u1(x75)) + x71)
_, x78 := bits.add_u64(x56, x72, u64(0x0))
x79, x80 := bits.add_u64(x58, x74, u64(fiat.u1(x78)))
x81, x82 := bits.add_u64(x60, x76, u64(fiat.u1(x80)))
x83, x84 := bits.add_u64(x62, x68, u64(fiat.u1(x82)))
x85, x86 := bits.add_u64(x64, x69, u64(fiat.u1(x84)))
x87 := (u64(fiat.u1(x86)) + u64(fiat.u1(x65)))
x89, x88 := bits.mul_u64(x2, arg1[3])
x91, x90 := bits.mul_u64(x2, arg1[2])
x93, x92 := bits.mul_u64(x2, arg1[1])
x95, x94 := bits.mul_u64(x2, arg1[0])
x96, x97 := bits.add_u64(x95, x92, u64(0x0))
x98, x99 := bits.add_u64(x93, x90, u64(fiat.u1(x97)))
x100, x101 := bits.add_u64(x91, x88, u64(fiat.u1(x99)))
x102 := (u64(fiat.u1(x101)) + x89)
x103, x104 := bits.add_u64(x79, x94, u64(0x0))
x105, x106 := bits.add_u64(x81, x96, u64(fiat.u1(x104)))
x107, x108 := bits.add_u64(x83, x98, u64(fiat.u1(x106)))
x109, x110 := bits.add_u64(x85, x100, u64(fiat.u1(x108)))
x111, x112 := bits.add_u64(x87, x102, u64(fiat.u1(x110)))
_, x113 := bits.mul_u64(x103, 0xd2b51da312547e1b)
x116, x115 := bits.mul_u64(x113, 0x1000000000000000)
x118, x117 := bits.mul_u64(x113, 0x14def9dea2f79cd6)
x120, x119 := bits.mul_u64(x113, 0x5812631a5cf5d3ed)
x121, x122 := bits.add_u64(x120, x117, u64(0x0))
x123 := (u64(fiat.u1(x122)) + x118)
_, x125 := bits.add_u64(x103, x119, u64(0x0))
x126, x127 := bits.add_u64(x105, x121, u64(fiat.u1(x125)))
x128, x129 := bits.add_u64(x107, x123, u64(fiat.u1(x127)))
x130, x131 := bits.add_u64(x109, x115, u64(fiat.u1(x129)))
x132, x133 := bits.add_u64(x111, x116, u64(fiat.u1(x131)))
x134 := (u64(fiat.u1(x133)) + u64(fiat.u1(x112)))
x136, x135 := bits.mul_u64(x3, arg1[3])
x138, x137 := bits.mul_u64(x3, arg1[2])
x140, x139 := bits.mul_u64(x3, arg1[1])
x142, x141 := bits.mul_u64(x3, arg1[0])
x143, x144 := bits.add_u64(x142, x139, u64(0x0))
x145, x146 := bits.add_u64(x140, x137, u64(fiat.u1(x144)))
x147, x148 := bits.add_u64(x138, x135, u64(fiat.u1(x146)))
x149 := (u64(fiat.u1(x148)) + x136)
x150, x151 := bits.add_u64(x126, x141, u64(0x0))
x152, x153 := bits.add_u64(x128, x143, u64(fiat.u1(x151)))
x154, x155 := bits.add_u64(x130, x145, u64(fiat.u1(x153)))
x156, x157 := bits.add_u64(x132, x147, u64(fiat.u1(x155)))
x158, x159 := bits.add_u64(x134, x149, u64(fiat.u1(x157)))
_, x160 := bits.mul_u64(x150, 0xd2b51da312547e1b)
x163, x162 := bits.mul_u64(x160, 0x1000000000000000)
x165, x164 := bits.mul_u64(x160, 0x14def9dea2f79cd6)
x167, x166 := bits.mul_u64(x160, 0x5812631a5cf5d3ed)
x168, x169 := bits.add_u64(x167, x164, u64(0x0))
x170 := (u64(fiat.u1(x169)) + x165)
_, x172 := bits.add_u64(x150, x166, u64(0x0))
x173, x174 := bits.add_u64(x152, x168, u64(fiat.u1(x172)))
x175, x176 := bits.add_u64(x154, x170, u64(fiat.u1(x174)))
x177, x178 := bits.add_u64(x156, x162, u64(fiat.u1(x176)))
x179, x180 := bits.add_u64(x158, x163, u64(fiat.u1(x178)))
x181 := (u64(fiat.u1(x180)) + u64(fiat.u1(x159)))
x182, x183 := bits.sub_u64(x173, 0x5812631a5cf5d3ed, u64(0x0))
x184, x185 := bits.sub_u64(x175, 0x14def9dea2f79cd6, u64(fiat.u1(x183)))
x186, x187 := bits.sub_u64(x177, u64(0x0), u64(fiat.u1(x185)))
x188, x189 := bits.sub_u64(x179, 0x1000000000000000, u64(fiat.u1(x187)))
_, x191 := bits.sub_u64(x181, u64(0x0), u64(fiat.u1(x189)))
x192 := fiat.cmovznz_u64(fiat.u1(x191), x182, x173)
x193 := fiat.cmovznz_u64(fiat.u1(x191), x184, x175)
x194 := fiat.cmovznz_u64(fiat.u1(x191), x186, x177)
x195 := fiat.cmovznz_u64(fiat.u1(x191), x188, x179)
out1[0] = x192
out1[1] = x193
out1[2] = x194
out1[3] = x195
}
fe_add :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
x1, x2 := bits.add_u64(arg1[0], arg2[0], u64(0x0))
x3, x4 := bits.add_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
x5, x6 := bits.add_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
x7, x8 := bits.add_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
x9, x10 := bits.sub_u64(x1, 0x5812631a5cf5d3ed, u64(0x0))
x11, x12 := bits.sub_u64(x3, 0x14def9dea2f79cd6, u64(fiat.u1(x10)))
x13, x14 := bits.sub_u64(x5, u64(0x0), u64(fiat.u1(x12)))
x15, x16 := bits.sub_u64(x7, 0x1000000000000000, u64(fiat.u1(x14)))
_, x18 := bits.sub_u64(u64(fiat.u1(x8)), u64(0x0), u64(fiat.u1(x16)))
x19 := fiat.cmovznz_u64(fiat.u1(x18), x9, x1)
x20 := fiat.cmovznz_u64(fiat.u1(x18), x11, x3)
x21 := fiat.cmovznz_u64(fiat.u1(x18), x13, x5)
x22 := fiat.cmovznz_u64(fiat.u1(x18), x15, x7)
out1[0] = x19
out1[1] = x20
out1[2] = x21
out1[3] = x22
}
fe_sub :: proc "contextless" (out1, arg1, arg2: ^Montgomery_Domain_Field_Element) {
x1, x2 := bits.sub_u64(arg1[0], arg2[0], u64(0x0))
x3, x4 := bits.sub_u64(arg1[1], arg2[1], u64(fiat.u1(x2)))
x5, x6 := bits.sub_u64(arg1[2], arg2[2], u64(fiat.u1(x4)))
x7, x8 := bits.sub_u64(arg1[3], arg2[3], u64(fiat.u1(x6)))
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
x10, x11 := bits.add_u64(x1, (x9 & 0x5812631a5cf5d3ed), u64(0x0))
x12, x13 := bits.add_u64(x3, (x9 & 0x14def9dea2f79cd6), u64(fiat.u1(x11)))
x14, x15 := bits.add_u64(x5, u64(0x0), u64(fiat.u1(x13)))
x16, _ := bits.add_u64(x7, (x9 & 0x1000000000000000), u64(fiat.u1(x15)))
out1[0] = x10
out1[1] = x12
out1[2] = x14
out1[3] = x16
}
fe_opp :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
x1, x2 := bits.sub_u64(u64(0x0), arg1[0], u64(0x0))
x3, x4 := bits.sub_u64(u64(0x0), arg1[1], u64(fiat.u1(x2)))
x5, x6 := bits.sub_u64(u64(0x0), arg1[2], u64(fiat.u1(x4)))
x7, x8 := bits.sub_u64(u64(0x0), arg1[3], u64(fiat.u1(x6)))
x9 := fiat.cmovznz_u64(fiat.u1(x8), u64(0x0), 0xffffffffffffffff)
x10, x11 := bits.add_u64(x1, (x9 & 0x5812631a5cf5d3ed), u64(0x0))
x12, x13 := bits.add_u64(x3, (x9 & 0x14def9dea2f79cd6), u64(fiat.u1(x11)))
x14, x15 := bits.add_u64(x5, u64(0x0), u64(fiat.u1(x13)))
x16, _ := bits.add_u64(x7, (x9 & 0x1000000000000000), u64(fiat.u1(x15)))
out1[0] = x10
out1[1] = x12
out1[2] = x14
out1[3] = x16
}
fe_one :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) {
out1[0] = 0xd6ec31748d98951d
out1[1] = 0xc6ef5bf4737dcf70
out1[2] = 0xfffffffffffffffe
out1[3] = 0xfffffffffffffff
}
fe_non_zero :: proc "contextless" (arg1: ^Montgomery_Domain_Field_Element) -> u64 {
return arg1[0] | (arg1[1] | (arg1[2] | arg1[3]))
}
@(optimization_mode = "none")
fe_cond_assign :: #force_no_inline proc "contextless" (
out1, arg1: ^Montgomery_Domain_Field_Element,
arg2: int,
) {
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
x4 := fiat.cmovznz_u64(fiat.u1(arg2), out1[3], arg1[3])
out1[0] = x1
out1[1] = x2
out1[2] = x3
out1[3] = x4
}
fe_from_montgomery :: proc "contextless" (
out1: ^Non_Montgomery_Domain_Field_Element,
arg1: ^Montgomery_Domain_Field_Element,
) {
x1 := arg1[0]
_, x2 := bits.mul_u64(x1, 0xd2b51da312547e1b)
x5, x4 := bits.mul_u64(x2, 0x1000000000000000)
x7, x6 := bits.mul_u64(x2, 0x14def9dea2f79cd6)
x9, x8 := bits.mul_u64(x2, 0x5812631a5cf5d3ed)
x10, x11 := bits.add_u64(x9, x6, u64(0x0))
_, x13 := bits.add_u64(x1, x8, u64(0x0))
x14, x15 := bits.add_u64(u64(0x0), x10, u64(fiat.u1(x13)))
x16, x17 := bits.add_u64(x14, arg1[1], u64(0x0))
_, x18 := bits.mul_u64(x16, 0xd2b51da312547e1b)
x21, x20 := bits.mul_u64(x18, 0x1000000000000000)
x23, x22 := bits.mul_u64(x18, 0x14def9dea2f79cd6)
x25, x24 := bits.mul_u64(x18, 0x5812631a5cf5d3ed)
x26, x27 := bits.add_u64(x25, x22, u64(0x0))
_, x29 := bits.add_u64(x16, x24, u64(0x0))
x30, x31 := bits.add_u64(
(u64(fiat.u1(x17)) + (u64(fiat.u1(x15)) + (u64(fiat.u1(x11)) + x7))),
x26,
u64(fiat.u1(x29)),
)
x32, x33 := bits.add_u64(x4, (u64(fiat.u1(x27)) + x23), u64(fiat.u1(x31)))
x34, x35 := bits.add_u64(x5, x20, u64(fiat.u1(x33)))
x36, x37 := bits.add_u64(x30, arg1[2], u64(0x0))
x38, x39 := bits.add_u64(x32, u64(0x0), u64(fiat.u1(x37)))
x40, x41 := bits.add_u64(x34, u64(0x0), u64(fiat.u1(x39)))
_, x42 := bits.mul_u64(x36, 0xd2b51da312547e1b)
x45, x44 := bits.mul_u64(x42, 0x1000000000000000)
x47, x46 := bits.mul_u64(x42, 0x14def9dea2f79cd6)
x49, x48 := bits.mul_u64(x42, 0x5812631a5cf5d3ed)
x50, x51 := bits.add_u64(x49, x46, u64(0x0))
_, x53 := bits.add_u64(x36, x48, u64(0x0))
x54, x55 := bits.add_u64(x38, x50, u64(fiat.u1(x53)))
x56, x57 := bits.add_u64(x40, (u64(fiat.u1(x51)) + x47), u64(fiat.u1(x55)))
x58, x59 := bits.add_u64(
(u64(fiat.u1(x41)) + (u64(fiat.u1(x35)) + x21)),
x44,
u64(fiat.u1(x57)),
)
x60, x61 := bits.add_u64(x54, arg1[3], u64(0x0))
x62, x63 := bits.add_u64(x56, u64(0x0), u64(fiat.u1(x61)))
x64, x65 := bits.add_u64(x58, u64(0x0), u64(fiat.u1(x63)))
_, x66 := bits.mul_u64(x60, 0xd2b51da312547e1b)
x69, x68 := bits.mul_u64(x66, 0x1000000000000000)
x71, x70 := bits.mul_u64(x66, 0x14def9dea2f79cd6)
x73, x72 := bits.mul_u64(x66, 0x5812631a5cf5d3ed)
x74, x75 := bits.add_u64(x73, x70, u64(0x0))
_, x77 := bits.add_u64(x60, x72, u64(0x0))
x78, x79 := bits.add_u64(x62, x74, u64(fiat.u1(x77)))
x80, x81 := bits.add_u64(x64, (u64(fiat.u1(x75)) + x71), u64(fiat.u1(x79)))
x82, x83 := bits.add_u64(
(u64(fiat.u1(x65)) + (u64(fiat.u1(x59)) + x45)),
x68,
u64(fiat.u1(x81)),
)
x84 := (u64(fiat.u1(x83)) + x69)
x85, x86 := bits.sub_u64(x78, 0x5812631a5cf5d3ed, u64(0x0))
x87, x88 := bits.sub_u64(x80, 0x14def9dea2f79cd6, u64(fiat.u1(x86)))
x89, x90 := bits.sub_u64(x82, u64(0x0), u64(fiat.u1(x88)))
x91, x92 := bits.sub_u64(x84, 0x1000000000000000, u64(fiat.u1(x90)))
_, x94 := bits.sub_u64(u64(0x0), u64(0x0), u64(fiat.u1(x92)))
x95 := fiat.cmovznz_u64(fiat.u1(x94), x85, x78)
x96 := fiat.cmovznz_u64(fiat.u1(x94), x87, x80)
x97 := fiat.cmovznz_u64(fiat.u1(x94), x89, x82)
x98 := fiat.cmovznz_u64(fiat.u1(x94), x91, x84)
out1[0] = x95
out1[1] = x96
out1[2] = x97
out1[3] = x98
}
fe_to_montgomery :: proc "contextless" (
out1: ^Montgomery_Domain_Field_Element,
arg1: ^Non_Montgomery_Domain_Field_Element,
) {
x1 := arg1[1]
x2 := arg1[2]
x3 := arg1[3]
x4 := arg1[0]
x6, x5 := bits.mul_u64(x4, 0x399411b7c309a3d)
x8, x7 := bits.mul_u64(x4, 0xceec73d217f5be65)
x10, x9 := bits.mul_u64(x4, 0xd00e1ba768859347)
x12, x11 := bits.mul_u64(x4, 0xa40611e3449c0f01)
x13, x14 := bits.add_u64(x12, x9, u64(0x0))
x15, x16 := bits.add_u64(x10, x7, u64(fiat.u1(x14)))
x17, x18 := bits.add_u64(x8, x5, u64(fiat.u1(x16)))
_, x19 := bits.mul_u64(x11, 0xd2b51da312547e1b)
x22, x21 := bits.mul_u64(x19, 0x1000000000000000)
x24, x23 := bits.mul_u64(x19, 0x14def9dea2f79cd6)
x26, x25 := bits.mul_u64(x19, 0x5812631a5cf5d3ed)
x27, x28 := bits.add_u64(x26, x23, u64(0x0))
_, x30 := bits.add_u64(x11, x25, u64(0x0))
x31, x32 := bits.add_u64(x13, x27, u64(fiat.u1(x30)))
x33, x34 := bits.add_u64(x15, (u64(fiat.u1(x28)) + x24), u64(fiat.u1(x32)))
x35, x36 := bits.add_u64(x17, x21, u64(fiat.u1(x34)))
x38, x37 := bits.mul_u64(x1, 0x399411b7c309a3d)
x40, x39 := bits.mul_u64(x1, 0xceec73d217f5be65)
x42, x41 := bits.mul_u64(x1, 0xd00e1ba768859347)
x44, x43 := bits.mul_u64(x1, 0xa40611e3449c0f01)
x45, x46 := bits.add_u64(x44, x41, u64(0x0))
x47, x48 := bits.add_u64(x42, x39, u64(fiat.u1(x46)))
x49, x50 := bits.add_u64(x40, x37, u64(fiat.u1(x48)))
x51, x52 := bits.add_u64(x31, x43, u64(0x0))
x53, x54 := bits.add_u64(x33, x45, u64(fiat.u1(x52)))
x55, x56 := bits.add_u64(x35, x47, u64(fiat.u1(x54)))
x57, x58 := bits.add_u64(
((u64(fiat.u1(x36)) + (u64(fiat.u1(x18)) + x6)) + x22),
x49,
u64(fiat.u1(x56)),
)
_, x59 := bits.mul_u64(x51, 0xd2b51da312547e1b)
x62, x61 := bits.mul_u64(x59, 0x1000000000000000)
x64, x63 := bits.mul_u64(x59, 0x14def9dea2f79cd6)
x66, x65 := bits.mul_u64(x59, 0x5812631a5cf5d3ed)
x67, x68 := bits.add_u64(x66, x63, u64(0x0))
_, x70 := bits.add_u64(x51, x65, u64(0x0))
x71, x72 := bits.add_u64(x53, x67, u64(fiat.u1(x70)))
x73, x74 := bits.add_u64(x55, (u64(fiat.u1(x68)) + x64), u64(fiat.u1(x72)))
x75, x76 := bits.add_u64(x57, x61, u64(fiat.u1(x74)))
x78, x77 := bits.mul_u64(x2, 0x399411b7c309a3d)
x80, x79 := bits.mul_u64(x2, 0xceec73d217f5be65)
x82, x81 := bits.mul_u64(x2, 0xd00e1ba768859347)
x84, x83 := bits.mul_u64(x2, 0xa40611e3449c0f01)
x85, x86 := bits.add_u64(x84, x81, u64(0x0))
x87, x88 := bits.add_u64(x82, x79, u64(fiat.u1(x86)))
x89, x90 := bits.add_u64(x80, x77, u64(fiat.u1(x88)))
x91, x92 := bits.add_u64(x71, x83, u64(0x0))
x93, x94 := bits.add_u64(x73, x85, u64(fiat.u1(x92)))
x95, x96 := bits.add_u64(x75, x87, u64(fiat.u1(x94)))
x97, x98 := bits.add_u64(
((u64(fiat.u1(x76)) + (u64(fiat.u1(x58)) + (u64(fiat.u1(x50)) + x38))) + x62),
x89,
u64(fiat.u1(x96)),
)
_, x99 := bits.mul_u64(x91, 0xd2b51da312547e1b)
x102, x101 := bits.mul_u64(x99, 0x1000000000000000)
x104, x103 := bits.mul_u64(x99, 0x14def9dea2f79cd6)
x106, x105 := bits.mul_u64(x99, 0x5812631a5cf5d3ed)
x107, x108 := bits.add_u64(x106, x103, u64(0x0))
_, x110 := bits.add_u64(x91, x105, u64(0x0))
x111, x112 := bits.add_u64(x93, x107, u64(fiat.u1(x110)))
x113, x114 := bits.add_u64(x95, (u64(fiat.u1(x108)) + x104), u64(fiat.u1(x112)))
x115, x116 := bits.add_u64(x97, x101, u64(fiat.u1(x114)))
x118, x117 := bits.mul_u64(x3, 0x399411b7c309a3d)
x120, x119 := bits.mul_u64(x3, 0xceec73d217f5be65)
x122, x121 := bits.mul_u64(x3, 0xd00e1ba768859347)
x124, x123 := bits.mul_u64(x3, 0xa40611e3449c0f01)
x125, x126 := bits.add_u64(x124, x121, u64(0x0))
x127, x128 := bits.add_u64(x122, x119, u64(fiat.u1(x126)))
x129, x130 := bits.add_u64(x120, x117, u64(fiat.u1(x128)))
x131, x132 := bits.add_u64(x111, x123, u64(0x0))
x133, x134 := bits.add_u64(x113, x125, u64(fiat.u1(x132)))
x135, x136 := bits.add_u64(x115, x127, u64(fiat.u1(x134)))
x137, x138 := bits.add_u64(
((u64(fiat.u1(x116)) + (u64(fiat.u1(x98)) + (u64(fiat.u1(x90)) + x78))) + x102),
x129,
u64(fiat.u1(x136)),
)
_, x139 := bits.mul_u64(x131, 0xd2b51da312547e1b)
x142, x141 := bits.mul_u64(x139, 0x1000000000000000)
x144, x143 := bits.mul_u64(x139, 0x14def9dea2f79cd6)
x146, x145 := bits.mul_u64(x139, 0x5812631a5cf5d3ed)
x147, x148 := bits.add_u64(x146, x143, u64(0x0))
_, x150 := bits.add_u64(x131, x145, u64(0x0))
x151, x152 := bits.add_u64(x133, x147, u64(fiat.u1(x150)))
x153, x154 := bits.add_u64(x135, (u64(fiat.u1(x148)) + x144), u64(fiat.u1(x152)))
x155, x156 := bits.add_u64(x137, x141, u64(fiat.u1(x154)))
x157 := ((u64(fiat.u1(x156)) + (u64(fiat.u1(x138)) + (u64(fiat.u1(x130)) + x118))) + x142)
x158, x159 := bits.sub_u64(x151, 0x5812631a5cf5d3ed, u64(0x0))
x160, x161 := bits.sub_u64(x153, 0x14def9dea2f79cd6, u64(fiat.u1(x159)))
x162, x163 := bits.sub_u64(x155, u64(0x0), u64(fiat.u1(x161)))
x164, x165 := bits.sub_u64(x157, 0x1000000000000000, u64(fiat.u1(x163)))
_, x167 := bits.sub_u64(u64(0x0), u64(0x0), u64(fiat.u1(x165)))
x168 := fiat.cmovznz_u64(fiat.u1(x167), x158, x151)
x169 := fiat.cmovznz_u64(fiat.u1(x167), x160, x153)
x170 := fiat.cmovznz_u64(fiat.u1(x167), x162, x155)
x171 := fiat.cmovznz_u64(fiat.u1(x167), x164, x157)
out1[0] = x168
out1[1] = x169
out1[2] = x170
out1[3] = x171
}
+109 -78
View File
@@ -7,50 +7,69 @@ package _sha3
List of contributors:
zhibog, dotbmp: Initial implementation.
Implementation of the Keccak hashing algorithm, standardized as SHA3 in <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf>
To use the original Keccak padding, set the is_keccak bool to true, otherwise it will use SHA3 padding.
Implementation of the Keccak hashing algorithm, standardized as SHA3
in <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf>.
As the only difference between the legacy Keccak and SHA3 is the domain
separation byte, set dsbyte to the appropriate value to pick the desired
algorithm.
*/
import "core:math/bits"
import "core:mem"
ROUNDS :: 24
Sha3_Context :: struct {
st: struct #raw_union {
RATE_128 :: 1344 / 8 // ONLY for SHAKE128.
RATE_224 :: 1152 / 8
RATE_256 :: 1088 / 8
RATE_384 :: 832 / 8
RATE_512 :: 576 / 8
DS_KECCAK :: 0x01
DS_SHA3 :: 0x06
DS_SHAKE :: 0x1f
DS_CSHAKE :: 0x04
Context :: struct {
st: struct #raw_union {
b: [200]u8,
q: [25]u64,
},
pt: int,
rsiz: int,
mdlen: int,
is_keccak: bool,
pt: int,
rsiz: int,
mdlen: int,
dsbyte: byte,
is_initialized: bool,
is_finalized: bool, // For SHAKE (unlimited squeeze is allowed)
}
@(private)
keccakf_rndc := [?]u64 {
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
}
@(private)
keccakf_rotc := [?]int {
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
}
@(private)
keccakf_piln := [?]i32 {
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
}
@(private)
keccakf :: proc "contextless" (st: ^[25]u64) {
keccakf_rndc := [?]u64 {
0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
0x8000000000008080, 0x0000000080000001, 0x8000000080008008,
}
keccakf_rotc := [?]int {
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
}
keccakf_piln := [?]i32 {
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
}
i, j, r: i32 = ---, ---, ---
t: u64 = ---
bc: [5]u64 = ---
@@ -103,81 +122,93 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
}
}
init :: proc(c: ^Sha3_Context) {
init :: proc(ctx: ^Context) {
for i := 0; i < 25; i += 1 {
c.st.q[i] = 0
ctx.st.q[i] = 0
}
c.rsiz = 200 - 2 * c.mdlen
c.pt = 0
ctx.rsiz = 200 - 2 * ctx.mdlen
ctx.pt = 0
c.is_initialized = true
c.is_finalized = false
ctx.is_initialized = true
ctx.is_finalized = false
}
update :: proc(c: ^Sha3_Context, data: []byte) {
assert(c.is_initialized)
assert(!c.is_finalized)
update :: proc(ctx: ^Context, data: []byte) {
assert(ctx.is_initialized)
assert(!ctx.is_finalized)
j := c.pt
j := ctx.pt
for i := 0; i < len(data); i += 1 {
c.st.b[j] ~= data[i]
ctx.st.b[j] ~= data[i]
j += 1
if j >= c.rsiz {
keccakf(&c.st.q)
if j >= ctx.rsiz {
keccakf(&ctx.st.q)
j = 0
}
}
c.pt = j
ctx.pt = j
}
final :: proc(c: ^Sha3_Context, hash: []byte) {
assert(c.is_initialized)
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
assert(ctx.is_initialized)
if len(hash) < c.mdlen {
if c.is_keccak {
panic("crypto/keccac: invalid destination digest size")
}
if len(hash) < ctx.mdlen {
panic("crypto/sha3: invalid destination digest size")
}
if c.is_keccak {
c.st.b[c.pt] ~= 0x01
} else {
c.st.b[c.pt] ~= 0x06
}
c.st.b[c.rsiz - 1] ~= 0x80
keccakf(&c.st.q)
for i := 0; i < c.mdlen; i += 1 {
hash[i] = c.st.b[i]
ctx := ctx
if finalize_clone {
tmp_ctx: Context
clone(&tmp_ctx, ctx)
ctx = &tmp_ctx
}
defer (reset(ctx))
c.is_initialized = false // No more absorb, no more squeeze.
ctx.st.b[ctx.pt] ~= ctx.dsbyte
ctx.st.b[ctx.rsiz - 1] ~= 0x80
keccakf(&ctx.st.q)
for i := 0; i < ctx.mdlen; i += 1 {
hash[i] = ctx.st.b[i]
}
}
shake_xof :: proc(c: ^Sha3_Context) {
assert(c.is_initialized)
assert(!c.is_finalized)
c.st.b[c.pt] ~= 0x1F
c.st.b[c.rsiz - 1] ~= 0x80
keccakf(&c.st.q)
c.pt = 0
c.is_finalized = true // No more absorb, unlimited squeeze.
clone :: proc(ctx, other: ^Context) {
ctx^ = other^
}
shake_out :: proc(c: ^Sha3_Context, hash: []byte) {
assert(c.is_initialized)
assert(c.is_finalized)
reset :: proc(ctx: ^Context) {
if !ctx.is_initialized {
return
}
j := c.pt
mem.zero_explicit(ctx, size_of(ctx^))
}
shake_xof :: proc(ctx: ^Context) {
assert(ctx.is_initialized)
assert(!ctx.is_finalized)
ctx.st.b[ctx.pt] ~= ctx.dsbyte
ctx.st.b[ctx.rsiz - 1] ~= 0x80
keccakf(&ctx.st.q)
ctx.pt = 0
ctx.is_finalized = true // No more absorb, unlimited squeeze.
}
shake_out :: proc(ctx: ^Context, hash: []byte) {
assert(ctx.is_initialized)
assert(ctx.is_finalized)
j := ctx.pt
for i := 0; i < len(hash); i += 1 {
if j >= c.rsiz {
keccakf(&c.st.q)
if j >= ctx.rsiz {
keccakf(&ctx.st.q)
j = 0
}
hash[i] = c.st.b[j]
hash[i] = ctx.st.b[j]
j += 1
}
c.pt = j
ctx.pt = j
}
+145
View File
@@ -0,0 +1,145 @@
package _sha3
import "core:encoding/endian"
import "core:math/bits"
init_cshake :: proc(ctx: ^Context, n, s: []byte, sec_strength: int) {
ctx.mdlen = sec_strength / 8
// No domain separator is equivalent to vanilla SHAKE.
if len(n) == 0 && len(s) == 0 {
ctx.dsbyte = DS_SHAKE
init(ctx)
return
}
ctx.dsbyte = DS_CSHAKE
init(ctx)
bytepad(ctx, [][]byte{n, s}, rate_cshake(sec_strength))
}
final_cshake :: proc(ctx: ^Context, dst: []byte, finalize_clone: bool = false) {
ctx := ctx
if finalize_clone {
tmp_ctx: Context
clone(&tmp_ctx, ctx)
ctx = &tmp_ctx
}
defer reset(ctx)
encode_byte_len(ctx, len(dst), false) // right_encode
shake_xof(ctx)
shake_out(ctx, dst)
}
rate_cshake :: #force_inline proc(sec_strength: int) -> int {
switch sec_strength {
case 128:
return RATE_128
case 256:
return RATE_256
}
panic("crypto/sha3: invalid security strength")
}
// right_encode and left_encode are defined to support 0 <= x < 2^2040
// however, the largest value we will ever need to encode is `max(int) * 8`.
//
// This is unfortunate as the extreme upper edge is larger than
// `max(u64)`. While such values are impractical at present,
// they are possible (ie: https://arxiv.org/pdf/quant-ph/9908043.pdf).
//
// Thus we support 0 <= x < 2^128.
@(private)
_PAD: [RATE_128]byte // Biggest possible value of w per spec.
bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
// 1. z = left_encode(w) || X.
z_hi: u64
z_lo := left_right_encode(ctx, 0, u64(w), true)
for x in x_strings {
// All uses of bytepad in SP 800-185 use the output from
// one or more encode_string values for `X`.
hi, lo := encode_string(ctx, x)
carry: u64
z_lo, carry = bits.add_u64(z_lo, lo, 0)
z_hi, carry = bits.add_u64(z_hi, hi, carry)
// This isn't actually possible, at least with the currently
// defined SP 800-185 routines.
if carry != 0 {
panic("crypto/sha3: bytepad input length overflow")
}
}
// We skip this step as we are doing a byte-oriented implementation
// rather than a bit oriented one.
//
// 2. while len(z) mod 8 ≠ 0:
// z = z || 0
// 3. while (len(z)/8) mod w ≠ 0:
// z = z || 00000000
z_len := u128(z_hi) << 64 | u128(z_lo)
z_rem := int(z_len % u128(w))
pad := _PAD[:w - z_rem]
// We just add the padding to the state, instead of returning z.
//
// 4. return z.
update(ctx, pad)
}
encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
l := encode_byte_len(ctx, len(s), true) // left_encode
update(ctx, s)
lo, hi := bits.add_u64(l, u64(len(s)), 0)
return hi, lo
}
encode_byte_len :: #force_inline proc(ctx: ^Context, l: int, is_left: bool) -> u64 {
hi, lo := bits.mul_u64(u64(l), 8)
return left_right_encode(ctx, hi, lo, is_left)
}
@(private)
left_right_encode :: proc(ctx: ^Context, hi, lo: u64, is_left: bool) -> u64 {
HI_OFFSET :: 1
LO_OFFSET :: HI_OFFSET + 8
RIGHT_OFFSET :: LO_OFFSET + 8
BUF_LEN :: RIGHT_OFFSET + 1
buf: [BUF_LEN]byte // prefix + largest uint + postfix
endian.unchecked_put_u64be(buf[HI_OFFSET:], hi)
endian.unchecked_put_u64be(buf[LO_OFFSET:], lo)
// 2. Strip leading `0x00` bytes.
off: int
for off = HI_OFFSET; off < RIGHT_OFFSET - 1; off = off + 1 {// Note: Minimum size is 1, not 0.
if buf[off] != 0 {
break
}
}
n := byte(RIGHT_OFFSET - off)
// 3. Prefix (left_encode) or postfix (right_encode) the length in bytes.
b: []byte
switch is_left {
case true:
buf[off - 1] = n // n | x
b = buf[off - 1:RIGHT_OFFSET]
case false:
buf[RIGHT_OFFSET] = n // x | n
b = buf[off:]
}
update(ctx, b)
return u64(len(b))
}
+34 -102
View File
@@ -1,3 +1,10 @@
/*
package blake2b implements the BLAKE2b hash algorithm.
See:
- https://datatracker.ietf.org/doc/html/rfc7693
- https://www.blake2.net
*/
package blake2b
/*
@@ -6,122 +13,47 @@ package blake2b
List of contributors:
zhibog, dotbmp: Initial implementation.
Interface for the BLAKE2b hashing algorithm.
BLAKE2b and BLAKE2s share the implementation in the _blake2 package.
*/
import "core:io"
import "core:os"
import "../_blake2"
/*
High level API
*/
// DIGEST_SIZE is the BLAKE2b digest size in bytes.
DIGEST_SIZE :: 64
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
ctx.cfg = cfg
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
ctx: Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
ctx.cfg = cfg
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
ctx.cfg = cfg
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
hash_stream,
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
// BLOCK_SIZE is the BLAKE2b block size in bytes.
BLOCK_SIZE :: _blake2.BLAKE2B_BLOCK_SIZE
// Context is a BLAKE2b instance.
Context :: _blake2.Blake2b_Context
// init initializes a Context with the default BLAKE2b config.
init :: proc(ctx: ^Context) {
_blake2.init(ctx)
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
_blake2.init(ctx, &cfg)
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
_blake2.update(ctx, data)
}
final :: proc(ctx: ^Context, hash: []byte) {
_blake2.final(ctx, hash)
// final finalizes the Context, writes the digest to hash, and calls
// reset on the Context.
//
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
_blake2.final(ctx, hash, finalize_clone)
}
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
_blake2.clone(ctx, other)
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
_blake2.reset(ctx)
}
+34 -102
View File
@@ -1,3 +1,10 @@
/*
package blake2s implements the BLAKE2s hash algorithm.
See:
- https://datatracker.ietf.org/doc/html/rfc7693
- https://www.blake2.net/
*/
package blake2s
/*
@@ -6,122 +13,47 @@ package blake2s
List of contributors:
zhibog, dotbmp: Initial implementation.
Interface for the BLAKE2s hashing algorithm.
BLAKE2s and BLAKE2b share the implementation in the _blake2 package.
*/
import "core:io"
import "core:os"
import "../_blake2"
/*
High level API
*/
// DIGEST_SIZE is the BLAKE2s digest size in bytes.
DIGEST_SIZE :: 32
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
ctx.cfg = cfg
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
ctx: Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
ctx.cfg = cfg
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
ctx.cfg = cfg
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
hash_stream,
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
// BLOCK_SIZE is the BLAKE2s block size in bytes.
BLOCK_SIZE :: _blake2.BLAKE2S_BLOCK_SIZE
// Context is a BLAKE2s instance.
Context :: _blake2.Blake2s_Context
// init initializes a Context with the default BLAKE2s config.
init :: proc(ctx: ^Context) {
_blake2.init(ctx)
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
_blake2.init(ctx, &cfg)
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
_blake2.update(ctx, data)
}
final :: proc(ctx: ^Context, hash: []byte) {
_blake2.final(ctx, hash)
// final finalizes the Context, writes the digest to hash, and calls
// reset on the Context.
//
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
_blake2.final(ctx, hash, finalize_clone)
}
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
_blake2.clone(ctx, other)
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
_blake2.reset(ctx)
}
+33 -15
View File
@@ -1,11 +1,21 @@
/*
package chacha20 implements the ChaCha20 and XChaCha20 stream ciphers.
See:
- https://datatracker.ietf.org/doc/html/rfc8439
- https://datatracker.ietf.org/doc/draft-irtf-cfrg-xchacha/03/
*/
package chacha20
import "core:encoding/endian"
import "core:math/bits"
import "core:mem"
// KEY_SIZE is the (X)ChaCha20 key size in bytes.
KEY_SIZE :: 32
// NONCE_SIZE is the ChaCha20 nonce size in bytes.
NONCE_SIZE :: 12
// XNONCE_SIZE is the XChaCha20 nonce size in bytes.
XNONCE_SIZE :: 24
@(private)
@@ -19,25 +29,26 @@ _STATE_SIZE_U32 :: 16
_ROUNDS :: 20
@(private)
_SIGMA_0 : u32 : 0x61707865
_SIGMA_0: u32 : 0x61707865
@(private)
_SIGMA_1 : u32 : 0x3320646e
_SIGMA_1: u32 : 0x3320646e
@(private)
_SIGMA_2 : u32 : 0x79622d32
_SIGMA_2: u32 : 0x79622d32
@(private)
_SIGMA_3 : u32 : 0x6b206574
_SIGMA_3: u32 : 0x6b206574
// Context is a ChaCha20 or XChaCha20 instance.
Context :: struct {
_s: [_STATE_SIZE_U32]u32,
_buffer: [_BLOCK_SIZE]byte,
_off: int,
_s: [_STATE_SIZE_U32]u32,
_buffer: [_BLOCK_SIZE]byte,
_off: int,
_is_ietf_flavor: bool,
_is_initialized: bool,
}
init :: proc (ctx: ^Context, key, nonce: []byte) {
// init inititializes a Context for ChaCha20 or XChaCha20 with the provided
// key and nonce.
init :: proc(ctx: ^Context, key, nonce: []byte) {
if len(key) != KEY_SIZE {
panic("crypto/chacha20: invalid ChaCha20 key size")
}
@@ -89,7 +100,8 @@ init :: proc (ctx: ^Context, key, nonce: []byte) {
ctx._is_initialized = true
}
seek :: proc (ctx: ^Context, block_nr: u64) {
// seek seeks the (X)ChaCha20 stream counter to the specified block.
seek :: proc(ctx: ^Context, block_nr: u64) {
assert(ctx._is_initialized)
if ctx._is_ietf_flavor {
@@ -103,7 +115,10 @@ seek :: proc (ctx: ^Context, block_nr: u64) {
ctx._off = _BLOCK_SIZE
}
xor_bytes :: proc (ctx: ^Context, dst, src: []byte) {
// xor_bytes XORs each byte in src with bytes taken from the (X)ChaCha20
// keystream, and writes the resulting output to dst. Dst and src MUST
// alias exactly or not at all.
xor_bytes :: proc(ctx: ^Context, dst, src: []byte) {
assert(ctx._is_initialized)
// TODO: Enforcing that dst and src alias exactly or not at all
@@ -147,7 +162,8 @@ xor_bytes :: proc (ctx: ^Context, dst, src: []byte) {
}
}
keystream_bytes :: proc (ctx: ^Context, dst: []byte) {
// keystream_bytes fills dst with the raw (X)ChaCha20 keystream output.
keystream_bytes :: proc(ctx: ^Context, dst: []byte) {
assert(ctx._is_initialized)
dst := dst
@@ -180,7 +196,9 @@ keystream_bytes :: proc (ctx: ^Context, dst: []byte) {
}
}
reset :: proc (ctx: ^Context) {
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
mem.zero_explicit(&ctx._s, size_of(ctx._s))
mem.zero_explicit(&ctx._buffer, size_of(ctx._buffer))
@@ -188,7 +206,7 @@ reset :: proc (ctx: ^Context) {
}
@(private)
_do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
_do_blocks :: proc(ctx: ^Context, dst, src: []byte, nr_blocks: int) {
// Enforce the maximum consumed keystream per nonce.
//
// While all modern "standard" definitions of ChaCha20 use
@@ -1,3 +1,10 @@
/*
package chacha20poly1305 implements the AEAD_CHACHA20_POLY1305 Authenticated
Encryption with Additional Data algorithm.
See:
- https://www.rfc-editor.org/rfc/rfc8439
*/
package chacha20poly1305
import "core:crypto"
@@ -6,8 +13,11 @@ import "core:crypto/poly1305"
import "core:encoding/endian"
import "core:mem"
// KEY_SIZE is the chacha20poly1305 key size in bytes.
KEY_SIZE :: chacha20.KEY_SIZE
// NONCE_SIZE is the chacha20poly1305 nonce size in bytes.
NONCE_SIZE :: chacha20.NONCE_SIZE
// TAG_SIZE is the chacha20poly1305 tag size in bytes.
TAG_SIZE :: poly1305.TAG_SIZE
@(private)
@@ -49,6 +59,8 @@ _update_mac_pad16 :: #force_inline proc (ctx: ^poly1305.Context, x_len: int) {
}
}
// encrypt encrypts the plaintext and authenticates the aad and ciphertext,
// with the provided key and nonce, stores the output in ciphertext and tag.
encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
_validate_common_slice_sizes(tag, key, nonce, aad, plaintext)
if len(ciphertext) != len(plaintext) {
@@ -95,6 +107,11 @@ encrypt :: proc (ciphertext, tag, key, nonce, aad, plaintext: []byte) {
poly1305.final(&mac_ctx, tag) // Implicitly sanitizes context.
}
// decrypt authenticates the aad and ciphertext, and decrypts the ciphertext,
// with the provided key, nonce, and tag, and stores the output in plaintext,
// returning true iff the authentication was successful.
//
// If authentication fails, the destination plaintext buffer will be zeroed.
decrypt :: proc (plaintext, tag, key, nonce, aad, ciphertext: []byte) -> bool {
_validate_common_slice_sizes(tag, key, nonce, aad, ciphertext)
if len(ciphertext) != len(plaintext) {
+10
View File
@@ -1,3 +1,7 @@
/*
package crypto implements a selection of cryptography algorithms and useful
helper routines.
*/
package crypto
import "core:mem"
@@ -51,3 +55,9 @@ rand_bytes :: proc (dst: []byte) {
_rand_bytes(dst)
}
// has_rand_bytes returns true iff the target has support for accessing the
// system entropty source.
has_rand_bytes :: proc () -> bool {
return _has_rand_bytes()
}
+314
View File
@@ -0,0 +1,314 @@
/*
package ed25519 implements the Ed25519 EdDSA signature algorithm.
See:
- https://datatracker.ietf.org/doc/html/rfc8032
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
- https://eprint.iacr.org/2020/1244.pdf
*/
package ed25519
import "core:crypto"
import grp "core:crypto/_edwards25519"
import "core:crypto/sha2"
import "core:mem"
// PRIVATE_KEY_SIZE is the byte-encoded private key size.
PRIVATE_KEY_SIZE :: 32
// PUBLIC_KEY_SIZE is the byte-encoded public key size.
PUBLIC_KEY_SIZE :: 32
// SIGNATURE_SIZE is the byte-encoded signature size.
SIGNATURE_SIZE :: 64
@(private)
NONCE_SIZE :: 32
// Private_Key is an Ed25519 private key.
Private_Key :: struct {
// WARNING: All of the members are to be treated as internal (ie:
// the Private_Key structure is intended to be opaque). There are
// subtle vulnerabilities that can be introduced if the internal
// values are allowed to be altered.
//
// See: https://github.com/MystenLabs/ed25519-unsafe-libs
_b: [PRIVATE_KEY_SIZE]byte,
_s: grp.Scalar,
_nonce: [NONCE_SIZE]byte,
_pub_key: Public_Key,
_is_initialized: bool,
}
// Public_Key is an Ed25519 public key.
Public_Key :: struct {
// WARNING: All of the members are to be treated as internal (ie:
// the Public_Key structure is intended to be opaque).
_b: [PUBLIC_KEY_SIZE]byte,
_neg_A: grp.Group_Element,
_is_valid: bool,
_is_initialized: bool,
}
// private_key_set_bytes decodes a byte-encoded private key, and returns
// true iff the operation was successful.
private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool {
if len(b) != PRIVATE_KEY_SIZE {
return false
}
// Derive the private key.
ctx: sha2.Context_512 = ---
h_bytes: [sha2.DIGEST_SIZE_512]byte = ---
sha2.init_512(&ctx)
sha2.update(&ctx, b)
sha2.final(&ctx, h_bytes[:])
copy(priv_key._b[:], b)
copy(priv_key._nonce[:], h_bytes[32:])
grp.sc_set_bytes_rfc8032(&priv_key._s, h_bytes[:32])
// Derive the corresponding public key.
A: grp.Group_Element = ---
grp.ge_scalarmult_basepoint(&A, &priv_key._s)
grp.ge_bytes(&A, priv_key._pub_key._b[:])
grp.ge_negate(&priv_key._pub_key._neg_A, &A)
priv_key._pub_key._is_valid = !grp.ge_is_small_order(&A)
priv_key._pub_key._is_initialized = true
priv_key._is_initialized = true
return true
}
// private_key_bytes sets dst to byte-encoding of priv_key.
private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
if !priv_key._is_initialized {
panic("crypto/ed25519: uninitialized private key")
}
if len(dst) != PRIVATE_KEY_SIZE {
panic("crypto/ed25519: invalid destination size")
}
copy(dst, priv_key._b[:])
}
// private_key_clear clears priv_key to the uninitialized state.
private_key_clear :: proc "contextless" (priv_key: ^Private_Key) {
mem.zero_explicit(priv_key, size_of(Private_Key))
}
// sign writes the signature by priv_key over msg to sig.
sign :: proc(priv_key: ^Private_Key, msg, sig: []byte) {
if !priv_key._is_initialized {
panic("crypto/ed25519: uninitialized private key")
}
if len(sig) != SIGNATURE_SIZE {
panic("crypto/ed25519: invalid destination size")
}
// 1. Compute the hash of the private key d, H(d) = (h_0, h_1, ..., h_2b-1)
// using SHA-512 for Ed25519. H(d) may be precomputed.
//
// 2. Using the second half of the digest hdigest2 = hb || ... || h2b-1,
// define:
//
// 2.1 For Ed25519, r = SHA-512(hdigest2 || M); Interpret r as a
// 64-octet little-endian integer.
ctx: sha2.Context_512 = ---
digest_bytes: [sha2.DIGEST_SIZE_512]byte = ---
sha2.init_512(&ctx)
sha2.update(&ctx, priv_key._nonce[:])
sha2.update(&ctx, msg)
sha2.final(&ctx, digest_bytes[:])
r: grp.Scalar = ---
grp.sc_set_bytes_wide(&r, &digest_bytes)
// 3. Compute the point [r]G. The octet string R is the encoding of
// the point [r]G.
R: grp.Group_Element = ---
R_bytes := sig[:32]
grp.ge_scalarmult_basepoint(&R, &r)
grp.ge_bytes(&R, R_bytes)
// 4. Derive s from H(d) as in the key pair generation algorithm.
// Use octet strings R, Q, and M to define:
//
// 4.1 For Ed25519, digest = SHA-512(R || Q || M).
// Interpret digest as a little-endian integer.
sha2.init_512(&ctx)
sha2.update(&ctx, R_bytes)
sha2.update(&ctx, priv_key._pub_key._b[:]) // Q in NIST terminology.
sha2.update(&ctx, msg)
sha2.final(&ctx, digest_bytes[:])
sc: grp.Scalar = --- // `digest` in NIST terminology.
grp.sc_set_bytes_wide(&sc, &digest_bytes)
// 5. Compute S = (r + digest × s) mod n. The octet string S is the
// encoding of the resultant integer.
grp.sc_mul(&sc, &sc, &priv_key._s)
grp.sc_add(&sc, &sc, &r)
// 6. Form the signature as the concatenation of the octet strings
// R and S.
grp.sc_bytes(sig[32:], &sc)
grp.sc_clear(&r)
}
// public_key_set_bytes decodes a byte-encoded public key, and returns
// true iff the operation was successful.
public_key_set_bytes :: proc "contextless" (pub_key: ^Public_Key, b: []byte) -> bool {
if len(b) != PUBLIC_KEY_SIZE {
return false
}
A: grp.Group_Element = ---
if !grp.ge_set_bytes(&A, b) {
return false
}
copy(pub_key._b[:], b)
grp.ge_negate(&pub_key._neg_A, &A)
pub_key._is_valid = !grp.ge_is_small_order(&A)
pub_key._is_initialized = true
return true
}
// public_key_set_priv sets pub_key to the public component of priv_key.
public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
if !priv_key._is_initialized {
panic("crypto/ed25519: uninitialized public key")
}
src := &priv_key._pub_key
copy(pub_key._b[:], src._b[:])
grp.ge_set(&pub_key._neg_A, &src._neg_A)
pub_key._is_valid = src._is_valid
pub_key._is_initialized = src._is_initialized
}
// public_key_bytes sets dst to byte-encoding of pub_key.
public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
if !pub_key._is_initialized {
panic("crypto/ed25519: uninitialized public key")
}
if len(dst) != PUBLIC_KEY_SIZE {
panic("crypto/ed25519: invalid destination size")
}
copy(dst, pub_key._b[:])
}
// public_key_equal returns true iff pub_key is equal to other.
public_key_equal :: proc(pub_key, other: ^Public_Key) -> bool {
if !pub_key._is_initialized || !other._is_initialized {
panic("crypto/ed25519: uninitialized public key")
}
return crypto.compare_constant_time(pub_key._b[:], other._b[:]) == 1
}
// verify returns true iff sig is a valid signature by pub_key over msg.
//
// The optional `allow_small_order_A` parameter will make this
// implementation strictly compatible with FIPS 186-5, at the expense of
// SBS-security. Doing so is NOT recommended, and the disallowed
// public keys all have a known discrete-log.
verify :: proc(pub_key: ^Public_Key, msg, sig: []byte, allow_small_order_A := false) -> bool {
switch {
case !pub_key._is_initialized:
return false
case len(sig) != SIGNATURE_SIZE:
return false
}
// TLDR: Just use ristretto255.
//
// While there are two "standards" for EdDSA, existing implementations
// diverge (sometimes dramatically). This implementation opts for
// "Algorithm 2" from "Taming the Many EdDSAs", which provides the
// strongest notion of security (SUF-CMA + SBS).
//
// The relevant properties are:
// - Reject non-canonical S.
// - Reject non-canonical A/R.
// - Reject small-order A (Extra non-standard check).
// - Cofactored verification equation.
//
// There are 19 possible non-canonical group element encodings of
// which:
// - 2 are small order
// - 10 are mixed order
// - 7 are not on the curve
//
// While historical implementations have been lax about enforcing
// that A/R are canonically encoded, that behavior is mandated by
// both the RFC and FIPS specification. No valid key generation
// or sign implementation will ever produce non-canonically encoded
// public keys or signatures.
//
// There are 8 small-order group elements, 1 which is in the
// prime-order sub-group, and thus the probability that a properly
// generated A is small-order is cryptographically insignificant.
//
// While both the RFC and FIPS standard allow for either the
// cofactored or non-cofactored equation. It is possible to
// artificially produce signatures that are valid for the former
// but not the latter. This will NEVER occur with a valid sign
// implementation. The choice of the latter is to be compatible
// with ABGLSV-Pornin, batch verification, and FROST (among other
// things).
s_bytes, r_bytes := sig[32:], sig[:32]
// 1. Reject the signature if S is not in the range [0, L).
s: grp.Scalar = ---
if !grp.sc_set_bytes(&s, s_bytes) {
return false
}
// 2. Reject the signature if the public key A is one of 8 small
// order points.
//
// As this check is optional and not part of the standard, we allow
// the caller to bypass it if desired. Disabling the check makes
// the scheme NOT SBS-secure.
if !pub_key._is_valid && !allow_small_order_A {
return false
}
// 3. Reject the signature if A or R are non-canonical.
//
// Note: All initialized public keys are guaranteed to be canonical.
neg_R: grp.Group_Element = ---
if !grp.ge_set_bytes(&neg_R, r_bytes) {
return false
}
grp.ge_negate(&neg_R, &neg_R)
// 4. Compute the hash SHA512(R||A||M) and reduce it mod L to get a
// scalar h.
ctx: sha2.Context_512 = ---
h_bytes: [sha2.DIGEST_SIZE_512]byte = ---
sha2.init_512(&ctx)
sha2.update(&ctx, r_bytes)
sha2.update(&ctx, pub_key._b[:])
sha2.update(&ctx, msg)
sha2.final(&ctx, h_bytes[:])
h: grp.Scalar = ---
grp.sc_set_bytes_wide(&h, &h_bytes)
// 5. Accept if 8(s * G) - 8R - 8(h * A) = 0
//
// > first compute V = SB R hA and then accept if V is one of
// > 8 small order points (or alternatively compute 8V with 3
// > doublings and check against the neutral element)
V: grp.Group_Element = ---
grp.ge_double_scalarmult_basepoint_vartime(&V, &h, &pub_key._neg_A, &s)
grp.ge_add(&V, &V, &neg_R)
return grp.ge_is_small_order(&V)
}
+62
View File
@@ -0,0 +1,62 @@
/*
package hash provides a generic interface to the supported hash algorithms.
A high-level convenience procedure group `hash` is provided to easily
accomplish common tasks.
- `hash_string` - Hash a given string and return the digest.
- `hash_bytes` - Hash a given byte slice and return the digest.
- `hash_string_to_buffer` - Hash a given string and put the digest in
the third parameter. It requires that the destination buffer
is at least as big as the digest size.
- `hash_bytes_to_buffer` - Hash a given string and put the computed
digest in the third parameter. It requires that the destination
buffer is at least as big as the digest size.
- `hash_stream` - Incrementally fully consume a `io.Stream`, and return
the computed digest.
- `hash_file` - Takes a file handle and returns the computed digest.
A third optional boolean parameter controls if the file is streamed
(default), or or read at once.
```odin
package hash_example
import "core:crypto/hash"
main :: proc() {
input := "Feed the fire."
// Compute the digest, using the high level API.
returned_digest := hash.hash(hash.Algorithm.SHA512_256, input)
defer delete(returned_digest)
// Variant that takes a destination buffer, instead of returning
// the digest.
digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.BLAKE2B]) // @note: Destination buffer has to be at least as big as the digest size of the hash.
defer delete(digest)
hash.hash(hash.Algorithm.BLAKE2B, input, digest)
}
```
A generic low level API is provided supporting the init/update/final interface
that is typical with cryptographic hash function implementations.
```odin
package hash_example
import "core:crypto/hash"
main :: proc() {
input := "Let the cinders burn."
// Compute the digest, using the low level API.
ctx: hash.Context
digest := make([]byte, hash.DIGEST_SIZES[hash.Algorithm.SHA3_512])
defer delete(digest)
hash.init(&ctx, hash.Algorithm.SHA3_512)
hash.update(&ctx, transmute([]byte)input)
hash.final(&ctx, digest)
}
```
*/
package crypto_hash
+116
View File
@@ -0,0 +1,116 @@
package crypto_hash
/*
Copyright 2021 zhibog
Made available under the BSD-3 license.
List of contributors:
zhibog, dotbmp: Initial implementation.
*/
import "core:io"
import "core:mem"
import "core:os"
// hash_bytes will hash the given input and return the computed digest
// in a newly allocated slice.
hash_string :: proc(algorithm: Algorithm, data: string, allocator := context.allocator) -> []byte {
return hash_bytes(algorithm, transmute([]byte)(data), allocator)
}
// hash_bytes will hash the given input and return the computed digest
// in a newly allocated slice.
hash_bytes :: proc(algorithm: Algorithm, data: []byte, allocator := context.allocator) -> []byte {
dst := make([]byte, DIGEST_SIZES[algorithm], allocator)
hash_bytes_to_buffer(algorithm, data, dst)
return dst
}
// hash_string_to_buffer will hash the given input and assign the
// computed digest to the third parameter. It requires that the
// destination buffer is at least as big as the digest size.
hash_string_to_buffer :: proc(algorithm: Algorithm, data: string, hash: []byte) {
hash_bytes_to_buffer(algorithm, transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed digest into the third parameter. It requires that the
// destination buffer is at least as big as the digest size.
hash_bytes_to_buffer :: proc(algorithm: Algorithm, data, hash: []byte) {
ctx: Context
init(&ctx, algorithm)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will incrementally fully consume a stream, and return the
// computed digest in a newly allocated slice.
hash_stream :: proc(
algorithm: Algorithm,
s: io.Stream,
allocator := context.allocator,
) -> (
[]byte,
io.Error,
) {
ctx: Context
buf: [MAX_BLOCK_SIZE * 4]byte
defer mem.zero_explicit(&buf, size_of(buf))
init(&ctx, algorithm)
loop: for {
n, err := io.read(s, buf[:])
if n > 0 {
// XXX/yawning: Can io.read return n > 0 and EOF?
update(&ctx, buf[:n])
}
#partial switch err {
case .None:
case .EOF:
break loop
case:
return nil, err
}
}
dst := make([]byte, DIGEST_SIZES[algorithm], allocator)
final(&ctx, dst)
return dst, io.Error.None
}
// hash_file will read the file provided by the given handle and return the
// computed digest in a newly allocated slice.
hash_file :: proc(
algorithm: Algorithm,
hd: os.Handle,
load_at_once := false,
allocator := context.allocator,
) -> (
[]byte,
io.Error,
) {
if !load_at_once {
return hash_stream(algorithm, os.stream_from_handle(hd), allocator)
}
buf, ok := os.read_entire_file(hd, allocator)
if !ok {
return nil, io.Error.Unknown
}
defer delete(buf, allocator)
return hash_bytes(algorithm, buf, allocator), io.Error.None
}
hash :: proc {
hash_stream,
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
+353
View File
@@ -0,0 +1,353 @@
package crypto_hash
import "core:crypto/blake2b"
import "core:crypto/blake2s"
import "core:crypto/sha2"
import "core:crypto/sha3"
import "core:crypto/sm3"
import "core:crypto/legacy/keccak"
import "core:crypto/legacy/md5"
import "core:crypto/legacy/sha1"
import "core:reflect"
// MAX_DIGEST_SIZE is the maximum size digest that can be returned by any
// of the Algorithms supported via this package.
MAX_DIGEST_SIZE :: 64
// MAX_BLOCK_SIZE is the maximum block size used by any of Algorithms
// supported by this package.
MAX_BLOCK_SIZE :: sha3.BLOCK_SIZE_224
// Algorithm is the algorithm identifier associated with a given Context.
Algorithm :: enum {
Invalid,
BLAKE2B,
BLAKE2S,
SHA224,
SHA256,
SHA384,
SHA512,
SHA512_256,
SHA3_224,
SHA3_256,
SHA3_384,
SHA3_512,
SM3,
Legacy_KECCAK_224,
Legacy_KECCAK_256,
Legacy_KECCAK_384,
Legacy_KECCAK_512,
Insecure_MD5,
Insecure_SHA1,
}
// ALGORITHM_NAMES is the Algorithm to algorithm name string.
ALGORITHM_NAMES := [Algorithm]string {
.Invalid = "Invalid",
.BLAKE2B = "BLAKE2b",
.BLAKE2S = "BLAKE2s",
.SHA224 = "SHA-224",
.SHA256 = "SHA-256",
.SHA384 = "SHA-384",
.SHA512 = "SHA-512",
.SHA512_256 = "SHA-512/256",
.SHA3_224 = "SHA3-224",
.SHA3_256 = "SHA3-256",
.SHA3_384 = "SHA3-384",
.SHA3_512 = "SHA3-512",
.SM3 = "SM3",
.Legacy_KECCAK_224 = "Keccak-224",
.Legacy_KECCAK_256 = "Keccak-256",
.Legacy_KECCAK_384 = "Keccak-384",
.Legacy_KECCAK_512 = "Keccak-512",
.Insecure_MD5 = "MD5",
.Insecure_SHA1 = "SHA-1",
}
// DIGEST_SIZES is the Algorithm to digest size in bytes.
DIGEST_SIZES := [Algorithm]int {
.Invalid = 0,
.BLAKE2B = blake2b.DIGEST_SIZE,
.BLAKE2S = blake2s.DIGEST_SIZE,
.SHA224 = sha2.DIGEST_SIZE_224,
.SHA256 = sha2.DIGEST_SIZE_256,
.SHA384 = sha2.DIGEST_SIZE_384,
.SHA512 = sha2.DIGEST_SIZE_512,
.SHA512_256 = sha2.DIGEST_SIZE_512_256,
.SHA3_224 = sha3.DIGEST_SIZE_224,
.SHA3_256 = sha3.DIGEST_SIZE_256,
.SHA3_384 = sha3.DIGEST_SIZE_384,
.SHA3_512 = sha3.DIGEST_SIZE_512,
.SM3 = sm3.DIGEST_SIZE,
.Legacy_KECCAK_224 = keccak.DIGEST_SIZE_224,
.Legacy_KECCAK_256 = keccak.DIGEST_SIZE_256,
.Legacy_KECCAK_384 = keccak.DIGEST_SIZE_384,
.Legacy_KECCAK_512 = keccak.DIGEST_SIZE_512,
.Insecure_MD5 = md5.DIGEST_SIZE,
.Insecure_SHA1 = sha1.DIGEST_SIZE,
}
// BLOCK_SIZES is the Algoritm to block size in bytes.
BLOCK_SIZES := [Algorithm]int {
.Invalid = 0,
.BLAKE2B = blake2b.BLOCK_SIZE,
.BLAKE2S = blake2s.BLOCK_SIZE,
.SHA224 = sha2.BLOCK_SIZE_256,
.SHA256 = sha2.BLOCK_SIZE_256,
.SHA384 = sha2.BLOCK_SIZE_512,
.SHA512 = sha2.BLOCK_SIZE_512,
.SHA512_256 = sha2.BLOCK_SIZE_512,
.SHA3_224 = sha3.BLOCK_SIZE_224,
.SHA3_256 = sha3.BLOCK_SIZE_256,
.SHA3_384 = sha3.BLOCK_SIZE_384,
.SHA3_512 = sha3.BLOCK_SIZE_512,
.SM3 = sm3.BLOCK_SIZE,
.Legacy_KECCAK_224 = keccak.BLOCK_SIZE_224,
.Legacy_KECCAK_256 = keccak.BLOCK_SIZE_256,
.Legacy_KECCAK_384 = keccak.BLOCK_SIZE_384,
.Legacy_KECCAK_512 = keccak.BLOCK_SIZE_512,
.Insecure_MD5 = md5.BLOCK_SIZE,
.Insecure_SHA1 = sha1.BLOCK_SIZE,
}
// Context is a concrete instantiation of a specific hash algorithm.
Context :: struct {
_algo: Algorithm,
_impl: union {
blake2b.Context,
blake2s.Context,
sha2.Context_256,
sha2.Context_512,
sha3.Context,
sm3.Context,
keccak.Context,
md5.Context,
sha1.Context,
},
}
@(private)
_IMPL_IDS := [Algorithm]typeid {
.Invalid = nil,
.BLAKE2B = typeid_of(blake2b.Context),
.BLAKE2S = typeid_of(blake2s.Context),
.SHA224 = typeid_of(sha2.Context_256),
.SHA256 = typeid_of(sha2.Context_256),
.SHA384 = typeid_of(sha2.Context_512),
.SHA512 = typeid_of(sha2.Context_512),
.SHA512_256 = typeid_of(sha2.Context_512),
.SHA3_224 = typeid_of(sha3.Context),
.SHA3_256 = typeid_of(sha3.Context),
.SHA3_384 = typeid_of(sha3.Context),
.SHA3_512 = typeid_of(sha3.Context),
.SM3 = typeid_of(sm3.Context),
.Legacy_KECCAK_224 = typeid_of(keccak.Context),
.Legacy_KECCAK_256 = typeid_of(keccak.Context),
.Legacy_KECCAK_384 = typeid_of(keccak.Context),
.Legacy_KECCAK_512 = typeid_of(keccak.Context),
.Insecure_MD5 = typeid_of(md5.Context),
.Insecure_SHA1 = typeid_of(sha1.Context),
}
// init initializes a Context with a specific hash Algorithm.
init :: proc(ctx: ^Context, algorithm: Algorithm) {
if ctx._impl != nil {
reset(ctx)
}
// Directly specialize the union by setting the type ID (save a copy).
reflect.set_union_variant_typeid(
ctx._impl,
_IMPL_IDS[algorithm],
)
switch algorithm {
case .BLAKE2B:
blake2b.init(&ctx._impl.(blake2b.Context))
case .BLAKE2S:
blake2s.init(&ctx._impl.(blake2s.Context))
case .SHA224:
sha2.init_224(&ctx._impl.(sha2.Context_256))
case .SHA256:
sha2.init_256(&ctx._impl.(sha2.Context_256))
case .SHA384:
sha2.init_384(&ctx._impl.(sha2.Context_512))
case .SHA512:
sha2.init_512(&ctx._impl.(sha2.Context_512))
case .SHA512_256:
sha2.init_512_256(&ctx._impl.(sha2.Context_512))
case .SHA3_224:
sha3.init_224(&ctx._impl.(sha3.Context))
case .SHA3_256:
sha3.init_256(&ctx._impl.(sha3.Context))
case .SHA3_384:
sha3.init_384(&ctx._impl.(sha3.Context))
case .SHA3_512:
sha3.init_512(&ctx._impl.(sha3.Context))
case .SM3:
sm3.init(&ctx._impl.(sm3.Context))
case .Legacy_KECCAK_224:
keccak.init_224(&ctx._impl.(keccak.Context))
case .Legacy_KECCAK_256:
keccak.init_256(&ctx._impl.(keccak.Context))
case .Legacy_KECCAK_384:
keccak.init_384(&ctx._impl.(keccak.Context))
case .Legacy_KECCAK_512:
keccak.init_512(&ctx._impl.(keccak.Context))
case .Insecure_MD5:
md5.init(&ctx._impl.(md5.Context))
case .Insecure_SHA1:
sha1.init(&ctx._impl.(sha1.Context))
case .Invalid:
panic("crypto/hash: uninitialized algorithm")
case:
panic("crypto/hash: invalid algorithm")
}
ctx._algo = algorithm
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
switch &impl in ctx._impl {
case blake2b.Context:
blake2b.update(&impl, data)
case blake2s.Context:
blake2s.update(&impl, data)
case sha2.Context_256:
sha2.update(&impl, data)
case sha2.Context_512:
sha2.update(&impl, data)
case sha3.Context:
sha3.update(&impl, data)
case sm3.Context:
sm3.update(&impl, data)
case keccak.Context:
keccak.update(&impl, data)
case md5.Context:
md5.update(&impl, data)
case sha1.Context:
sha1.update(&impl, data)
case:
panic("crypto/hash: uninitialized algorithm")
}
}
// final finalizes the Context, writes the digest to hash, and calls
// reset on the Context.
//
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
switch &impl in ctx._impl {
case blake2b.Context:
blake2b.final(&impl, hash, finalize_clone)
case blake2s.Context:
blake2s.final(&impl, hash, finalize_clone)
case sha2.Context_256:
sha2.final(&impl, hash, finalize_clone)
case sha2.Context_512:
sha2.final(&impl, hash, finalize_clone)
case sha3.Context:
sha3.final(&impl, hash, finalize_clone)
case sm3.Context:
sm3.final(&impl, hash, finalize_clone)
case keccak.Context:
keccak.final(&impl, hash, finalize_clone)
case md5.Context:
md5.final(&impl, hash, finalize_clone)
case sha1.Context:
sha1.final(&impl, hash, finalize_clone)
case:
panic("crypto/hash: uninitialized algorithm")
}
if !finalize_clone {
reset(ctx)
}
}
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
// XXX/yawning: Maybe these cases should panic, because both cases,
// are probably bugs.
if ctx == other {
return
}
if ctx._impl != nil {
reset(ctx)
}
ctx._algo = other._algo
reflect.set_union_variant_typeid(
ctx._impl,
reflect.union_variant_typeid(other._impl),
)
switch &src_impl in other._impl {
case blake2b.Context:
blake2b.clone(&ctx._impl.(blake2b.Context), &src_impl)
case blake2s.Context:
blake2s.clone(&ctx._impl.(blake2s.Context), &src_impl)
case sha2.Context_256:
sha2.clone(&ctx._impl.(sha2.Context_256), &src_impl)
case sha2.Context_512:
sha2.clone(&ctx._impl.(sha2.Context_512), &src_impl)
case sha3.Context:
sha3.clone(&ctx._impl.(sha3.Context), &src_impl)
case sm3.Context:
sm3.clone(&ctx._impl.(sm3.Context), &src_impl)
case keccak.Context:
keccak.clone(&ctx._impl.(keccak.Context), &src_impl)
case md5.Context:
md5.clone(&ctx._impl.(md5.Context), &src_impl)
case sha1.Context:
sha1.clone(&ctx._impl.(sha1.Context), &src_impl)
case:
panic("crypto/hash: uninitialized algorithm")
}
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
switch &impl in ctx._impl {
case blake2b.Context:
blake2b.reset(&impl)
case blake2s.Context:
blake2s.reset(&impl)
case sha2.Context_256:
sha2.reset(&impl)
case sha2.Context_512:
sha2.reset(&impl)
case sha3.Context:
sha3.reset(&impl)
case sm3.Context:
sm3.reset(&impl)
case keccak.Context:
keccak.reset(&impl)
case md5.Context:
md5.reset(&impl)
case sha1.Context:
sha1.reset(&impl)
case:
// Unlike clone, calling reset repeatedly is fine.
}
ctx._algo = .Invalid
ctx._impl = nil
}
// algorithm returns the Algorithm used by a Context instance.
algorithm :: proc(ctx: ^Context) -> Algorithm {
return ctx._algo
}
// digest_size returns the digest size of a Context instance in bytes.
digest_size :: proc(ctx: ^Context) -> int {
return DIGEST_SIZES[ctx._algo]
}
// block_size returns the block size of a Context instance in bytes.
block_size :: proc(ctx: ^Context) -> int {
return BLOCK_SIZES[ctx._algo]
}
+103
View File
@@ -0,0 +1,103 @@
/*
package hkdf implements the HKDF HMAC-based Extract-and-Expand Key
Derivation Function.
See: https://www.rfc-editor.org/rfc/rfc5869
*/
package hkdf
import "core:crypto/hash"
import "core:crypto/hmac"
import "core:mem"
// extract_and_expand derives output keying material (OKM) via the
// HKDF-Extract and HKDF-Expand algorithms, with the specified has
// function, salt, input keying material (IKM), and optional info.
// The dst buffer must be less-than-or-equal to 255 HMAC tags.
extract_and_expand :: proc(algorithm: hash.Algorithm, salt, ikm, info, dst: []byte) {
h_len := hash.DIGEST_SIZES[algorithm]
tmp: [hash.MAX_DIGEST_SIZE]byte
prk := tmp[:h_len]
defer mem.zero_explicit(raw_data(prk), h_len)
extract(algorithm, salt, ikm, prk)
expand(algorithm, prk, info, dst)
}
// extract derives a pseudorandom key (PRK) via the HKDF-Extract algorithm,
// with the specified hash function, salt, and input keying material (IKM).
// It requires that the dst buffer be the HMAC tag size for the specified
// hash function.
extract :: proc(algorithm: hash.Algorithm, salt, ikm, dst: []byte) {
// PRK = HMAC-Hash(salt, IKM)
hmac.sum(algorithm, dst, ikm, salt)
}
// expand derives output keying material (OKM) via the HKDF-Expand algorithm,
// with the specified hash function, pseudorandom key (PRK), and optional
// info. The dst buffer must be less-than-or-equal to 255 HMAC tags.
expand :: proc(algorithm: hash.Algorithm, prk, info, dst: []byte) {
h_len := hash.DIGEST_SIZES[algorithm]
// (<= 255*HashLen)
dk_len := len(dst)
switch {
case dk_len == 0:
return
case dk_len > h_len * 255:
panic("crypto/hkdf: derived key too long")
case:
}
// The output OKM is calculated as follows:
//
// N = ceil(L/HashLen)
// T = T(1) | T(2) | T(3) | ... | T(N)
// OKM = first L octets of T
//
// where:
// T(0) = empty string (zero length)
// T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
// T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
// T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
// ...
n := dk_len / h_len
r := dk_len % h_len
base: hmac.Context
defer hmac.reset(&base)
hmac.init(&base, algorithm, prk)
dst_blk := dst
prev: []byte
for i in 1 ..= n {
_F(&base, prev, info, i, dst_blk[:h_len])
prev = dst_blk[:h_len]
dst_blk = dst_blk[h_len:]
}
if r > 0 {
tmp: [hash.MAX_DIGEST_SIZE]byte
blk := tmp[:h_len]
defer mem.zero_explicit(raw_data(blk), h_len)
_F(&base, prev, info, n + 1, blk)
copy(dst_blk, blk)
}
}
@(private)
_F :: proc(base: ^hmac.Context, prev, info: []byte, i: int, dst_blk: []byte) {
prf: hmac.Context
hmac.clone(&prf, base)
hmac.update(&prf, prev)
hmac.update(&prf, info)
hmac.update(&prf, []byte{u8(i)})
hmac.final(&prf, dst_blk)
}
+174
View File
@@ -0,0 +1,174 @@
/*
package hmac implements the HMAC MAC algorithm.
See:
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf
*/
package hmac
import "core:crypto"
import "core:crypto/hash"
import "core:mem"
// sum will compute the HMAC with the specified algorithm and key
// over msg, and write the computed tag to dst. It requires that
// the dst buffer is the tag size.
sum :: proc(algorithm: hash.Algorithm, dst, msg, key: []byte) {
ctx: Context
init(&ctx, algorithm, key)
update(&ctx, msg)
final(&ctx, dst)
}
// verify will verify the HMAC tag computed with the specified algorithm
// and key over msg and return true iff the tag is valid. It requires
// that the tag is correctly sized.
verify :: proc(algorithm: hash.Algorithm, tag, msg, key: []byte) -> bool {
tag_buf: [hash.MAX_DIGEST_SIZE]byte
derived_tag := tag_buf[:hash.DIGEST_SIZES[algorithm]]
sum(algorithm, derived_tag, msg, key)
return crypto.compare_constant_time(derived_tag, tag) == 1
}
// Context is a concrete instantiation of HMAC with a specific hash
// algorithm.
Context :: struct {
_o_hash: hash.Context, // H(k ^ ipad) (not finalized)
_i_hash: hash.Context, // H(k ^ opad) (not finalized)
_tag_sz: int,
_is_initialized: bool,
}
// init initializes a Context with a specific hash Algorithm and key.
init :: proc(ctx: ^Context, algorithm: hash.Algorithm, key: []byte) {
if ctx._is_initialized {
reset(ctx)
}
_init_hashes(ctx, algorithm, key)
ctx._tag_sz = hash.DIGEST_SIZES[algorithm]
ctx._is_initialized = true
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
assert(ctx._is_initialized)
hash.update(&ctx._i_hash, data)
}
// final finalizes the Context, writes the tag to dst, and calls
// reset on the Context.
final :: proc(ctx: ^Context, dst: []byte) {
assert(ctx._is_initialized)
defer (reset(ctx))
if len(dst) != ctx._tag_sz {
panic("crypto/hmac: invalid destination tag size")
}
hash.final(&ctx._i_hash, dst) // H((k ^ ipad) || text)
hash.update(&ctx._o_hash, dst) // H((k ^ opad) || H((k ^ ipad) || text))
hash.final(&ctx._o_hash, dst)
}
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
if ctx == other {
return
}
hash.clone(&ctx._o_hash, &other._o_hash)
hash.clone(&ctx._i_hash, &other._i_hash)
ctx._tag_sz = other._tag_sz
ctx._is_initialized = other._is_initialized
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
if !ctx._is_initialized {
return
}
hash.reset(&ctx._o_hash)
hash.reset(&ctx._i_hash)
ctx._tag_sz = 0
ctx._is_initialized = false
}
// algorithm returns the Algorithm used by a Context instance.
algorithm :: proc(ctx: ^Context) -> hash.Algorithm {
assert(ctx._is_initialized)
return hash.algorithm(&ctx._i_hash)
}
// tag_size returns the tag size of a Context instance in bytes.
tag_size :: proc(ctx: ^Context) -> int {
assert(ctx._is_initialized)
return ctx._tag_sz
}
@(private)
_I_PAD :: 0x36
_O_PAD :: 0x5c
@(private)
_init_hashes :: proc(ctx: ^Context, algorithm: hash.Algorithm, key: []byte) {
K0_buf: [hash.MAX_BLOCK_SIZE]byte
kPad_buf: [hash.MAX_BLOCK_SIZE]byte
kLen := len(key)
B := hash.BLOCK_SIZES[algorithm]
K0 := K0_buf[:B]
defer mem.zero_explicit(raw_data(K0), B)
switch {
case kLen == B, kLen < B:
// If the length of K = B: set K0 = K.
//
// If the length of K < B: append zeros to the end of K to
// create a B-byte string K0 (e.g., if K is 20 bytes in
// length and B = 64, then K will be appended with 44 zero
// bytes x00).
//
// K0 is zero-initialized, so the copy handles both cases.
copy(K0, key)
case kLen > B:
// If the length of K > B: hash K to obtain an L byte string,
// then append (B-L) zeros to create a B-byte string K0
// (i.e., K0 = H(K) || 00...00).
tmpCtx := &ctx._o_hash // Saves allocating a hash.Context.
hash.init(tmpCtx, algorithm)
hash.update(tmpCtx, key)
hash.final(tmpCtx, K0)
}
// Initialize the hashes, and write the padded keys:
// - ctx._i_hash -> H(K0 ^ ipad)
// - ctx._o_hash -> H(K0 ^ opad)
hash.init(&ctx._o_hash, algorithm)
hash.init(&ctx._i_hash, algorithm)
kPad := kPad_buf[:B]
defer mem.zero_explicit(raw_data(kPad), B)
for v, i in K0 {
kPad[i] = v ~ _I_PAD
}
hash.update(&ctx._i_hash, kPad)
for v, i in K0 {
kPad[i] = v ~ _O_PAD
}
hash.update(&ctx._o_hash, kPad)
}
+116
View File
@@ -0,0 +1,116 @@
/*
package kmac implements the KMAC MAC algorithm.
See:
- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
*/
package kmac
import "../_sha3"
import "core:crypto"
import "core:crypto/shake"
// MIN_KEY_SIZE_128 is the minimum key size for KMAC128 in bytes.
MIN_KEY_SIZE_128 :: 128 / 8
// MIN_KEY_SIZE_256 is the minimum key size for KMAC256 in bytes.
MIN_KEY_SIZE_256 :: 256 / 8
// MIN_TAG_SIZE is the absolute minimum tag size for KMAC in bytes (8.4.2).
// Most callers SHOULD use at least 128-bits if not 256-bits for the tag
// size.
MIN_TAG_SIZE :: 32 / 8
// sum will compute the KMAC with the specified security strength,
// key, and domain separator over msg, and write the computed digest to
// dst.
sum :: proc(sec_strength: int, dst, msg, key, domain_sep: []byte) {
ctx: Context
_init_kmac(&ctx, key, domain_sep, sec_strength)
update(&ctx, msg)
final(&ctx, dst)
}
// verify will verify the KMAC tag computed with the specified security
// strength, key and domain separator over msg and return true iff the
// tag is valid.
verify :: proc(sec_strength: int, tag, msg, key, domain_sep: []byte, allocator := context.temp_allocator) -> bool {
derived_tag := make([]byte, len(tag), allocator)
sum(sec_strength, derived_tag, msg, key, domain_sep)
return crypto.compare_constant_time(derived_tag, tag) == 1
}
// Context is a KMAC instance.
Context :: distinct shake.Context
// init_128 initializes a Context for KMAC28. This routine will panic if
// the key length is less than MIN_KEY_SIZE_128.
init_128 :: proc(ctx: ^Context, key, domain_sep: []byte) {
_init_kmac(ctx, key, domain_sep, 128)
}
// init_256 initializes a Context for KMAC256. This routine will panic if
// the key length is less than MIN_KEY_SIZE_256.
init_256 :: proc(ctx: ^Context, key, domain_sep: []byte) {
_init_kmac(ctx, key, domain_sep, 256)
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
assert(ctx.is_initialized)
shake.write(transmute(^shake.Context)(ctx), data)
}
// final finalizes the Context, writes the tag to dst, and calls reset
// on the Context. This routine will panic if the dst length is less than
// MIN_TAG_SIZE.
final :: proc(ctx: ^Context, dst: []byte) {
assert(ctx.is_initialized)
defer reset(ctx)
if len(dst) < MIN_TAG_SIZE {
panic("crypto/kmac: invalid KMAC tag_size, too short")
}
_sha3.final_cshake(transmute(^_sha3.Context)(ctx), dst)
}
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
if ctx == other {
return
}
shake.clone(transmute(^shake.Context)(ctx), transmute(^shake.Context)(other))
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
if !ctx.is_initialized {
return
}
shake.reset(transmute(^shake.Context)(ctx))
}
@(private)
_init_kmac :: proc(ctx: ^Context, key, s: []byte, sec_strength: int) {
if ctx.is_initialized {
reset(ctx)
}
if len(key) < sec_strength / 8 {
panic("crypto/kmac: invalid KMAC key, too short")
}
ctx_ := transmute(^_sha3.Context)(ctx)
_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
_sha3.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
}
@(private)
N_KMAC := []byte{'K', 'M', 'A', 'C'}
+59 -341
View File
@@ -1,3 +1,11 @@
/*
package keccak implements the Keccak hash algorithm family.
During the SHA-3 standardization process, the padding scheme was changed
thus Keccac and SHA-3 produce different outputs. Most users should use
SHA-3 and/or SHAKE instead, however the legacy algorithm is provided for
backward compatibility purposes.
*/
package keccak
/*
@@ -6,372 +14,82 @@ package keccak
List of contributors:
zhibog, dotbmp: Initial implementation.
Interface for the Keccak hashing algorithm.
This is done because the padding in the SHA3 standard was changed by the NIST, resulting in a different output.
*/
import "core:io"
import "core:os"
import "../../_sha3"
/*
High level API
*/
// DIGEST_SIZE_224 is the Keccak-224 digest size.
DIGEST_SIZE_224 :: 28
// DIGEST_SIZE_256 is the Keccak-256 digest size.
DIGEST_SIZE_256 :: 32
// DIGEST_SIZE_384 is the Keccak-384 digest size.
DIGEST_SIZE_384 :: 48
// DIGEST_SIZE_512 is the Keccak-512 digest size.
DIGEST_SIZE_512 :: 64
// hash_string_224 will hash the given input and return the
// computed hash
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// BLOCK_SIZE_224 is the Keccak-224 block size in bytes.
BLOCK_SIZE_224 :: _sha3.RATE_224
// BLOCK_SIZE_256 is the Keccak-256 block size in bytes.
BLOCK_SIZE_256 :: _sha3.RATE_256
// BLOCK_SIZE_384 is the Keccak-384 block size in bytes.
BLOCK_SIZE_384 :: _sha3.RATE_384
// BLOCK_SIZE_512 is the Keccak-512 block size in bytes.
BLOCK_SIZE_512 :: _sha3.RATE_512
// hash_bytes_224 will hash the given input and return the
// computed hash
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
hash: [DIGEST_SIZE_224]byte
ctx: Context
// Context is a Keccak instance.
Context :: distinct _sha3.Context
// init_224 initializes a Context for Keccak-224.
init_224 :: proc(ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_224
ctx.is_keccak = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_224 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_224 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
ctx: Context
ctx.mdlen = DIGEST_SIZE_224
ctx.is_keccak = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
hash: [DIGEST_SIZE_224]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_224
ctx.is_keccak = true
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_224 will read the file provided by the given handle
// and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
hash_stream_224,
hash_file_224,
hash_bytes_224,
hash_string_224,
hash_bytes_to_buffer_224,
hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: Context
// init_256 initializes a Context for Keccak-256.
init_256 :: proc(ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_256
ctx.is_keccak = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
ctx: Context
ctx.mdlen = DIGEST_SIZE_256
ctx.is_keccak = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_256
ctx.is_keccak = true
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
hash_stream_256,
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
hash: [DIGEST_SIZE_384]byte
ctx: Context
// init_384 initializes a Context for Keccak-384.
init_384 :: proc(ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_384
ctx.is_keccak = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_384 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_384 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
ctx: Context
ctx.mdlen = DIGEST_SIZE_384
ctx.is_keccak = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
hash: [DIGEST_SIZE_384]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_384
ctx.is_keccak = true
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_384 will read the file provided by the given handle
// and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
hash_stream_384,
hash_file_384,
hash_bytes_384,
hash_string_384,
hash_bytes_to_buffer_384,
hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
hash: [DIGEST_SIZE_512]byte
ctx: Context
// init_512 initializes a Context for Keccak-512.
init_512 :: proc(ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_512
ctx.is_keccak = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_512 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_512 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
ctx: Context
ctx.mdlen = DIGEST_SIZE_512
ctx.is_keccak = true
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
hash: [DIGEST_SIZE_512]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_512
ctx.is_keccak = true
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_512 will read the file provided by the given handle
// and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
hash_stream_512,
hash_file_512,
hash_bytes_512,
hash_string_512,
hash_bytes_to_buffer_512,
hash_string_to_buffer_512,
}
/*
Low level API
*/
Context :: _sha3.Sha3_Context
init :: proc(ctx: ^Context) {
ctx.is_keccak = true
_sha3.init(ctx)
@(private)
_init :: proc(ctx: ^Context) {
ctx.dsbyte = _sha3.DS_KECCAK
_sha3.init(transmute(^_sha3.Context)(ctx))
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
_sha3.update(ctx, data)
_sha3.update(transmute(^_sha3.Context)(ctx), data)
}
final :: proc(ctx: ^Context, hash: []byte) {
_sha3.final(ctx, hash)
// final finalizes the Context, writes the digest to hash, and calls
// reset on the Context.
//
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
_sha3.final(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
}
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
_sha3.reset(transmute(^_sha3.Context)(ctx))
}
+52 -100
View File
@@ -1,3 +1,13 @@
/*
package md5 implements the MD5 hash algorithm.
WARNING: The MD5 algorithm is known to be insecure and should only be
used for interoperating with legacy applications.
See:
- https://eprint.iacr.org/2005/075
- https://datatracker.ietf.org/doc/html/rfc1321
*/
package md5
/*
@@ -6,103 +16,29 @@ package md5
List of contributors:
zhibog, dotbmp: Initial implementation.
Implementation of the MD5 hashing algorithm, as defined in RFC 1321 <https://datatracker.ietf.org/doc/html/rfc1321>
*/
import "core:encoding/endian"
import "core:io"
import "core:math/bits"
import "core:mem"
import "core:os"
/*
High level API
*/
// DIGEST_SIZE is the MD5 digest size in bytes.
DIGEST_SIZE :: 16
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
// BLOCK_SIZE is the MD5 block size in bytes.
BLOCK_SIZE :: 64
// Context is a MD5 instance.
Context :: struct {
data: [BLOCK_SIZE]byte,
state: [4]u32,
bitlen: u64,
datalen: u32,
is_initialized: bool,
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
ctx: Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
hash_stream,
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
// init initializes a Context.
init :: proc(ctx: ^Context) {
ctx.state[0] = 0x67452301
ctx.state[1] = 0xefcdab89
@@ -115,6 +51,7 @@ init :: proc(ctx: ^Context) {
ctx.is_initialized = true
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
assert(ctx.is_initialized)
@@ -129,13 +66,26 @@ update :: proc(ctx: ^Context, data: []byte) {
}
}
final :: proc(ctx: ^Context, hash: []byte) {
// final finalizes the Context, writes the digest to hash, and calls
// reset on the Context.
//
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
assert(ctx.is_initialized)
if len(hash) < DIGEST_SIZE {
panic("crypto/md5: invalid destination digest size")
}
ctx := ctx
if finalize_clone {
tmp_ctx: Context
clone(&tmp_ctx, ctx)
ctx = &tmp_ctx
}
defer(reset(ctx))
i := ctx.datalen
if ctx.datalen < 56 {
@@ -163,25 +113,27 @@ final :: proc(ctx: ^Context, hash: []byte) {
for i = 0; i < DIGEST_SIZE / 4; i += 1 {
endian.unchecked_put_u32le(hash[i * 4:], ctx.state[i])
}
}
ctx.is_initialized = false
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^$T) {
ctx^ = other^
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^$T) {
if !ctx.is_initialized {
return
}
mem.zero_explicit(ctx, size_of(ctx^))
}
/*
MD5 implementation
*/
BLOCK_SIZE :: 64
Context :: struct {
data: [BLOCK_SIZE]byte,
state: [4]u32,
bitlen: u64,
datalen: u32,
is_initialized: bool,
}
/*
@note(zh): F, G, H and I, as mentioned in the RFC, have been inlined into FF, GG, HH
and II respectively, instead of declaring them separately.
+54 -101
View File
@@ -1,3 +1,14 @@
/*
package sha1 implements the SHA1 hash algorithm.
WARNING: The SHA1 algorithm is known to be insecure and should only be
used for interoperating with legacy applications.
See:
- https://eprint.iacr.org/2017/190
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
- https://datatracker.ietf.org/doc/html/rfc3174
*/
package sha1
/*
@@ -6,103 +17,30 @@ package sha1
List of contributors:
zhibog, dotbmp: Initial implementation.
Implementation of the SHA1 hashing algorithm, as defined in RFC 3174 <https://datatracker.ietf.org/doc/html/rfc3174>
*/
import "core:encoding/endian"
import "core:io"
import "core:math/bits"
import "core:mem"
import "core:os"
/*
High level API
*/
// DIGEST_SIZE is the SHA1 digest size in bytes.
DIGEST_SIZE :: 20
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
// BLOCK_SIZE is the SHA1 block size in bytes.
BLOCK_SIZE :: 64
// Context is a SHA1 instance.
Context :: struct {
data: [BLOCK_SIZE]byte,
state: [5]u32,
k: [4]u32,
bitlen: u64,
datalen: u32,
is_initialized: bool,
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
ctx: Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
hash_stream,
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
// init initializes a Context.
init :: proc(ctx: ^Context) {
ctx.state[0] = 0x67452301
ctx.state[1] = 0xefcdab89
@@ -120,6 +58,7 @@ init :: proc(ctx: ^Context) {
ctx.is_initialized = true
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
assert(ctx.is_initialized)
@@ -134,13 +73,26 @@ update :: proc(ctx: ^Context, data: []byte) {
}
}
final :: proc(ctx: ^Context, hash: []byte) {
// final finalizes the Context, writes the digest to hash, and calls
// reset on the Context.
//
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
assert(ctx.is_initialized)
if len(hash) < DIGEST_SIZE {
panic("crypto/sha1: invalid destination digest size")
}
ctx := ctx
if finalize_clone {
tmp_ctx: Context
clone(&tmp_ctx, ctx)
ctx = &tmp_ctx
}
defer(reset(ctx))
i := ctx.datalen
if ctx.datalen < 56 {
@@ -168,26 +120,27 @@ final :: proc(ctx: ^Context, hash: []byte) {
for i = 0; i < DIGEST_SIZE / 4; i += 1 {
endian.unchecked_put_u32be(hash[i * 4:], ctx.state[i])
}
}
ctx.is_initialized = false
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^$T) {
ctx^ = other^
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^$T) {
if !ctx.is_initialized {
return
}
mem.zero_explicit(ctx, size_of(ctx^))
}
/*
SHA1 implementation
*/
BLOCK_SIZE :: 64
Context :: struct {
data: [BLOCK_SIZE]byte,
datalen: u32,
bitlen: u64,
state: [5]u32,
k: [4]u32,
is_initialized: bool,
}
@(private)
transform :: proc "contextless" (ctx: ^Context, data: []byte) {
a, b, c, d, e, i, t: u32
+122
View File
@@ -0,0 +1,122 @@
/*
package pbkdf2 implements the PBKDF2 password-based key derivation function.
See: https://www.rfc-editor.org/rfc/rfc2898
*/
package pbkdf2
import "core:crypto/hash"
import "core:crypto/hmac"
import "core:encoding/endian"
import "core:mem"
// derive invokes PBKDF2-HMAC with the specified hash algorithm, password,
// salt, iteration count, and outputs the derived key to dst.
derive :: proc(
hmac_hash: hash.Algorithm,
password: []byte,
salt: []byte,
iterations: u32,
dst: []byte,
) {
h_len := hash.DIGEST_SIZES[hmac_hash]
// 1. If dkLen > (2^32 - 1) * hLen, output "derived key too long"
// and stop.
dk_len := len(dst)
switch {
case dk_len == 0:
return
case u64(dk_len) > u64(max(u32)) * u64(h_len):
// This is so beyond anything that is practical or reasonable,
// so just panic instead of returning an error.
panic("crypto/pbkdf2: derived key too long")
case:
}
// 2. Let l be the number of hLen-octet blocks in the derived key,
// rounding up, and let r be the number of octets in the last block.
l := dk_len / h_len // Don't need to round up.
r := dk_len % h_len
// 3. For each block of the derived key apply the function F defined
// below to the password P, the salt S, the iteration count c, and
// the block index to compute the block.
//
// 4. Concatenate the blocks and extract the first dkLen octets to
// produce a derived key DK.
//
// 5. Output the derived key DK.
// Each iteration of F is always `PRF (P, ...)`, so instantiate the
// PRF, and clone since memcpy is faster than having to re-initialize
// HMAC repeatedly.
base: hmac.Context
defer hmac.reset(&base)
hmac.init(&base, hmac_hash, password)
// Process all of the blocks that will be written directly to dst.
dst_blk := dst
for i in 1 ..= l { // F expects i starting at 1.
_F(&base, salt, iterations, u32(i), dst_blk[:h_len])
dst_blk = dst_blk[h_len:]
}
// Instead of rounding l up, just proceass the one extra block iff
// r != 0.
if r > 0 {
tmp: [hash.MAX_DIGEST_SIZE]byte
blk := tmp[:h_len]
defer mem.zero_explicit(raw_data(blk), h_len)
_F(&base, salt, iterations, u32(l + 1), blk)
copy(dst_blk, blk)
}
}
@(private)
_F :: proc(base: ^hmac.Context, salt: []byte, c: u32, i: u32, dst_blk: []byte) {
h_len := len(dst_blk)
tmp: [hash.MAX_DIGEST_SIZE]byte
u := tmp[:h_len]
defer mem.zero_explicit(raw_data(u), h_len)
// F (P, S, c, i) = U_1 \xor U_2 \xor ... \xor U_c
//
// where
//
// U_1 = PRF (P, S || INT (i)) ,
// U_2 = PRF (P, U_1) ,
// ...
// U_c = PRF (P, U_{c-1}) .
//
// Here, INT (i) is a four-octet encoding of the integer i, most
// significant octet first.
prf: hmac.Context
// U_1: PRF (P, S || INT (i))
hmac.clone(&prf, base)
hmac.update(&prf, salt)
endian.unchecked_put_u32be(u, i) // Use u as scratch space.
hmac.update(&prf, u[:4])
hmac.final(&prf, u)
copy(dst_blk, u)
// U_2 ... U_c: U_n = PRF (P, U_(n-1))
for _ in 1 ..< c {
hmac.clone(&prf, base)
hmac.update(&prf, u)
hmac.final(&prf, u)
// XOR dst_blk and u.
for v, i in u {
dst_blk[i] ~= v
}
}
}
+53 -28
View File
@@ -1,17 +1,31 @@
/*
package poly1305 implements the Poly1305 one-time MAC algorithm.
See:
- https://datatracker.ietf.org/doc/html/rfc8439
*/
package poly1305
import "core:crypto"
import field "core:crypto/_fiat/field_poly1305"
import "core:encoding/endian"
import "core:math/bits"
import "core:mem"
// KEY_SIZE is the Poly1305 key size in bytes.
KEY_SIZE :: 32
// TAG_SIZE is the Poly1305 tag size in bytes.
TAG_SIZE :: 16
@(private)
_BLOCK_SIZE :: 16
sum :: proc (dst, msg, key: []byte) {
// sum will compute the Poly1305 MAC with the key over msg, and write
// the computed tag to dst. It requires that the dst buffer is the tag
// size.
//
// The key SHOULD be unique and MUST be unpredictable for each invocation.
sum :: proc(dst, msg, key: []byte) {
ctx: Context = ---
init(&ctx, key)
@@ -19,13 +33,12 @@ sum :: proc (dst, msg, key: []byte) {
final(&ctx, dst)
}
verify :: proc (tag, msg, key: []byte) -> bool {
// verify will verify the Poly1305 tag computed with the key over msg and
// return true iff the tag is valid. It requires that the tag is correctly
// sized.
verify :: proc(tag, msg, key: []byte) -> bool {
ctx: Context = ---
derived_tag: [16]byte = ---
if len(tag) != TAG_SIZE {
panic("crypto/poly1305: invalid tag size")
}
derived_tag: [TAG_SIZE]byte = ---
init(&ctx, key)
update(&ctx, msg)
@@ -34,18 +47,19 @@ verify :: proc (tag, msg, key: []byte) -> bool {
return crypto.compare_constant_time(derived_tag[:], tag) == 1
}
// Context is a Poly1305 instance.
Context :: struct {
_r: field.Tight_Field_Element,
_a: field.Tight_Field_Element,
_s: field.Tight_Field_Element,
_buffer: [_BLOCK_SIZE]byte,
_leftover: int,
_r: field.Tight_Field_Element,
_a: field.Tight_Field_Element,
_s: [2]u64,
_buffer: [_BLOCK_SIZE]byte,
_leftover: int,
_is_initialized: bool,
}
init :: proc (ctx: ^Context, key: []byte) {
// init initializes a Context with the specified key. The key SHOULD be
// unique and MUST be unpredictable for each invocation.
init :: proc(ctx: ^Context, key: []byte) {
if len(key) != KEY_SIZE {
panic("crypto/poly1305: invalid key size")
}
@@ -53,11 +67,12 @@ init :: proc (ctx: ^Context, key: []byte) {
// r = le_bytes_to_num(key[0..15])
// r = clamp(r) (r &= 0xffffffc0ffffffc0ffffffc0fffffff)
tmp_lo := endian.unchecked_get_u64le(key[0:]) & 0x0ffffffc0fffffff
tmp_hi := endian.unchecked_get_u64le(key[8:]) & 0xffffffc0ffffffc
tmp_hi := endian.unchecked_get_u64le(key[8:]) & 0x0ffffffc0ffffffc
field.fe_from_u64s(&ctx._r, tmp_lo, tmp_hi)
// s = le_bytes_to_num(key[16..31])
field.fe_from_bytes(&ctx._s, key[16:32], 0)
ctx._s[0] = endian.unchecked_get_u64le(key[16:])
ctx._s[1] = endian.unchecked_get_u64le(key[24:])
// a = 0
field.fe_zero(&ctx._a)
@@ -68,7 +83,8 @@ init :: proc (ctx: ^Context, key: []byte) {
ctx._is_initialized = true
}
update :: proc (ctx: ^Context, data: []byte) {
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
assert(ctx._is_initialized)
msg := data
@@ -105,8 +121,11 @@ update :: proc (ctx: ^Context, data: []byte) {
}
}
final :: proc (ctx: ^Context, dst: []byte) {
// final finalizes the Context, writes the tag to dst, and calls
// reset on the Context.
final :: proc(ctx: ^Context, dst: []byte) {
assert(ctx._is_initialized)
defer reset(ctx)
if len(dst) != TAG_SIZE {
panic("poly1305: invalid destination tag size")
@@ -121,19 +140,25 @@ final :: proc (ctx: ^Context, dst: []byte) {
_blocks(ctx, ctx._buffer[:], true)
}
// a += s
field.fe_add(field.fe_relax_cast(&ctx._a), &ctx._a, &ctx._s) // _a unreduced
field.fe_carry(&ctx._a, field.fe_relax_cast(&ctx._a)) // _a reduced
// return num_to_16_le_bytes(a)
// a += s (NOT mod p)
tmp: [32]byte = ---
field.fe_to_bytes(&tmp, &ctx._a)
copy_slice(dst, tmp[0:16])
reset(ctx)
c: u64
lo := endian.unchecked_get_u64le(tmp[0:])
hi := endian.unchecked_get_u64le(tmp[8:])
lo, c = bits.add_u64(lo, ctx._s[0], 0)
hi, _ = bits.add_u64(hi, ctx._s[1], c)
// return num_to_16_le_bytes(a)
endian.unchecked_put_u64le(dst[0:], lo)
endian.unchecked_put_u64le(dst[8:], hi)
}
reset :: proc (ctx: ^Context) {
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
mem.zero_explicit(&ctx._r, size_of(ctx._r))
mem.zero_explicit(&ctx._a, size_of(ctx._a))
mem.zero_explicit(&ctx._s, size_of(ctx._s))
@@ -143,7 +168,7 @@ reset :: proc (ctx: ^Context) {
}
@(private)
_blocks :: proc (ctx: ^Context, msg: []byte, final := false) {
_blocks :: proc "contextless" (ctx: ^Context, msg: []byte, final := false) {
n: field.Tight_Field_Element = ---
final_byte := byte(!final)
+16
View File
@@ -0,0 +1,16 @@
//+build freebsd, openbsd
package crypto
foreign import libc "system:c"
foreign libc {
arc4random_buf :: proc(buf: [^]byte, nbytes: uint) ---
}
_rand_bytes :: proc(dst: []byte) {
arc4random_buf(raw_data(dst), len(dst))
}
_has_rand_bytes :: proc () -> bool {
return true
}
+16
View File
@@ -0,0 +1,16 @@
package crypto
import "core:fmt"
import "core:sys/darwin"
_rand_bytes :: proc(dst: []byte) {
res := darwin.SecRandomCopyBytes(count=len(dst), bytes=raw_data(dst))
if res != .Success {
msg := darwin.CFStringCopyToOdinString(darwin.SecCopyErrorMessageString(res))
panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", res, msg))
}
}
_has_rand_bytes :: proc () -> bool {
return true
}
+12 -4
View File
@@ -1,7 +1,15 @@
//+build !linux
//+build !windows
//+build !openbsd
//+build !freebsd
//+build !darwin
//+build !js
package crypto
when ODIN_OS != .Linux && ODIN_OS != .OpenBSD && ODIN_OS != .Windows && ODIN_OS != .JS {
_rand_bytes :: proc(dst: []byte) {
unimplemented("crypto: rand_bytes not supported on this OS")
}
_rand_bytes :: proc(dst: []byte) {
unimplemented("crypto: rand_bytes not supported on this OS")
}
_has_rand_bytes :: proc () -> bool {
return false
}
+4
View File
@@ -18,3 +18,7 @@ _rand_bytes :: proc(dst: []byte) {
dst = dst[to_read:]
}
}
_has_rand_bytes :: proc () -> bool {
return true
}
+4
View File
@@ -34,3 +34,7 @@ _rand_bytes :: proc (dst: []byte) {
dst = dst[n_read:]
}
}
_has_rand_bytes :: proc () -> bool {
return true
}
-12
View File
@@ -1,12 +0,0 @@
package crypto
import "core:c"
foreign import libc "system:c"
foreign libc {
arc4random_buf :: proc "c" (buf: rawptr, nbytes: c.size_t) ---
}
_rand_bytes :: proc (dst: []byte) {
arc4random_buf(raw_data(dst), len(dst))
}
+4
View File
@@ -21,3 +21,7 @@ _rand_bytes :: proc(dst: []byte) {
}
}
}
_has_rand_bytes :: proc () -> bool {
return true
}
+510
View File
@@ -0,0 +1,510 @@
/*
package ristretto255 implement the ristretto255 prime-order group.
See:
- https://www.rfc-editor.org/rfc/rfc9496
*/
package ristretto255
import grp "core:crypto/_edwards25519"
import field "core:crypto/_fiat/field_curve25519"
import "core:mem"
// ELEMENT_SIZE is the size of a byte-encoded ristretto255 group element.
ELEMENT_SIZE :: 32
// WIDE_ELEMENT_SIZE is the side of a wide byte-encoded ristretto255
// group element.
WIDE_ELEMENT_SIZE :: 64
@(private)
FE_NEG_ONE := field.Tight_Field_Element {
2251799813685228,
2251799813685247,
2251799813685247,
2251799813685247,
2251799813685247,
}
@(private)
FE_INVSQRT_A_MINUS_D := field.Tight_Field_Element {
278908739862762,
821645201101625,
8113234426968,
1777959178193151,
2118520810568447,
}
@(private)
FE_ONE_MINUS_D_SQ := field.Tight_Field_Element {
1136626929484150,
1998550399581263,
496427632559748,
118527312129759,
45110755273534,
}
@(private)
FE_D_MINUS_ONE_SQUARED := field.Tight_Field_Element {
1507062230895904,
1572317787530805,
683053064812840,
317374165784489,
1572899562415810,
}
@(private)
FE_SQRT_AD_MINUS_ONE := field.Tight_Field_Element {
2241493124984347,
425987919032274,
2207028919301688,
1220490630685848,
974799131293748,
}
@(private)
GE_IDENTITY := Group_Element{grp.GE_IDENTITY, true}
// Group_Element is a ristretto255 group element. The zero-initialized
// value is invalid.
Group_Element :: struct {
// WARNING: While the internal representation is an Edwards25519
// group element, this is not guaranteed to always be the case,
// and your code *WILL* break if you mess with `_p`.
_p: grp.Group_Element,
_is_initialized: bool,
}
// ge_clear clears ge to the uninitialized state.
ge_clear :: proc "contextless" (ge: ^Group_Element) {
mem.zero_explicit(ge, size_of(Group_Element))
}
// ge_set sets `ge = a`.
ge_set :: proc(ge, a: ^Group_Element) {
_ge_assert_initialized([]^Group_Element{a})
grp.ge_set(&ge._p, &a._p)
ge._is_initialized = true
}
// ge_identity sets ge to the identity (neutral) element.
ge_identity :: proc "contextless" (ge: ^Group_Element) {
grp.ge_identity(&ge._p)
ge._is_initialized = true
}
// ge_generator sets ge to the group generator.
ge_generator :: proc "contextless" (ge: ^Group_Element) {
grp.ge_generator(&ge._p)
ge._is_initialized = true
}
// ge_set_bytes sets ge to the result of decoding b as a ristretto255
// group element, and returns true on success.
@(require_results)
ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
// 1. Interpret the string as an unsigned integer s in little-endian
// representation. If the length of the string is not 32 bytes or
// if the resulting value is >= p, decoding fails.
//
// 2. If IS_NEGATIVE(s) returns TRUE, decoding fails.
if len(b) != ELEMENT_SIZE {
return false
}
if b[31] & 128 != 0 || b[0] & 1 != 0 {
// Fail early if b is clearly > p, or negative.
return false
}
b_ := transmute(^[32]byte)(raw_data(b))
s: field.Tight_Field_Element = ---
defer field.fe_clear(&s)
field.fe_from_bytes(&s, b_)
if field.fe_equal_bytes(&s, b_) != 1 {
// Reject non-canonical encodings of s.
return false
}
// 3. Process s as follows:
v, u1, u2: field.Loose_Field_Element = ---, ---, ---
tmp, u2_sqr: field.Tight_Field_Element = ---, ---
// ss = s^2
// u1 = 1 - ss
// u2 = 1 + ss
// u2_sqr = u2^2
field.fe_carry_square(&tmp, field.fe_relax_cast(&s))
field.fe_sub(&u1, &field.FE_ONE, &tmp)
field.fe_add(&u2, &field.FE_ONE, &tmp)
field.fe_carry_square(&u2_sqr, &u2)
// v = -(D * u1^2) - u2_sqr
field.fe_carry_square(&tmp, &u1)
field.fe_carry_mul(&tmp, field.fe_relax_cast(&grp.FE_D), field.fe_relax_cast(&tmp))
field.fe_carry_add(&tmp, &tmp, &u2_sqr)
field.fe_opp(&v, &tmp)
// (was_square, invsqrt) = SQRT_RATIO_M1(1, v * u2_sqr)
field.fe_carry_mul(&tmp, &v, field.fe_relax_cast(&u2_sqr))
was_square := field.fe_carry_sqrt_ratio_m1(
&tmp,
field.fe_relax_cast(&field.FE_ONE),
field.fe_relax_cast(&tmp),
)
// den_x = invsqrt * u2
// den_y = invsqrt * den_x * v
x, y, t: field.Tight_Field_Element = ---, ---, ---
field.fe_carry_mul(&x, field.fe_relax_cast(&tmp), &u2)
field.fe_carry_mul(&y, field.fe_relax_cast(&tmp), field.fe_relax_cast(&x))
field.fe_carry_mul(&y, field.fe_relax_cast(&y), &v)
// x = CT_ABS(2 * s * den_x)
field.fe_carry_mul(&x, field.fe_relax_cast(&s), field.fe_relax_cast(&x))
field.fe_carry_add(&x, &x, &x)
field.fe_carry_abs(&x, &x)
// y = u1 * den_y
field.fe_carry_mul(&y, &u1, field.fe_relax_cast(&y))
// t = x * y
field.fe_carry_mul(&t, field.fe_relax_cast(&x), field.fe_relax_cast(&y))
field.fe_clear_vec([]^field.Loose_Field_Element{&v, &u1, &u2})
field.fe_clear_vec([]^field.Tight_Field_Element{&tmp, &u2_sqr})
defer field.fe_clear_vec([]^field.Tight_Field_Element{&x, &y, &t})
// 4. If was_square is FALSE, IS_NEGATIVE(t) returns TRUE, or y = 0,
// decoding fails. Otherwise, return the group element represented
// by the internal representation (x, y, 1, t) as the result of
// decoding.
switch {
case was_square == 0:
// Not sure why the RFC doesn't have this just fail early.
return false
case field.fe_is_negative(&t) != 0:
return false
case field.fe_equal(&y, &field.FE_ZERO) != 0:
return false
}
field.fe_set(&ge._p.x, &x)
field.fe_set(&ge._p.y, &y)
field.fe_one(&ge._p.z)
field.fe_set(&ge._p.t, &t)
ge._is_initialized = true
return true
}
// ge_set_wide_bytes sets ge to the result of deriving a ristretto255
// group element, from a wide (512-bit) byte string.
ge_set_wide_bytes :: proc(ge: ^Group_Element, b: []byte) {
if len(b) != WIDE_ELEMENT_SIZE {
panic("crypto/ristretto255: invalid wide input size")
}
// The element derivation function on an input string b proceeds as
// follows:
//
// 1. Compute P1 as MAP(b[0:32]).
// 2. Compute P2 as MAP(b[32:64]).
// 3. Return P1 + P2.
p1, p2: Group_Element = ---, ---
ge_map(&p1, b[0:32])
ge_map(&p2, b[32:64])
ge_add(ge, &p1, &p2)
ge_clear(&p1)
ge_clear(&p2)
}
// ge_bytes sets dst to the canonical encoding of ge.
ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
_ge_assert_initialized([]^Group_Element{ge})
if len(dst) != ELEMENT_SIZE {
panic("crypto/ristretto255: invalid destination size")
}
x0, y0, z0, t0 := &ge._p.x, &ge._p.y, &ge._p.z, &ge._p.t
// 1. Process the internal representation into a field element s as
// follows:
// u1 = (z0 + y0) * (z0 - y0)
// u2 = x0 * y0
u1, u2: field.Tight_Field_Element = ---, ---
tmp1, tmp2: field.Loose_Field_Element = ---, ---
field.fe_add(&tmp1, z0, y0)
field.fe_sub(&tmp2, z0, y0)
field.fe_carry_mul(&u1, &tmp1, &tmp2)
field.fe_carry_mul(&u2, field.fe_relax_cast(x0), field.fe_relax_cast(y0))
// Ignore was_square since this is always square.
// (_, invsqrt) = SQRT_RATIO_M1(1, u1 * u2^2)
tmp: field.Tight_Field_Element = ---
field.fe_carry_square(&tmp, field.fe_relax_cast(&u2))
field.fe_carry_mul(&tmp, field.fe_relax_cast(&u1), field.fe_relax_cast(&tmp))
_ = field.fe_carry_sqrt_ratio_m1(
&tmp,
field.fe_relax_cast(&field.FE_ONE),
field.fe_relax_cast(&tmp),
)
// den1 = invsqrt * u1
// den2 = invsqrt * u2
// z_inv = den1 * den2 * t0
den1, den2 := &u1, &u2
z_inv: field.Tight_Field_Element = ---
field.fe_carry_mul(den1, field.fe_relax_cast(&tmp), field.fe_relax_cast(&u1))
field.fe_carry_mul(den2, field.fe_relax_cast(&tmp), field.fe_relax_cast(&u2))
field.fe_carry_mul(&z_inv, field.fe_relax_cast(den1), field.fe_relax_cast(den2))
field.fe_carry_mul(&z_inv, field.fe_relax_cast(&z_inv), field.fe_relax_cast(t0))
// rotate = IS_NEGATIVE(t0 * z_inv)
// Note: Reordered from the RFC because invsqrt is no longer needed.
field.fe_carry_mul(&tmp, field.fe_relax_cast(t0), field.fe_relax_cast(&z_inv))
rotate := field.fe_is_negative(&tmp)
// ix0 = x0 * SQRT_M1
// iy0 = y0 * SQRT_M1
// enchanted_denominator = den1 * INVSQRT_A_MINUS_D
ix0, iy0: field.Tight_Field_Element = ---, ---
field.fe_carry_mul(&ix0, field.fe_relax_cast(x0), field.fe_relax_cast(&field.FE_SQRT_M1))
field.fe_carry_mul(&iy0, field.fe_relax_cast(y0), field.fe_relax_cast(&field.FE_SQRT_M1))
field.fe_carry_mul(&tmp, field.fe_relax_cast(den1), field.fe_relax_cast(&FE_INVSQRT_A_MINUS_D))
// Conditionally rotate x and y.
// x = CT_SELECT(iy0 IF rotate ELSE x0)
// y = CT_SELECT(ix0 IF rotate ELSE y0)
// z = z0
// den_inv = CT_SELECT(enchanted_denominator IF rotate ELSE den2)
x, y: field.Tight_Field_Element = ---, ---
field.fe_cond_select(&x, x0, &iy0, rotate)
field.fe_cond_select(&y, y0, &ix0, rotate)
field.fe_cond_select(&tmp, den2, &tmp, rotate)
// y = CT_SELECT(-y IF IS_NEGATIVE(x * z_inv) ELSE y)
field.fe_carry_mul(&x, field.fe_relax_cast(&x), field.fe_relax_cast(&z_inv))
field.fe_cond_negate(&y, &y, field.fe_is_negative(&x))
// s = CT_ABS(den_inv * (z - y))
field.fe_sub(&tmp1, z0, &y)
field.fe_carry_mul(&tmp, field.fe_relax_cast(&tmp), &tmp1)
field.fe_carry_abs(&tmp, &tmp)
// 2. Return the 32-byte little-endian encoding of s. More
// specifically, this is the encoding of the canonical
// representation of s as an integer between 0 and p-1, inclusive.
dst_ := transmute(^[32]byte)(raw_data(dst))
field.fe_to_bytes(dst_, &tmp)
field.fe_clear_vec([]^field.Tight_Field_Element{&u1, &u2, &tmp, &z_inv, &ix0, &iy0, &x, &y})
field.fe_clear_vec([]^field.Loose_Field_Element{&tmp1, &tmp2})
}
// ge_add sets `ge = a + b`.
ge_add :: proc(ge, a, b: ^Group_Element) {
_ge_assert_initialized([]^Group_Element{a, b})
grp.ge_add(&ge._p, &a._p, &b._p)
ge._is_initialized = true
}
// ge_double sets `ge = a + a`.
ge_double :: proc(ge, a: ^Group_Element) {
_ge_assert_initialized([]^Group_Element{a})
grp.ge_double(&ge._p, &a._p)
ge._is_initialized = true
}
// ge_negate sets `ge = -a`.
ge_negate :: proc(ge, a: ^Group_Element) {
_ge_assert_initialized([]^Group_Element{a})
grp.ge_negate(&ge._p, &a._p)
ge._is_initialized = true
}
// ge_scalarmult sets `ge = A * sc`.
ge_scalarmult :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
_ge_assert_initialized([]^Group_Element{A})
grp.ge_scalarmult(&ge._p, &A._p, sc)
ge._is_initialized = true
}
// ge_scalarmult_generator sets `ge = G * sc`
ge_scalarmult_generator :: proc "contextless" (ge: ^Group_Element, sc: ^Scalar) {
grp.ge_scalarmult_basepoint(&ge._p, sc)
ge._is_initialized = true
}
// ge_scalarmult_vartime sets `ge = A * sc` in variable time.
ge_scalarmult_vartime :: proc(ge, A: ^Group_Element, sc: ^Scalar) {
_ge_assert_initialized([]^Group_Element{A})
grp.ge_scalarmult_vartime(&ge._p, &A._p, sc)
ge._is_initialized = true
}
// ge_double_scalarmult_generator_vartime sets `ge = A * a + G * b` in variable
// time.
ge_double_scalarmult_generator_vartime :: proc(
ge: ^Group_Element,
a: ^Scalar,
A: ^Group_Element,
b: ^Scalar,
) {
_ge_assert_initialized([]^Group_Element{A})
grp.ge_double_scalarmult_basepoint_vartime(&ge._p, a, &A._p, b)
ge._is_initialized = true
}
// ge_cond_negate sets `ge = a` iff `ctrl == 0` and `ge = -a` iff `ctrl == 1`.
// Behavior for all other values of ctrl are undefined,
ge_cond_negate :: proc(ge, a: ^Group_Element, ctrl: int) {
_ge_assert_initialized([]^Group_Element{a})
grp.ge_cond_negate(&ge._p, &a._p, ctrl)
ge._is_initialized = true
}
// ge_cond_assign sets `ge = ge` iff `ctrl == 0` and `ge = a` iff `ctrl == 1`.
// Behavior for all other values of ctrl are undefined,
ge_cond_assign :: proc(ge, a: ^Group_Element, ctrl: int) {
_ge_assert_initialized([]^Group_Element{ge, a})
grp.ge_cond_assign(&ge._p, &a._p, ctrl)
}
// ge_cond_select sets `ge = a` iff `ctrl == 0` and `ge = b` iff `ctrl == 1`.
// Behavior for all other values of ctrl are undefined,
ge_cond_select :: proc(ge, a, b: ^Group_Element, ctrl: int) {
_ge_assert_initialized([]^Group_Element{a, b})
grp.ge_cond_select(&ge._p, &a._p, &b._p, ctrl)
ge._is_initialized = true
}
// ge_equal returns 1 iff `a == b`, and 0 otherwise.
@(require_results)
ge_equal :: proc(a, b: ^Group_Element) -> int {
_ge_assert_initialized([]^Group_Element{a, b})
// CT_EQ(x1 * y2, y1 * x2) | CT_EQ(y1 * y2, x1 * x2)
ax_by, ay_bx, ay_by, ax_bx: field.Tight_Field_Element = ---, ---, ---, ---
field.fe_carry_mul(&ax_by, field.fe_relax_cast(&a._p.x), field.fe_relax_cast(&b._p.y))
field.fe_carry_mul(&ay_bx, field.fe_relax_cast(&a._p.y), field.fe_relax_cast(&b._p.x))
field.fe_carry_mul(&ay_by, field.fe_relax_cast(&a._p.y), field.fe_relax_cast(&b._p.y))
field.fe_carry_mul(&ax_bx, field.fe_relax_cast(&a._p.x), field.fe_relax_cast(&b._p.x))
ret := field.fe_equal(&ax_by, &ay_bx) | field.fe_equal(&ay_by, &ax_bx)
field.fe_clear_vec([]^field.Tight_Field_Element{&ax_by, &ay_bx, &ay_by, &ax_bx})
return ret
}
// ge_is_identity returns 1 iff `ge` is the identity element, and 0 otherwise.
@(require_results)
ge_is_identity :: proc(ge: ^Group_Element) -> int {
return ge_equal(ge, &GE_IDENTITY)
}
@(private)
ge_map :: proc "contextless" (ge: ^Group_Element, b: []byte) {
b_ := transmute(^[32]byte)(raw_data(b))
// The MAP function is defined on 32-byte strings as:
//
// 1. Mask the most significant bit in the final byte of the string,
// and interpret the string as an unsigned integer r in little-
// endian representation. Reduce r modulo p to obtain a field
// element t.
// * Masking the most significant bit is equivalent to interpreting
// the whole string as an unsigned integer in little-endian
// representation and then reducing it modulo 2^255.
t: field.Tight_Field_Element = ---
field.fe_from_bytes(&t, b_)
// 2. Process t as follows:
//
// r = SQRT_M1 * t^2
// u = (r + 1) * ONE_MINUS_D_SQ
// v = (-1 - r*D) * (r + D)
tmp1: field.Loose_Field_Element = ---
r, u, v: field.Tight_Field_Element = ---, ---, ---
field.fe_carry_square(&r, field.fe_relax_cast(&t))
field.fe_carry_mul(&r, field.fe_relax_cast(&field.FE_SQRT_M1), field.fe_relax_cast(&r))
field.fe_add(&tmp1, &field.FE_ONE, &r)
field.fe_carry_mul(&u, &tmp1, field.fe_relax_cast(&FE_ONE_MINUS_D_SQ))
field.fe_carry_mul(&v, field.fe_relax_cast(&r), field.fe_relax_cast(&grp.FE_D))
field.fe_carry_add(&v, &field.FE_ONE, &v)
field.fe_carry_opp(&v, &v)
field.fe_add(&tmp1, &r, &grp.FE_D)
field.fe_carry_mul(&v, field.fe_relax_cast(&v), &tmp1)
// (was_square, s) = SQRT_RATIO_M1(u, v)
// s_prime = -CT_ABS(s*t)
// s = CT_SELECT(s IF was_square ELSE s_prime)
// c = CT_SELECT(-1 IF was_square ELSE r)
s, s_prime, c: field.Tight_Field_Element = ---, ---, ---
was_square := field.fe_carry_sqrt_ratio_m1(
&s,
field.fe_relax_cast(&u),
field.fe_relax_cast(&v),
)
field.fe_carry_mul(&s_prime, field.fe_relax_cast(&s), field.fe_relax_cast(&t))
field.fe_carry_abs(&s_prime, &s_prime)
field.fe_carry_opp(&s_prime, &s_prime)
field.fe_cond_select(&s, &s_prime, &s, was_square)
field.fe_cond_select(&c, &r, &FE_NEG_ONE, was_square)
// N = c * (r - 1) * D_MINUS_ONE_SQ - v
N: field.Tight_Field_Element = ---
field.fe_sub(&tmp1, &r, &field.FE_ONE)
field.fe_carry_mul(&N, field.fe_relax_cast(&c), &tmp1)
field.fe_carry_mul(&N, field.fe_relax_cast(&N), field.fe_relax_cast(&FE_D_MINUS_ONE_SQUARED))
field.fe_carry_sub(&N, &N, &v)
// w0 = 2 * s * v
// w1 = N * SQRT_AD_MINUS_ONE
// w2 = 1 - s^2
// w3 = 1 + s^2
w0, w1: field.Tight_Field_Element = ---, ---
w2, w3: field.Loose_Field_Element = ---, ---
field.fe_carry_mul(&w0, field.fe_relax_cast(&s), field.fe_relax_cast(&v))
field.fe_carry_add(&w0, &w0, &w0)
field.fe_carry_mul(&w1, field.fe_relax_cast(&N), field.fe_relax_cast(&FE_SQRT_AD_MINUS_ONE))
field.fe_carry_square(&s, field.fe_relax_cast(&s))
field.fe_sub(&w2, &field.FE_ONE, &s)
field.fe_add(&w3, &field.FE_ONE, &s)
// 3. Return the group element represented by the internal
// representation (w0*w3, w2*w1, w1*w3, w0*w2).
field.fe_carry_mul(&ge._p.x, field.fe_relax_cast(&w0), &w3)
field.fe_carry_mul(&ge._p.y, &w2, field.fe_relax_cast(&w1))
field.fe_carry_mul(&ge._p.z, field.fe_relax_cast(&w1), &w3)
field.fe_carry_mul(&ge._p.t, field.fe_relax_cast(&w0), &w2)
ge._is_initialized = true
field.fe_clear_vec([]^field.Tight_Field_Element{&r, &u, &v, &s, &s_prime, &c, &N, &w0, &w1})
field.fe_clear_vec([]^field.Loose_Field_Element{&tmp1, &w2, &w3})
}
@(private)
_ge_assert_initialized :: proc(ges: []^Group_Element) {
for ge in ges {
if !ge._is_initialized {
panic("crypto/ristretto255: uninitialized group element")
}
}
}
@@ -0,0 +1,97 @@
package ristretto255
import grp "core:crypto/_edwards25519"
// SCALAR_SIZE is the size of a byte-encoded ristretto255 scalar.
SCALAR_SIZE :: 32
// WIDE_SCALAR_SIZE is the size of a wide byte-encoded ristretto255
// scalar.
WIDE_SCALAR_SIZE :: 64
// Scalar is a ristretto255 scalar. The zero-initialized value is valid,
// and represents `0`.
Scalar :: grp.Scalar
// sc_clear clears sc to the uninitialized state.
sc_clear :: proc "contextless" (sc: ^Scalar) {
grp.sc_clear(sc)
}
// sc_set sets `sc = a`.
sc_set :: proc "contextless" (sc, a: ^Scalar) {
grp.sc_set(sc, a)
}
// sc_set_u64 sets `sc = i`.
sc_set_u64 :: proc "contextless" (sc: ^Scalar, i: u64) {
grp.sc_set_u64(sc, i)
}
// sc_set_bytes sets sc to the result of decoding b as a ristretto255
// scalar, and returns true on success.
@(require_results)
sc_set_bytes :: proc(sc: ^Scalar, b: []byte) -> bool {
if len(b) != SCALAR_SIZE {
return false
}
return grp.sc_set_bytes(sc, b)
}
// sc_set_wide_bytes sets sc to the result of deriving a ristretto255
// scalar, from a wide (512-bit) byte string by interpreting b as a
// little-endian value, and reducing it mod the group order.
sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
if len(b) != WIDE_SCALAR_SIZE {
panic("crypto/ristretto255: invalid wide input size")
}
b_ := transmute(^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
grp.sc_set_bytes_wide(sc, b_)
}
// sc_bytes sets dst to the canonical encoding of sc.
sc_bytes :: proc(sc: ^Scalar, dst: []byte) {
if len(dst) != SCALAR_SIZE {
panic("crypto/ristretto255: invalid destination size")
}
grp.sc_bytes(dst, sc)
}
// sc_add sets `sc = a + b`.
sc_add :: proc "contextless" (sc, a, b: ^Scalar) {
grp.sc_add(sc, a, b)
}
// sc_sub sets `sc = a - b`.
sc_sub :: proc "contextless" (sc, a, b: ^Scalar) {
grp.sc_sub(sc, a, b)
}
// sc_negate sets `sc = -a`.
sc_negate :: proc "contextless" (sc, a: ^Scalar) {
grp.sc_negate(sc, a)
}
// sc_mul sets `sc = a * b`.
sc_mul :: proc "contextless" (sc, a, b: ^Scalar) {
grp.sc_mul(sc, a, b)
}
// sc_square sets `sc = a^2`.
sc_square :: proc "contextless" (sc, a: ^Scalar) {
grp.sc_square(sc, a)
}
// sc_cond_assign sets `sc = sc` iff `ctrl == 0` and `sc = a` iff `ctrl == 1`.
// Behavior for all other values of ctrl are undefined,
sc_cond_assign :: proc(sc, a: ^Scalar, ctrl: int) {
grp.sc_cond_assign(sc, a, ctrl)
}
// sc_equal returns 1 iff `a == b`, and 0 otherwise.
@(require_results)
sc_equal :: proc(a, b: ^Scalar) -> int {
return grp.sc_equal(a, b)
}
+92 -429
View File
@@ -1,3 +1,10 @@
/*
package sha2 implements the SHA2 hash algorithm family.
See:
- https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
- https://datatracker.ietf.org/doc/html/rfc3874
*/
package sha2
/*
@@ -6,431 +13,83 @@ package sha2
List of contributors:
zhibog, dotbmp: Initial implementation.
Implementation of the SHA2 hashing algorithm, as defined in <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf>
and in RFC 3874 <https://datatracker.ietf.org/doc/html/rfc3874>
*/
import "core:encoding/endian"
import "core:io"
import "core:math/bits"
import "core:os"
/*
High level API
*/
import "core:mem"
// DIGEST_SIZE_224 is the SHA-224 digest size in bytes.
DIGEST_SIZE_224 :: 28
// DIGEST_SIZE_256 is the SHA-256 digest size in bytes.
DIGEST_SIZE_256 :: 32
// DIGEST_SIZE_384 is the SHA-384 digest size in bytes.
DIGEST_SIZE_384 :: 48
// DIGEST_SIZE_512 is the SHA-512 digest size in bytes.
DIGEST_SIZE_512 :: 64
// DIGEST_SIZE_512_256 is the SHA-512/256 digest size in bytes.
DIGEST_SIZE_512_256 :: 32
// hash_string_224 will hash the given input and return the
// computed hash
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
// BLOCK_SIZE_256 is the SHA-224 and SHA-256 block size in bytes.
BLOCK_SIZE_256 :: 64
// BLOCK_SIZE_512 is the SHA-384, SHA-512, and SHA-512/256 block size
// in bytes.
BLOCK_SIZE_512 :: 128
// Context_256 is a SHA-224 or SHA-256 instance.
Context_256 :: struct {
block: [BLOCK_SIZE_256]byte,
h: [8]u32,
bitlength: u64,
length: u64,
md_bits: int,
is_initialized: bool,
}
// hash_bytes_224 will hash the given input and return the
// computed hash
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
hash: [DIGEST_SIZE_224]byte
ctx: Context_256
// Context_512 is a SHA-384, SHA-512 or SHA-512/256 instance.
Context_512 :: struct {
block: [BLOCK_SIZE_512]byte,
h: [8]u64,
bitlength: u64,
length: u64,
md_bits: int,
is_initialized: bool,
}
// init_224 initializes a Context_256 for SHA-224.
init_224 :: proc(ctx: ^Context_256) {
ctx.md_bits = 224
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_224 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_224 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
ctx: Context_256
ctx.md_bits = 224
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
hash: [DIGEST_SIZE_224]byte
ctx: Context_256
ctx.md_bits = 224
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_224 will read the file provided by the given handle
// and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
hash_stream_224,
hash_file_224,
hash_bytes_224,
hash_string_224,
hash_bytes_to_buffer_224,
hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: Context_256
// init_256 initializes a Context_256 for SHA-256.
init_256 :: proc(ctx: ^Context_256) {
ctx.md_bits = 256
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
ctx: Context_256
ctx.md_bits = 256
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: Context_256
ctx.md_bits = 256
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
hash_stream_256,
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
hash: [DIGEST_SIZE_384]byte
ctx: Context_512
// init_384 initializes a Context_512 for SHA-384.
init_384 :: proc(ctx: ^Context_512) {
ctx.md_bits = 384
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_384 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_384 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
ctx: Context_512
ctx.md_bits = 384
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
hash: [DIGEST_SIZE_384]byte
ctx: Context_512
ctx.md_bits = 384
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_384 will read the file provided by the given handle
// and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
hash_stream_384,
hash_file_384,
hash_bytes_384,
hash_string_384,
hash_bytes_to_buffer_384,
hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
hash: [DIGEST_SIZE_512]byte
ctx: Context_512
// init_512 initializes a Context_512 for SHA-512.
init_512 :: proc(ctx: ^Context_512) {
ctx.md_bits = 512
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_512 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_512 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
ctx: Context_512
ctx.md_bits = 512
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
hash: [DIGEST_SIZE_512]byte
ctx: Context_512
ctx.md_bits = 512
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_512 will read the file provided by the given handle
// and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
hash_stream_512,
hash_file_512,
hash_bytes_512,
hash_string_512,
hash_bytes_to_buffer_512,
hash_string_to_buffer_512,
}
// hash_string_512_256 will hash the given input and return the
// computed hash
hash_string_512_256 :: proc(data: string) -> [DIGEST_SIZE_512_256]byte {
return hash_bytes_512_256(transmute([]byte)(data))
}
// hash_bytes_512_256 will hash the given input and return the
// computed hash
hash_bytes_512_256 :: proc(data: []byte) -> [DIGEST_SIZE_512_256]byte {
hash: [DIGEST_SIZE_512_256]byte
ctx: Context_512
// init_512_256 initializes a Context_512 for SHA-512/256.
init_512_256 :: proc(ctx: ^Context_512) {
ctx.md_bits = 256
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_512_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_512_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_512_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_512_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_512_256 :: proc(data, hash: []byte) {
ctx: Context_512
ctx.md_bits = 256
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_512_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_512_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512_256]byte, bool) {
hash: [DIGEST_SIZE_512_256]byte
ctx: Context_512
ctx.md_bits = 256
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_512_256 will read the file provided by the given handle
// and compute a hash
hash_file_512_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512_256]byte, bool) {
if !load_at_once {
return hash_stream_512_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512_256(buf[:]), ok
}
}
return [DIGEST_SIZE_512_256]byte{}, false
}
hash_512_256 :: proc {
hash_stream_512_256,
hash_file_512_256,
hash_bytes_512_256,
hash_string_512_256,
hash_bytes_to_buffer_512_256,
hash_string_to_buffer_512_256,
}
/*
Low level API
*/
init :: proc(ctx: ^$T) {
@(private)
_init :: proc(ctx: ^$T) {
when T == Context_256 {
switch ctx.md_bits {
case 224:
@@ -497,13 +156,14 @@ init :: proc(ctx: ^$T) {
ctx.is_initialized = true
}
// update adds more data to the Context.
update :: proc(ctx: ^$T, data: []byte) {
assert(ctx.is_initialized)
when T == Context_256 {
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
} else when T == Context_512 {
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
CURR_BLOCK_SIZE :: BLOCK_SIZE_512
}
data := data
@@ -528,21 +188,34 @@ update :: proc(ctx: ^$T, data: []byte) {
}
}
final :: proc(ctx: ^$T, hash: []byte) {
// final finalizes the Context, writes the digest to hash, and calls
// reset on the Context.
//
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^$T, hash: []byte, finalize_clone: bool = false) {
assert(ctx.is_initialized)
if len(hash) * 8 < ctx.md_bits {
panic("crypto/sha2: invalid destination digest size")
}
ctx := ctx
if finalize_clone {
tmp_ctx: T
clone(&tmp_ctx, ctx)
ctx = &tmp_ctx
}
defer(reset(ctx))
length := ctx.length
raw_pad: [SHA512_BLOCK_SIZE]byte
raw_pad: [BLOCK_SIZE_512]byte
when T == Context_256 {
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
pm_len := 8 // 64-bits for length
} else when T == Context_512 {
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
CURR_BLOCK_SIZE :: BLOCK_SIZE_512
pm_len := 16 // 128-bits for length
}
pad := raw_pad[:CURR_BLOCK_SIZE]
@@ -576,37 +249,27 @@ final :: proc(ctx: ^$T, hash: []byte) {
endian.unchecked_put_u64be(hash[i * 8:], ctx.h[i])
}
}
}
ctx.is_initialized = false
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^$T) {
ctx^ = other^
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^$T) {
if !ctx.is_initialized {
return
}
mem.zero_explicit(ctx, size_of(ctx^))
}
/*
SHA2 implementation
*/
SHA256_BLOCK_SIZE :: 64
SHA512_BLOCK_SIZE :: 128
Context_256 :: struct {
block: [SHA256_BLOCK_SIZE]byte,
h: [8]u32,
bitlength: u64,
length: u64,
md_bits: int,
is_initialized: bool,
}
Context_512 :: struct {
block: [SHA512_BLOCK_SIZE]byte,
h: [8]u64,
bitlength: u64,
length: u64,
md_bits: int,
is_initialized: bool,
}
@(private)
sha256_k := [64]u32 {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
@@ -737,12 +400,12 @@ sha2_transf :: proc "contextless" (ctx: ^$T, data: []byte) {
w: [64]u32
wv: [8]u32
t1, t2: u32
CURR_BLOCK_SIZE :: SHA256_BLOCK_SIZE
CURR_BLOCK_SIZE :: BLOCK_SIZE_256
} else when T == Context_512 {
w: [80]u64
wv: [8]u64
t1, t2: u64
CURR_BLOCK_SIZE :: SHA512_BLOCK_SIZE
CURR_BLOCK_SIZE :: BLOCK_SIZE_512
}
data := data
+61 -328
View File
@@ -1,3 +1,13 @@
/*
package sha3 implements the SHA3 hash algorithm family.
The SHAKE XOF can be found in crypto/shake. While discouraged if the
pre-standardization Keccak algorithm is required, it can be found in
crypto/legacy/keccak.
See:
- https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf
*/
package sha3
/*
@@ -6,359 +16,82 @@ package sha3
List of contributors:
zhibog, dotbmp: Initial implementation.
Interface for the SHA3 hashing algorithm. The SHAKE functionality can be found in package shake.
If you wish to compute a Keccak hash, you can use the keccak package, it will use the original padding.
*/
import "core:io"
import "core:os"
import "../_sha3"
/*
High level API
*/
// DIGEST_SIZE_224 is the SHA3-224 digest size.
DIGEST_SIZE_224 :: 28
// DIGEST_SIZE_256 is the SHA3-256 digest size.
DIGEST_SIZE_256 :: 32
// DIGEST_SIZE_384 is the SHA3-384 digest size.
DIGEST_SIZE_384 :: 48
// DIGEST_SIZE_512 is the SHA3-512 digest size.
DIGEST_SIZE_512 :: 64
// hash_string_224 will hash the given input and return the
// computed hash
hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// BLOCK_SIZE_224 is the SHA3-224 block size in bytes.
BLOCK_SIZE_224 :: _sha3.RATE_224
// BLOCK_SIZE_256 is the SHA3-256 block size in bytes.
BLOCK_SIZE_256 :: _sha3.RATE_256
// BLOCK_SIZE_384 is the SHA3-384 block size in bytes.
BLOCK_SIZE_384 :: _sha3.RATE_384
// BLOCK_SIZE_512 is the SHA3-512 block size in bytes.
BLOCK_SIZE_512 :: _sha3.RATE_512
// hash_bytes_224 will hash the given input and return the
// computed hash
hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
hash: [DIGEST_SIZE_224]byte
ctx: Context
// Context is a SHA3 instance.
Context :: distinct _sha3.Context
// init_224 initializes a Context for SHA3-224.
init_224 :: proc(ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_224
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_224 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_224 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
ctx: Context
ctx.mdlen = DIGEST_SIZE_224
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
hash: [DIGEST_SIZE_224]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_224
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_224 will read the file provided by the given handle
// and compute a hash
hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_224(buf[:]), ok
}
}
return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
hash_stream_224,
hash_file_224,
hash_bytes_224,
hash_string_224,
hash_bytes_to_buffer_224,
hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: Context
// init_256 initializes a Context for SHA3-256.
init_256 :: proc(ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_256
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
ctx: Context
ctx.mdlen = DIGEST_SIZE_256
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_256
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
hash_stream_256,
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
hash: [DIGEST_SIZE_384]byte
ctx: Context
// init_384 initializes a Context for SHA3-384.
init_384 :: proc(ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_384
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_384 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_384 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
ctx: Context
ctx.mdlen = DIGEST_SIZE_384
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
hash: [DIGEST_SIZE_384]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_384
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_384 will read the file provided by the given handle
// and compute a hash
hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_384(buf[:]), ok
}
}
return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
hash_stream_384,
hash_file_384,
hash_bytes_384,
hash_string_384,
hash_bytes_to_buffer_384,
hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
hash: [DIGEST_SIZE_512]byte
ctx: Context
// init_512 initializes a Context for SHA3-512.
init_512 :: proc(ctx: ^Context) {
ctx.mdlen = DIGEST_SIZE_512
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
_init(ctx)
}
// hash_string_to_buffer_512 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_512 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
ctx: Context
ctx.mdlen = DIGEST_SIZE_512
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
hash: [DIGEST_SIZE_512]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_512
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_512 will read the file provided by the given handle
// and compute a hash
hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_512(buf[:]), ok
}
}
return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
hash_stream_512,
hash_file_512,
hash_bytes_512,
hash_string_512,
hash_bytes_to_buffer_512,
hash_string_to_buffer_512,
}
/*
Low level API
*/
Context :: _sha3.Sha3_Context
init :: proc(ctx: ^Context) {
_sha3.init(ctx)
@(private)
_init :: proc(ctx: ^Context) {
ctx.dsbyte = _sha3.DS_SHA3
_sha3.init(transmute(^_sha3.Context)(ctx))
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
_sha3.update(ctx, data)
_sha3.update(transmute(^_sha3.Context)(ctx), data)
}
final :: proc(ctx: ^Context, hash: []byte) {
_sha3.final(ctx, hash)
// final finalizes the Context, writes the digest to hash, and calls
// reset on the Context.
//
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
_sha3.final(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
}
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
_sha3.reset(transmute(^_sha3.Context)(ctx))
}
+44 -178
View File
@@ -1,3 +1,12 @@
/*
package shake implements the SHAKE and cSHAKE XOF algorithm families.
The SHA3 hash algorithm can be found in the crypto/sha3.
See:
- https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.202.pdf
- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
*/
package shake
/*
@@ -6,201 +15,58 @@ package shake
List of contributors:
zhibog, dotbmp: Initial implementation.
Interface for the SHAKE hashing algorithm.
The SHA3 functionality can be found in package sha3.
TODO: This should provide an incremental squeeze interface, in addition
to the one-shot final call.
*/
import "core:io"
import "core:os"
import "../_sha3"
/*
High level API
*/
// Context is a SHAKE128, SHAKE256, cSHAKE128, or cSHAKE256 instance.
Context :: distinct _sha3.Context
DIGEST_SIZE_128 :: 16
DIGEST_SIZE_256 :: 32
// hash_string_128 will hash the given input and return the
// computed hash
hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128(transmute([]byte)(data))
// init_128 initializes a Context for SHAKE128.
init_128 :: proc(ctx: ^Context) {
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 128)
}
// hash_bytes_128 will hash the given input and return the
// computed hash
hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
hash: [DIGEST_SIZE_128]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_128
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
// init_256 initializes a Context for SHAKE256.
init_256 :: proc(ctx: ^Context) {
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 256)
}
// hash_string_to_buffer_128 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
// init_cshake_128 initializes a Context for cSHAKE128.
init_cshake_128 :: proc(ctx: ^Context, domain_sep: []byte) {
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, domain_sep, 128)
}
// hash_bytes_to_buffer_128 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
ctx: Context
ctx.mdlen = DIGEST_SIZE_128
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
// init_cshake_256 initializes a Context for cSHAKE256.
init_cshake_256 :: proc(ctx: ^Context, domain_sep: []byte) {
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, domain_sep, 256)
}
// hash_stream_128 will read the stream in chunks and compute a
// hash from its contents
hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
hash: [DIGEST_SIZE_128]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_128
init(&ctx)
// write writes more data into the SHAKE instance. This MUST not be called
// after any reads have been done, and attempts to do so will panic.
write :: proc(ctx: ^Context, data: []byte) {
_sha3.update(transmute(^_sha3.Context)(ctx), data)
}
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
// read reads output from the SHAKE instance. There is no practical upper
// limit to the amount of data that can be read from SHAKE. After read has
// been called one or more times, further calls to write will panic.
read :: proc(ctx: ^Context, dst: []byte) {
ctx_ := transmute(^_sha3.Context)(ctx)
if !ctx.is_finalized {
_sha3.shake_xof(ctx_)
}
final(&ctx, hash[:])
return hash, true
_sha3.shake_out(ctx_, dst)
}
// hash_file_128 will read the file provided by the given handle
// and compute a hash
hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_128(buf[:]), ok
}
}
return [DIGEST_SIZE_128]byte{}, false
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
}
hash_128 :: proc {
hash_stream_128,
hash_file_128,
hash_bytes_128,
hash_string_128,
hash_bytes_to_buffer_128,
hash_string_to_buffer_128,
}
// hash_string_256 will hash the given input and return the
// computed hash
hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
hash: [DIGEST_SIZE_256]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_256
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer_256 will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer_256 will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
ctx: Context
ctx.mdlen = DIGEST_SIZE_256
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
}
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
hash: [DIGEST_SIZE_256]byte
ctx: Context
ctx.mdlen = DIGEST_SIZE_256
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file_256 will read the file provided by the given handle
// and compute a hash
hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes_256(buf[:]), ok
}
}
return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
hash_stream_256,
hash_file_256,
hash_bytes_256,
hash_string_256,
hash_bytes_to_buffer_256,
hash_string_to_buffer_256,
}
/*
Low level API
*/
Context :: _sha3.Sha3_Context
init :: proc(ctx: ^Context) {
_sha3.init(ctx)
}
update :: proc(ctx: ^Context, data: []byte) {
_sha3.update(ctx, data)
}
final :: proc(ctx: ^Context, hash: []byte) {
_sha3.shake_xof(ctx)
_sha3.shake_out(ctx, hash[:])
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
_sha3.reset(transmute(^_sha3.Context)(ctx))
}
+49 -100
View File
@@ -1,3 +1,9 @@
/*
package sm3 implements the SM3 hash algorithm.
See:
- https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02
*/
package sm3
/*
@@ -6,102 +12,29 @@ package sm3
List of contributors:
zhibog, dotbmp: Initial implementation.
Implementation of the SM3 hashing algorithm, as defined in <https://datatracker.ietf.org/doc/html/draft-sca-cfrg-sm3-02>
*/
import "core:encoding/endian"
import "core:io"
import "core:math/bits"
import "core:os"
/*
High level API
*/
import "core:mem"
// DIGEST_SIZE is the SM3 digest size in bytes.
DIGEST_SIZE :: 32
// hash_string will hash the given input and return the
// computed hash
hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
// BLOCK_SIZE is the SM3 block size in bytes.
BLOCK_SIZE :: 64
// Context is a SM3 instance.
Context :: struct {
state: [8]u32,
x: [BLOCK_SIZE]byte,
bitlength: u64,
length: u64,
is_initialized: bool,
}
// hash_bytes will hash the given input and return the
// computed hash
hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
hash: [DIGEST_SIZE]byte
ctx: Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash[:])
return hash
}
// hash_string_to_buffer will hash the given input and assign the
// computed hash to the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_string_to_buffer :: proc(data: string, hash: []byte) {
hash_bytes_to_buffer(transmute([]byte)(data), hash)
}
// hash_bytes_to_buffer will hash the given input and write the
// computed hash into the second parameter.
// It requires that the destination buffer is at least as big as the digest size
hash_bytes_to_buffer :: proc(data, hash: []byte) {
ctx: Context
init(&ctx)
update(&ctx, data)
final(&ctx, hash)
}
// hash_stream will read the stream in chunks and compute a
// hash from its contents
hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
hash: [DIGEST_SIZE]byte
ctx: Context
init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
read := 1
for read > 0 {
read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
}
final(&ctx, hash[:])
return hash, true
}
// hash_file will read the file provided by the given handle
// and compute a hash
hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
if buf, ok := os.read_entire_file(hd); ok {
return hash_bytes(buf[:]), ok
}
}
return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
hash_stream,
hash_file,
hash_bytes,
hash_string,
hash_bytes_to_buffer,
hash_string_to_buffer,
}
/*
Low level API
*/
// init initializes a Context.
init :: proc(ctx: ^Context) {
ctx.state[0] = IV[0]
ctx.state[1] = IV[1]
@@ -118,6 +51,7 @@ init :: proc(ctx: ^Context) {
ctx.is_initialized = true
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
assert(ctx.is_initialized)
@@ -143,13 +77,26 @@ update :: proc(ctx: ^Context, data: []byte) {
}
}
final :: proc(ctx: ^Context, hash: []byte) {
// final finalizes the Context, writes the digest to hash, and calls
// reset on the Context.
//
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
assert(ctx.is_initialized)
if len(hash) < DIGEST_SIZE {
panic("crypto/sm3: invalid destination digest size")
}
ctx := ctx
if finalize_clone {
tmp_ctx: Context
clone(&tmp_ctx, ctx)
ctx = &tmp_ctx
}
defer(reset(ctx))
length := ctx.length
pad: [BLOCK_SIZE]byte
@@ -168,25 +115,27 @@ final :: proc(ctx: ^Context, hash: []byte) {
for i := 0; i < DIGEST_SIZE / 4; i += 1 {
endian.unchecked_put_u32be(hash[i * 4:], ctx.state[i])
}
}
ctx.is_initialized = false
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
ctx^ = other^
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
if !ctx.is_initialized {
return
}
mem.zero_explicit(ctx, size_of(ctx^))
}
/*
SM3 implementation
*/
BLOCK_SIZE :: 64
Context :: struct {
state: [8]u32,
x: [BLOCK_SIZE]byte,
bitlength: u64,
length: u64,
is_initialized: bool,
}
@(private)
IV := [8]u32 {
0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
+66
View File
@@ -0,0 +1,66 @@
/*
package tuplehash implements the TupleHash and TupleHashXOF algorithms.
See:
- https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-185.pdf
*/
package tuplehash
import "../_sha3"
// Context is a TupleHash or TupleHashXOF instance.
Context :: distinct _sha3.Context
// init_128 initializes a Context for TupleHash128 or TupleHashXOF128.
init_128 :: proc(ctx: ^Context, domain_sep: []byte) {
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 128)
}
// init_256 initializes a Context for TupleHash256 or TupleHashXOF256.
init_256 :: proc(ctx: ^Context, domain_sep: []byte) {
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 256)
}
// write_element writes a tuple element into the TupleHash or TupleHashXOF
// instance. This MUST not be called after any reads have been done, and
// any attempts to do so will panic.
write_element :: proc(ctx: ^Context, data: []byte) {
_, _ = _sha3.encode_string(transmute(^_sha3.Context)(ctx), data)
}
// final finalizes the Context, writes the digest to hash, and calls
// reset on the Context.
//
// Iff finalize_clone is set, final will work on a copy of the Context,
// which is useful for for calculating rolling digests.
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
_sha3.final_cshake(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
}
// read reads output from the TupleHashXOF instance. There is no practical
// upper limit to the amount of data that can be read from TupleHashXOF.
// After read has been called one or more times, further calls to
// write_element will panic.
read :: proc(ctx: ^Context, dst: []byte) {
ctx_ := transmute(^_sha3.Context)(ctx)
if !ctx.is_finalized {
_sha3.encode_byte_len(ctx_, 0, false) // right_encode
_sha3.shake_xof(ctx_)
}
_sha3.shake_out(ctx_, dst)
}
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
}
// reset sanitizes the Context. The Context must be re-initialized to
// be used again.
reset :: proc(ctx: ^Context) {
_sha3.reset(transmute(^_sha3.Context)(ctx))
}
@(private)
N_TUPLEHASH := []byte{'T', 'u', 'p', 'l', 'e', 'H', 'a', 's', 'h'}
+21 -14
View File
@@ -1,9 +1,18 @@
/*
package x25519 implements the X25519 (aka curve25519) Elliptic-Curve
Diffie-Hellman key exchange protocol.
See:
- https://www.rfc-editor.org/rfc/rfc7748
*/
package x25519
import field "core:crypto/_fiat/field_curve25519"
import "core:mem"
// SCALAR_SIZE is the size of a X25519 scalar (private key) in bytes.
SCALAR_SIZE :: 32
// POINT_SIZE is the size of a X25519 point (public key/shared secret) in bytes.
POINT_SIZE :: 32
@(private)
@@ -14,11 +23,11 @@ _scalar_bit :: #force_inline proc "contextless" (s: ^[32]byte, i: int) -> u8 {
if i < 0 {
return 0
}
return (s[i>>3] >> uint(i&7)) & 1
return (s[i >> 3] >> uint(i & 7)) & 1
}
@(private)
_scalarmult :: proc (out, scalar, point: ^[32]byte) {
_scalarmult :: proc "contextless" (out, scalar, point: ^[32]byte) {
// Montgomery pseduo-multiplication taken from Monocypher.
// computes the scalar product
@@ -26,7 +35,7 @@ _scalarmult :: proc (out, scalar, point: ^[32]byte) {
field.fe_from_bytes(&x1, point)
// computes the actual scalar product (the result is in x2 and z2)
x2, x3, z2, z3: field.Tight_Field_Element = ---, ---, ---, ---
x2, x3, z2, z3: field.Tight_Field_Element = ---, ---, ---, ---
t0, t1: field.Loose_Field_Element = ---, ---
// Montgomery ladder
@@ -38,7 +47,7 @@ _scalarmult :: proc (out, scalar, point: ^[32]byte) {
field.fe_one(&z3)
swap: int
for pos := 255-1; pos >= 0; pos = pos - 1 {
for pos := 255 - 1; pos >= 0; pos = pos - 1 {
// constant time conditional swap before ladder step
b := int(_scalar_bit(scalar, pos))
swap ~= b // xor trick avoids swapping at the end of the loop
@@ -85,16 +94,13 @@ _scalarmult :: proc (out, scalar, point: ^[32]byte) {
field.fe_carry_mul(&x2, field.fe_relax_cast(&x2), field.fe_relax_cast(&z2))
field.fe_to_bytes(out, &x2)
mem.zero_explicit(&x1, size_of(x1))
mem.zero_explicit(&x2, size_of(x2))
mem.zero_explicit(&x3, size_of(x3))
mem.zero_explicit(&z2, size_of(z2))
mem.zero_explicit(&z3, size_of(z3))
mem.zero_explicit(&t0, size_of(t0))
mem.zero_explicit(&t1, size_of(t1))
field.fe_clear_vec([]^field.Tight_Field_Element{&x1, &x2, &x3, &z2, &z3})
field.fe_clear_vec([]^field.Loose_Field_Element{&t0, &t1})
}
scalarmult :: proc (dst, scalar, point: []byte) {
// scalarmult "multiplies" the provided scalar and point, and writes the
// resulting point to dst.
scalarmult :: proc(dst, scalar, point: []byte) {
if len(scalar) != SCALAR_SIZE {
panic("crypto/x25519: invalid scalar size")
}
@@ -123,7 +129,8 @@ scalarmult :: proc (dst, scalar, point: []byte) {
mem.zero_explicit(&d, size_of(d))
}
scalarmult_basepoint :: proc (dst, scalar: []byte) {
// TODO/perf: Switch to using a precomputed table.
// scalarmult_basepoint "multiplies" the provided scalar with the X25519
// base point and writes the resulting point to dst.
scalarmult_basepoint :: proc(dst, scalar: []byte) {
scalarmult(dst, scalar, _BASE_POINT[:])
}
+18 -25
View File
@@ -123,40 +123,34 @@ Returns:
See doc.odin for an example.
*/
initialize_symbols :: proc(symbol_table: ^$T, library_path: string, symbol_prefix := "", handle_field_name := "__handle") -> (count: int, ok: bool) where intrinsics.type_is_struct(T) {
initialize_symbols :: proc(
symbol_table: ^$T, library_path: string,
symbol_prefix := "", handle_field_name := "__handle",
) -> (count: int = -1, ok: bool = false) where intrinsics.type_is_struct(T) {
assert(symbol_table != nil)
handle: Library
if handle, ok = load_library(library_path); !ok {
return -1, false
}
// `symbol_table` must be a struct because of the where clause, so this can't fail.
ti := runtime.type_info_base(type_info_of(T))
s, _ := ti.variant.(runtime.Type_Info_Struct)
handle := load_library(library_path) or_return
// Buffer to concatenate the prefix + symbol name.
prefixed_symbol_buf: [2048]u8 = ---
sym_ptr: rawptr
for field_name, i in s.names {
count = 0
for field, i in reflect.struct_fields_zipped(T) {
// Calculate address of struct member
field_ptr := rawptr(uintptr(rawptr(symbol_table)) + uintptr(s.offsets[i]))
field_ptr := rawptr(uintptr(symbol_table) + field.offset)
// If we've come across the struct member for the handle, store it and continue scanning for other symbols.
if field_name == handle_field_name {
if field.name == handle_field_name {
// We appear to be hot reloading. Unload previous incarnation of the library.
if old_handle := (^Library)(field_ptr)^; old_handle != nil {
if ok = unload_library(old_handle); !ok {
return count, ok
}
unload_library(old_handle) or_return
}
(^Library)(field_ptr)^ = handle
continue
}
// We're not the library handle, so the field needs to be a pointer type, be it a procedure pointer or an exported global.
if !(reflect.is_procedure(s.types[i]) || reflect.is_pointer(s.types[i])) {
if !(reflect.is_procedure(field.type) || reflect.is_pointer(field.type)) {
continue
}
@@ -164,22 +158,21 @@ initialize_symbols :: proc(symbol_table: ^$T, library_path: string, symbol_prefi
prefixed_name: string
// Do we have a symbol override tag?
if override, tag_ok := reflect.struct_tag_lookup(reflect.Struct_Tag(s.tags[i]), "dynlib"); tag_ok {
prefixed_name = string(override)
if override, tag_ok := reflect.struct_tag_lookup(field.tag, "dynlib"); tag_ok {
prefixed_name = override
}
// No valid symbol override tag found, fall back to `<symbol_prefix>name`.
if len(prefixed_name) == 0 {
offset := copy(prefixed_symbol_buf[:], symbol_prefix)
copy(prefixed_symbol_buf[offset:], field_name)
prefixed_name = string(prefixed_symbol_buf[:len(symbol_prefix) + len(field_name)])
copy(prefixed_symbol_buf[offset:], field.name)
prefixed_name = string(prefixed_symbol_buf[:len(symbol_prefix) + len(field.name)])
}
// Assign procedure (or global) pointer if found.
if sym_ptr, ok = symbol_address(handle, prefixed_name); ok {
(^rawptr)(field_ptr)^ = sym_ptr
count += 1
}
sym_ptr := symbol_address(handle, prefixed_name) or_continue
(^rawptr)(field_ptr)^ = sym_ptr
count += 1
}
return count, count > 0
}
+1 -1
View File
@@ -177,7 +177,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
}
defer file.nodes = file.nodes[:node_count]
for node_idx in 0..<header.internal_node_count {
for _ in 0..<header.internal_node_count {
node := &file.nodes[node_count]
type := read_value(r, Node_Type) or_return
if type > max(Node_Type) {
+109 -24
View File
@@ -51,6 +51,11 @@ Marshal_Options :: struct {
// NOTE: This will temp allocate and sort a list for each map.
sort_maps_by_key: bool,
// Output enum value's name instead of its underlying value.
//
// NOTE: If a name isn't found it'll use the underlying value.
use_enum_names: bool,
// Internal state
indentation: int,
mjson_skipped_first_braces_start: bool,
@@ -228,6 +233,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Matrix:
return .Unsupported_Type
case runtime.Type_Info_Bit_Field:
return .Unsupported_Type
case runtime.Type_Info_Array:
opt_write_start(w, opt, '[') or_return
for i in 0..<info.count {
@@ -238,7 +246,6 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
opt_write_end(w, opt, ']') or_return
case runtime.Type_Info_Enumerated_Array:
index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
opt_write_start(w, opt, '[') or_return
for i in 0..<info.count {
opt_write_iteration(w, opt, i) or_return
@@ -291,14 +298,14 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
// check for string type
{
v := any{key, info.key.id}
ti := runtime.type_info_base(type_info_of(v.id))
a := any{v.data, ti.id}
kv := any{key, info.key.id}
kti := runtime.type_info_base(type_info_of(kv.id))
ka := any{kv.data, kti.id}
name: string
#partial switch info in ti.variant {
#partial switch info in kti.variant {
case runtime.Type_Info_String:
switch s in a {
switch s in ka {
case string: name = s
case cstring: name = string(s)
}
@@ -328,13 +335,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
// check for string type
{
v := any{key, info.key.id}
ti := runtime.type_info_base(type_info_of(v.id))
a := any{v.data, ti.id}
kv := any{key, info.key.id}
kti := runtime.type_info_base(type_info_of(kv.id))
ka := any{kv.data, kti.id}
#partial switch info in ti.variant {
#partial switch info in kti.variant {
case runtime.Type_Info_String:
switch s in a {
switch s in ka {
case string: name = s
case cstring: name = string(s)
}
@@ -359,24 +366,93 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
opt_write_end(w, opt, '}') or_return
case runtime.Type_Info_Struct:
opt_write_start(w, opt, '{') or_return
for name, i in info.names {
opt_write_iteration(w, opt, i) or_return
if json_name := string(reflect.struct_tag_get(auto_cast info.tags[i], "json")); json_name != "" {
opt_write_key(w, opt, json_name) or_return
} else {
opt_write_key(w, opt, name) or_return
is_omitempty :: proc(v: any) -> bool {
v := v
if v == nil {
return true
}
id := info.types[i].id
data := rawptr(uintptr(v.data) + info.offsets[i])
marshal_to_writer(w, any{data, id}, opt) or_return
ti := runtime.type_info_core(type_info_of(v.id))
#partial switch info in ti.variant {
case runtime.Type_Info_String:
switch x in v {
case string:
return x == ""
case cstring:
return x == nil || x == ""
}
case runtime.Type_Info_Any:
return v.(any) == nil
case runtime.Type_Info_Type_Id:
return v.(typeid) == nil
case runtime.Type_Info_Pointer,
runtime.Type_Info_Multi_Pointer,
runtime.Type_Info_Procedure:
return (^rawptr)(v.data)^ == nil
case runtime.Type_Info_Dynamic_Array:
return (^runtime.Raw_Dynamic_Array)(v.data).len == 0
case runtime.Type_Info_Slice:
return (^runtime.Raw_Slice)(v.data).len == 0
case runtime.Type_Info_Union,
runtime.Type_Info_Bit_Set,
runtime.Type_Info_Soa_Pointer:
return reflect.is_nil(v)
case runtime.Type_Info_Map:
return (^runtime.Raw_Map)(v.data).len == 0
}
return false
}
marshal_struct_fields :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
ti := runtime.type_info_base(type_info_of(v.id))
info := ti.variant.(runtime.Type_Info_Struct)
for name, i in info.names {
omitempty := false
json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json"))
for flag in strings.split_iterator(&extra, ",") {
switch flag {
case "omitempty":
omitempty = true
}
}
id := info.types[i].id
data := rawptr(uintptr(v.data) + info.offsets[i])
the_value := any{data, id}
if is_omitempty(the_value) {
continue
}
opt_write_iteration(w, opt, i) or_return
if json_name != "" {
opt_write_key(w, opt, json_name) or_return
} else {
// Marshal the fields of 'using _: T' fields directly into the parent struct
if info.usings[i] && name == "_" {
marshal_struct_fields(w, the_value, opt) or_return
continue
} else {
opt_write_key(w, opt, name) or_return
}
}
marshal_to_writer(w, the_value, opt) or_return
}
return
}
opt_write_start(w, opt, '{') or_return
marshal_struct_fields(w, v, opt) or_return
opt_write_end(w, opt, '}') or_return
case runtime.Type_Info_Union:
if len(info.variants) == 0 || v.data == nil {
io.write_string(w, "null") or_return
return nil
}
tag_ptr := uintptr(v.data) + info.tag_offset
tag_any := any{rawptr(tag_ptr), info.tag_type.id}
@@ -401,7 +477,16 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
}
case runtime.Type_Info_Enum:
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
if !opt.use_enum_names || len(info.names) == 0 {
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
} else {
name, found := reflect.enum_name_from_value_any(v)
if found {
return marshal_to_writer(w, name, opt)
} else {
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
}
}
case runtime.Type_Info_Bit_Set:
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
+6 -6
View File
@@ -204,8 +204,8 @@ parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
}
@(private)
bytes_make :: proc(size, alignment: int, allocator: mem.Allocator) -> (bytes: []byte, err: Error) {
b, berr := mem.alloc_bytes(size, alignment, allocator)
bytes_make :: proc(size, alignment: int, allocator: mem.Allocator, loc := #caller_location) -> (bytes: []byte, err: Error) {
b, berr := mem.alloc_bytes(size, alignment, allocator, loc)
if berr != nil {
if berr == .Out_Of_Memory {
err = .Out_Of_Memory
@@ -217,9 +217,9 @@ bytes_make :: proc(size, alignment: int, allocator: mem.Allocator) -> (bytes: []
return
}
clone_string :: proc(s: string, allocator: mem.Allocator) -> (str: string, err: Error) {
clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_location) -> (str: string, err: Error) {
n := len(s)
b := bytes_make(n+1, 1, allocator) or_return
b := bytes_make(n+1, 1, allocator, loc) or_return
copy(b, s)
if len(b) > n {
b[n] = 0
@@ -290,7 +290,7 @@ parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
// IMPORTANT NOTE(bill): unquote_string assumes a mostly valid string
unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator) -> (value: string, err: Error) {
unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator, loc := #caller_location) -> (value: string, err: Error) {
get_u2_rune :: proc(s: string) -> rune {
if len(s) < 4 || s[0] != '\\' || s[1] != 'x' {
return -1
@@ -359,7 +359,7 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
i += w
}
if i == len(s) {
return clone_string(s, allocator)
return clone_string(s, allocator, loc)
}
b := bytes_make(len(s) + 2*utf8.UTF_MAX, 1, allocator) or_return
+68 -14
View File
@@ -6,6 +6,7 @@ import "core:reflect"
import "core:strconv"
import "core:strings"
import "base:runtime"
import "base:intrinsics"
Unmarshal_Data_Error :: enum {
Invalid_Data,
@@ -342,6 +343,16 @@ unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_loca
return prev
}
@(private)
json_name_from_tag_value :: proc(value: string) -> (json_name, extra: string) {
json_name = value
if comma_index := strings.index_byte(json_name, ','); comma_index >= 0 {
json_name = json_name[:comma_index]
extra = json_name[comma_index:]
}
return
}
@(private)
unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
@@ -368,16 +379,23 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
unmarshal_expect_token(p, .Colon)
fields := reflect.struct_fields_zipped(ti.id)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
field_used := make([]bool, len(fields), context.temp_allocator)
field_test :: #force_inline proc "contextless" (field_used: [^]byte, offset: uintptr) -> bool {
prev_set := field_used[offset/8] & byte(offset&7) != 0
field_used[offset/8] |= byte(offset&7)
return prev_set
}
field_used_bytes := (reflect.size_of_typeid(ti.id)+7)/8
field_used := intrinsics.alloca(field_used_bytes, 1)
intrinsics.mem_zero(field_used, field_used_bytes)
use_field_idx := -1
for field, field_idx in fields {
tag_value := string(reflect.struct_tag_get(field.tag, "json"))
if key == tag_value {
json_name, _ := json_name_from_tag_value(tag_value)
if key == json_name {
use_field_idx = field_idx
break
}
@@ -392,14 +410,45 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
}
}
if use_field_idx >= 0 {
if field_used[use_field_idx] {
check_children_using_fields :: proc(key: string, parent: typeid) -> (
offset: uintptr,
type: ^reflect.Type_Info,
found: bool,
) {
for field in reflect.struct_fields_zipped(parent) {
if field.is_using && field.name == "_" {
offset, type, found = check_children_using_fields(key, field.type.id)
if found {
offset += field.offset
return
}
}
if field.name == key {
offset = field.offset
type = field.type
found = true
return
}
}
return
}
offset: uintptr
type: ^reflect.Type_Info
field_found: bool = use_field_idx >= 0
if field_found {
offset = fields[use_field_idx].offset
type = fields[use_field_idx].type
} else {
offset, type, field_found = check_children_using_fields(key, ti.id)
}
if field_found {
if field_test(field_used, offset) {
return .Multiple_Use_Field
}
field_used[use_field_idx] = true
offset := fields[use_field_idx].offset
type := fields[use_field_idx].type
name := fields[use_field_idx].name
field_ptr := rawptr(uintptr(v.data) + offset)
field := any{field_ptr, type.id}
@@ -411,6 +460,12 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
continue struct_loop
} else {
// allows skipping unused struct fields
// NOTE(bill): prevent possible memory leak if a string is unquoted
allocator := p.allocator
defer p.allocator = allocator
p.allocator = mem.nil_allocator()
parse_value(p) or_return
if parse_comma(p) {
break struct_loop
@@ -439,9 +494,9 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
mem.zero_slice(elem_backing)
if err := unmarshal_value(p, map_backing_value); err != nil {
if uerr := unmarshal_value(p, map_backing_value); uerr != nil {
delete(key, p.allocator)
return err
return uerr
}
key_ptr := rawptr(&key)
@@ -492,7 +547,6 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
}
}
return nil
case:
return UNSUPPORTED_TYPE
}
+467 -226
View File
@@ -147,16 +147,30 @@ aprintln :: proc(args: ..any, sep := " ", allocator := context.allocator) -> str
// *Allocates Using Context's Allocator*
//
// Inputs:
// - fmt: A format string with placeholders for the provided arguments.
// - args: A variadic list of arguments to be formatted.
// - newline: Whether the string should end with a newline. (See `aprintfln`.)
//
// Returns: A formatted string. The returned string must be freed accordingly.
//
aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator, newline := false) -> string {
str: strings.Builder
strings.builder_init(&str, allocator)
sbprintf(&str, fmt, ..args, newline=newline)
return strings.to_string(str)
}
// Creates a formatted string using a format string and arguments, followed by a newline.
//
// *Allocates Using Context's Allocator*
//
// Inputs:
// - fmt: A format string with placeholders for the provided arguments.
// - args: A variadic list of arguments to be formatted.
//
// Returns: A formatted string. The returned string must be freed accordingly.
//
aprintf :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string {
str: strings.Builder
strings.builder_init(&str, allocator)
sbprintf(&str, fmt, ..args)
return strings.to_string(str)
aprintfln :: proc(fmt: string, args: ..any, allocator := context.allocator) -> string {
return aprintf(fmt, ..args, allocator=allocator, newline=true)
}
// Creates a formatted string
//
@@ -195,16 +209,30 @@ tprintln :: proc(args: ..any, sep := " ") -> string {
// *Allocates Using Context's Temporary Allocator*
//
// Inputs:
// - fmt: A format string with placeholders for the provided arguments.
// - args: A variadic list of arguments to be formatted.
// - newline: Whether the string should end with a newline. (See `tprintfln`.)
//
// Returns: A formatted string.
//
tprintf :: proc(fmt: string, args: ..any, newline := false) -> string {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
sbprintf(&str, fmt, ..args, newline=newline)
return strings.to_string(str)
}
// Creates a formatted string using a format string and arguments, followed by a newline.
//
// *Allocates Using Context's Temporary Allocator*
//
// Inputs:
// - fmt: A format string with placeholders for the provided arguments.
// - args: A variadic list of arguments to be formatted.
//
// Returns: A formatted string.
//
tprintf :: proc(fmt: string, args: ..any) -> string {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
sbprintf(&str, fmt, ..args)
return strings.to_string(str)
tprintfln :: proc(fmt: string, args: ..any) -> string {
return tprintf(fmt, ..args, newline=true)
}
// Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer.
//
@@ -238,12 +266,25 @@ bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
// - buf: The backing buffer
// - fmt: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
// - newline: Whether the string should end with a newline. (See `bprintfln`.)
//
// Returns: A formatted string
//
bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
bprintf :: proc(buf: []byte, fmt: string, args: ..any, newline := false) -> string {
sb := strings.builder_from_bytes(buf)
return sbprintf(&sb, fmt, ..args)
return sbprintf(&sb, fmt, ..args, newline=newline)
}
// Creates a formatted string using a supplied buffer as the backing array, followed by a newline. Writes into the buffer.
//
// Inputs:
// - buf: The backing buffer
// - fmt: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
//
// Returns: A formatted string
//
bprintfln :: proc(buf: []byte, fmt: string, args: ..any) -> string {
return bprintf(buf, fmt, ..args, newline=true)
}
// Runtime assertion with a formatted message
//
@@ -294,17 +335,31 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
// Inputs:
// - format: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
// - newline: Whether the string should end with a newline. (See `caprintfln`.)
//
// Returns: A formatted C string
//
caprintf :: proc(format: string, args: ..any) -> cstring {
caprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
str: strings.Builder
strings.builder_init(&str)
sbprintf(&str, format, ..args)
sbprintf(&str, format, ..args, newline=newline)
strings.write_byte(&str, 0)
s := strings.to_string(str)
return cstring(raw_data(s))
}
// Creates a formatted C string, followed by a newline.
//
// *Allocates Using Context's Allocator*
//
// Inputs:
// - format: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
//
// Returns: A formatted C string
//
caprintfln :: proc(format: string, args: ..any) -> cstring {
return caprintf(format, ..args, newline=true)
}
// Creates a formatted C string
//
// *Allocates Using Context's Temporary Allocator*
@@ -312,16 +367,30 @@ caprintf :: proc(format: string, args: ..any) -> cstring {
// Inputs:
// - format: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
// - newline: Whether the string should end with a newline. (See `ctprintfln`.)
//
// Returns: A formatted C string
//
ctprintf :: proc(format: string, args: ..any, newline := false) -> cstring {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
sbprintf(&str, format, ..args, newline=newline)
strings.write_byte(&str, 0)
s := strings.to_string(str)
return cstring(raw_data(s))
}
// Creates a formatted C string, followed by a newline.
//
// *Allocates Using Context's Temporary Allocator*
//
// Inputs:
// - format: A format string with placeholders for the provided arguments
// - args: A variadic list of arguments to be formatted
//
// Returns: A formatted C string
//
ctprintf :: proc(format: string, args: ..any) -> cstring {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
sbprintf(&str, format, ..args)
strings.write_byte(&str, 0)
s := strings.to_string(str)
return cstring(raw_data(s))
ctprintfln :: proc(format: string, args: ..any) -> cstring {
return ctprintf(format, ..args, newline=true)
}
// Formats using the default print settings and writes to the given strings.Builder
//
@@ -355,13 +424,25 @@ sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
// - buf: A pointer to a strings.Builder buffer
// - fmt: The format string
// - args: A variadic list of arguments to be formatted
// - newline: Whether a trailing newline should be written. (See `sbprintfln`.)
//
// Returns: The resulting formatted string
//
sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string {
wprintf(strings.to_writer(buf), fmt, ..args, flush=true)
sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any, newline := false) -> string {
wprintf(strings.to_writer(buf), fmt, ..args, flush=true, newline=newline)
return strings.to_string(buf^)
}
// Formats and writes to a strings.Builder buffer according to the specified format string, followed by a newline.
//
// Inputs:
// - buf: A pointer to a strings.Builder to store the formatted string
// - args: A variadic list of arguments to be formatted
//
// Returns: A formatted string
//
sbprintfln :: proc(buf: ^strings.Builder, format: string, args: ..any) -> string {
return sbprintf(buf, format, ..args, newline=true)
}
// Formats and writes to an io.Writer using the default print settings
//
// Inputs:
@@ -435,10 +516,11 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ", flush := true) -> int {
// - w: An io.Writer to write to
// - fmt: The format string
// - args: A variadic list of arguments to be formatted
// - newline: Whether a trailing newline should be written. (See `wprintfln`.)
//
// Returns: The number of bytes written
//
wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true) -> int {
wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline := false) -> int {
fi: Info
arg_index: int = 0
end := len(fmt)
@@ -708,12 +790,27 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true) -> int {
}
io.write_string(fi.writer, ")", &fi.n)
}
if newline {
io.write_byte(w, '\n', &fi.n)
}
if flush {
io.flush(w)
}
return fi.n
}
// Formats and writes to an io.Writer according to the specified format string, followed by a newline.
//
// Inputs:
// - w: The io.Writer to write to.
// - args: A variadic list of arguments to be formatted.
//
// Returns: The number of bytes written.
//
wprintfln :: proc(w: io.Writer, format: string, args: ..any, flush := true) -> int {
return wprintf(w, format, ..args, flush=flush, newline=true)
}
// Writes a ^runtime.Type_Info value to an io.Writer
//
// Inputs:
@@ -875,7 +972,7 @@ fmt_bad_verb :: proc(fi: ^Info, verb: rune) {
//
fmt_bool :: proc(fi: ^Info, b: bool, verb: rune) {
switch verb {
case 't', 'v':
case 't', 'v', 'w':
fmt_string(fi, b ? "true" : "false", 's')
case:
fmt_bad_verb(fi, verb)
@@ -1112,7 +1209,7 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
switch verb {
case 'c', 'r', 'v':
io.write_rune(fi.writer, r, &fi.n)
case 'q':
case 'q', 'w':
fi.n += io.write_quoted_rune(fi.writer, r)
case:
fmt_int(fi, u64(r), false, 32, verb)
@@ -1129,7 +1226,8 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
//
fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
switch verb {
case 'v': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
case 'v', 'w':
_fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
case 'b': _fmt_int(fi, u, 2, is_signed, bit_size, __DIGITS_LOWER)
case 'o': _fmt_int(fi, u, 8, is_signed, bit_size, __DIGITS_LOWER)
case 'i', 'd': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
@@ -1164,7 +1262,8 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
//
fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: rune) {
switch verb {
case 'v': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
case 'v', 'w':
_fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
case 'b': _fmt_int_128(fi, u, 2, is_signed, bit_size, __DIGITS_LOWER)
case 'o': _fmt_int_128(fi, u, 8, is_signed, bit_size, __DIGITS_LOWER)
case 'i', 'd': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
@@ -1254,7 +1353,7 @@ _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: b
//
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
switch verb {
case 'g', 'G', 'v':
case 'g', 'G', 'v', 'w':
_fmt_float_as(fi, v, bit_size, verb, 'g', -1)
case 'f', 'F':
_fmt_float_as(fi, v, bit_size, verb, 'f', 3)
@@ -1327,7 +1426,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
io.write_string(fi.writer, s, &fi.n)
}
case 'q': // quoted string
case 'q', 'w': // quoted string
io.write_quoted_string(fi.writer, s, '"', &fi.n)
case 'x', 'X':
@@ -1370,7 +1469,7 @@ fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) {
fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
u := u64(uintptr(p))
switch verb {
case 'p', 'v':
case 'p', 'v', 'w':
if !fi.hash && verb == 'v' {
io.write_string(fi.writer, "0x", &fi.n)
}
@@ -1408,34 +1507,9 @@ fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) {
//
// Returns: The string representation of the enum value and a boolean indicating success.
//
@(require_results)
enum_value_to_string :: proc(val: any) -> (string, bool) {
v := val
v.id = runtime.typeid_base(v.id)
type_info := type_info_of(v.id)
#partial switch e in type_info.variant {
case: return "", false
case runtime.Type_Info_Enum:
Enum_Value :: runtime.Type_Info_Enum_Value
ev_, ok := reflect.as_i64(val)
ev := Enum_Value(ev_)
if ok {
if len(e.values) == 0 {
return "", true
} else {
for val, idx in e.values {
if val == ev {
return e.names[idx], true
}
}
}
return "", false
}
}
return "", false
return reflect.enum_name_from_value_any(val)
}
// Returns the enum value of a string representation.
//
@@ -1463,7 +1537,7 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) {
// Inputs:
// - fi: Pointer to the Info struct containing format settings.
// - v: The enum value to format.
// - verb: The format specifier character (e.g. 'i','d','f','s','v','q').
// - verb: The format specifier character (e.g. 'i','d','f','s','v','q','w').
//
fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
if v.id == nil || v.data == nil {
@@ -1487,6 +1561,15 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, 'i')
io.write_string(fi.writer, ")", &fi.n)
}
case 'w':
if str, ok := enum_value_to_string(v); ok {
io.write_byte(fi.writer, '.', &fi.n)
io.write_string(fi.writer, str, &fi.n)
} else {
io.write_string(fi.writer, "%!(BAD ENUM VALUE=", &fi.n)
fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, 'i')
io.write_string(fi.writer, ")", &fi.n)
}
}
}
}
@@ -1675,8 +1758,8 @@ fmt_write_indent :: proc(fi: ^Info) {
// - verb: The formatting verb to be used for the array elements.
//
fmt_write_array :: proc(fi: ^Info, array_data: rawptr, count: int, elem_size: int, elem_id: typeid, verb: rune) {
io.write_byte(fi.writer, '[', &fi.n)
defer io.write_byte(fi.writer, ']', &fi.n)
io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n)
defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
if count <= 0 {
return
@@ -1749,6 +1832,13 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb:
r, w := utf8.decode_rune_in_string(value)
value = value[w:]
if value == "" || value[0] == ',' {
if verb^ == 'w' {
// TODO(bill): is this a good idea overriding that field tags if 'w' is used?
switch r {
case 's': r = 'q'
case: r = 'w'
}
}
verb^ = r
if len(value) > 0 && value[0] == ',' {
field_name := value[1:]
@@ -1760,7 +1850,7 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb:
switch r {
case 's', 'q':
handle_optional_len(data, info, field_name, optional_len)
case 'v':
case 'v', 'w':
#partial switch reflect.type_kind(info.types[idx].id) {
case .String, .Multi_Pointer, .Array, .Slice, .Dynamic_Array:
handle_optional_len(data, info, field_name, optional_len)
@@ -1782,7 +1872,7 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb:
// - type_name: The name of the type being formatted
//
fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_Struct, type_name: string) {
if the_verb != 'v' {
if the_verb != 'v' && the_verb != 'w' {
fmt_bad_verb(fi, the_verb)
return
}
@@ -1799,7 +1889,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
is_soa := info.soa_kind != .None
io.write_string(fi.writer, type_name, &fi.n)
io.write_byte(fi.writer, '[' if is_soa else '{', &fi.n)
io.write_byte(fi.writer, '[' if is_soa && the_verb == 'v' else '{', &fi.n)
fi.record_level += 1
defer fi.record_level -= 1
@@ -1817,7 +1907,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
if hash {
for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
}
io.write_byte(fi.writer, ']' if is_soa else '}', &fi.n)
io.write_byte(fi.writer, ']' if is_soa && the_verb == 'v' else '}', &fi.n)
}
if is_soa {
@@ -1900,7 +1990,7 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
for name, i in info.names {
optional_len: int = -1
use_nul_termination: bool = false
verb := 'v'
verb := the_verb if the_verb == 'w' else 'v'
if handle_tag(v.data, info, i, &verb, &optional_len, &use_nul_termination) {
continue
}
@@ -1979,7 +2069,7 @@ fmt_array_nul_terminated :: proc(fi: ^Info, data: rawptr, max_n: int, elem_size:
// - n: The number of elements in the array.
// - elem_size: The size of each element in the array.
// - elem: Pointer to the type information of the array element.
// - verb: The formatting verb (e.g. 's','q','p').
// - verb: The formatting verb (e.g. 's','q','p','w').
//
fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) {
if data == nil && n > 0 {
@@ -2058,149 +2148,170 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named)
}
// Built-in Custom Formatters for core library types
switch a in v {
case runtime.Source_Code_Location:
io.write_string(fi.writer, a.file_path, &fi.n)
if verb != 'w' {
switch a in v {
case runtime.Source_Code_Location:
io.write_string(fi.writer, a.file_path, &fi.n)
when ODIN_ERROR_POS_STYLE == .Default {
io.write_byte(fi.writer, '(', &fi.n)
io.write_int(fi.writer, int(a.line), 10, &fi.n)
io.write_byte(fi.writer, ':', &fi.n)
io.write_int(fi.writer, int(a.column), 10, &fi.n)
io.write_byte(fi.writer, ')', &fi.n)
} else when ODIN_ERROR_POS_STYLE == .Unix {
io.write_byte(fi.writer, ':', &fi.n)
io.write_int(fi.writer, int(a.line), 10, &fi.n)
io.write_byte(fi.writer, ':', &fi.n)
io.write_int(fi.writer, int(a.column), 10, &fi.n)
io.write_byte(fi.writer, ':', &fi.n)
} else {
#panic("Unhandled ODIN_ERROR_POS_STYLE")
}
return
case time.Duration:
ffrac :: proc(buf: []byte, v: u64, prec: int) -> (nw: int, nv: u64) {
v := v
w := len(buf)
print := false
for _ in 0..<prec {
digit := v % 10
print = print || digit != 0
if print {
w -= 1
buf[w] = byte(digit) + '0'
}
v /= 10
}
if print {
w -= 1
buf[w] = '.'
}
return w, v
}
fint :: proc(buf: []byte, v: u64) -> int {
v := v
w := len(buf)
if v == 0 {
w -= 1
buf[w] = '0'
when ODIN_ERROR_POS_STYLE == .Default {
io.write_byte(fi.writer, '(', &fi.n)
io.write_int(fi.writer, int(a.line), 10, &fi.n)
io.write_byte(fi.writer, ':', &fi.n)
io.write_int(fi.writer, int(a.column), 10, &fi.n)
io.write_byte(fi.writer, ')', &fi.n)
} else when ODIN_ERROR_POS_STYLE == .Unix {
io.write_byte(fi.writer, ':', &fi.n)
io.write_int(fi.writer, int(a.line), 10, &fi.n)
io.write_byte(fi.writer, ':', &fi.n)
io.write_int(fi.writer, int(a.column), 10, &fi.n)
io.write_byte(fi.writer, ':', &fi.n)
} else {
for v > 0 {
w -= 1
buf[w] = byte(v%10) + '0'
#panic("Unhandled ODIN_ERROR_POS_STYLE")
}
return
case time.Duration:
ffrac :: proc(buf: []byte, v: u64, prec: int) -> (nw: int, nv: u64) {
v := v
w := len(buf)
print := false
for _ in 0..<prec {
digit := v % 10
print = print || digit != 0
if print {
w -= 1
buf[w] = byte(digit) + '0'
}
v /= 10
}
if print {
w -= 1
buf[w] = '.'
}
return w, v
}
return w
}
buf: [32]byte
w := len(buf)
u := u64(a)
neg := a < 0
if neg {
u = -u
}
if u < u64(time.Second) {
prec: int
w -= 1
buf[w] = 's'
w -= 1
switch {
case u == 0:
io.write_string(fi.writer, "0s", &fi.n)
return
case u < u64(time.Microsecond):
prec = 0
buf[w] = 'n'
case u < u64(time.Millisecond):
prec = 3
// U+00B5 'µ' micro sign == 0xC2 0xB5
w -= 1 // Need room for two bytes
copy(buf[w:], "µ")
case:
prec = 6
buf[w] = 'm'
fint :: proc(buf: []byte, v: u64) -> int {
v := v
w := len(buf)
if v == 0 {
w -= 1
buf[w] = '0'
} else {
for v > 0 {
w -= 1
buf[w] = byte(v%10) + '0'
v /= 10
}
}
return w
}
w, u = ffrac(buf[:w], u, prec)
w = fint(buf[:w], u)
} else {
w -= 1
buf[w] = 's'
w, u = ffrac(buf[:w], u, 9)
w = fint(buf[:w], u%60)
u /= 60
if u > 0 {
buf: [32]byte
w := len(buf)
u := u64(a)
neg := a < 0
if neg {
u = -u
}
if u < u64(time.Second) {
prec: int
w -= 1
buf[w] = 'm'
buf[w] = 's'
w -= 1
switch {
case u == 0:
io.write_string(fi.writer, "0s", &fi.n)
return
case u < u64(time.Microsecond):
prec = 0
buf[w] = 'n'
case u < u64(time.Millisecond):
prec = 3
// U+00B5 'µ' micro sign == 0xC2 0xB5
w -= 1 // Need room for two bytes
copy(buf[w:], "µ")
case:
prec = 6
buf[w] = 'm'
}
w, u = ffrac(buf[:w], u, prec)
w = fint(buf[:w], u)
} else {
w -= 1
buf[w] = 's'
w, u = ffrac(buf[:w], u, 9)
w = fint(buf[:w], u%60)
u /= 60
if u > 0 {
w -= 1
buf[w] = 'h'
w = fint(buf[:w], u)
buf[w] = 'm'
w = fint(buf[:w], u%60)
u /= 60
if u > 0 {
w -= 1
buf[w] = 'h'
w = fint(buf[:w], u)
}
}
}
if neg {
w -= 1
buf[w] = '-'
}
io.write_string(fi.writer, string(buf[w:]), &fi.n)
return
case time.Time:
t := a
y, mon, d := time.date(t)
h, min, s := time.clock(t)
ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9
write_padded_number(fi, i64(y), 4)
io.write_byte(fi.writer, '-', &fi.n)
write_padded_number(fi, i64(mon), 2)
io.write_byte(fi.writer, '-', &fi.n)
write_padded_number(fi, i64(d), 2)
io.write_byte(fi.writer, ' ', &fi.n)
write_padded_number(fi, i64(h), 2)
io.write_byte(fi.writer, ':', &fi.n)
write_padded_number(fi, i64(min), 2)
io.write_byte(fi.writer, ':', &fi.n)
write_padded_number(fi, i64(s), 2)
io.write_byte(fi.writer, '.', &fi.n)
write_padded_number(fi, (ns), 9)
io.write_string(fi.writer, " +0000 UTC", &fi.n)
return
}
if neg {
w -= 1
buf[w] = '-'
}
io.write_string(fi.writer, string(buf[w:]), &fi.n)
return
case time.Time:
t := a
y, mon, d := time.date(t)
h, min, s := time.clock(t)
ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9
write_padded_number(fi, i64(y), 4)
io.write_byte(fi.writer, '-', &fi.n)
write_padded_number(fi, i64(mon), 2)
io.write_byte(fi.writer, '-', &fi.n)
write_padded_number(fi, i64(d), 2)
io.write_byte(fi.writer, ' ', &fi.n)
write_padded_number(fi, i64(h), 2)
io.write_byte(fi.writer, ':', &fi.n)
write_padded_number(fi, i64(min), 2)
io.write_byte(fi.writer, ':', &fi.n)
write_padded_number(fi, i64(s), 2)
io.write_byte(fi.writer, '.', &fi.n)
write_padded_number(fi, (ns), 9)
io.write_string(fi.writer, " +0000 UTC", &fi.n)
return
}
#partial switch b in info.base.variant {
case runtime.Type_Info_Struct:
fmt_struct(fi, v, verb, b, info.name)
case runtime.Type_Info_Bit_Field:
fmt_bit_field(fi, v, verb, b, info.name)
case runtime.Type_Info_Bit_Set:
fmt_bit_set(fi, v, verb = verb)
case:
if verb == 'w' {
#partial switch _ in info.base.variant {
case runtime.Type_Info_Array,
runtime.Type_Info_Enumerated_Array,
runtime.Type_Info_Dynamic_Array,
runtime.Type_Info_Slice,
runtime.Type_Info_Struct,
runtime.Type_Info_Union,
runtime.Type_Info_Enum,
runtime.Type_Info_Map,
runtime.Type_Info_Bit_Set,
runtime.Type_Info_Simd_Vector,
runtime.Type_Info_Matrix,
runtime.Type_Info_Bit_Field:
io.write_string(fi.writer, info.name, &fi.n)
}
}
fmt_value(fi, any{v.data, info.base.id}, verb)
}
}
@@ -2267,8 +2378,13 @@ fmt_union :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Union,
// - info: A runtime.Type_Info_Matrix struct containing matrix type information.
//
fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix) {
io.write_string(fi.writer, "matrix[", &fi.n)
defer io.write_byte(fi.writer, ']', &fi.n)
if verb == 'w' {
io.write_byte(fi.writer, '{', &fi.n)
} else {
io.write_string(fi.writer, "matrix", &fi.n)
io.write_byte(fi.writer, '[', &fi.n)
}
defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
fi.indent += 1
@@ -2280,7 +2396,11 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix
for col in 0..<info.column_count {
if col > 0 { io.write_string(fi.writer, ", ", &fi.n) }
offset := (row + col*info.elem_stride)*info.elem_size
offset: int
switch info.layout {
case .Column_Major: offset = (row + col*info.elem_stride)*info.elem_size
case .Row_Major: offset = (col + row*info.elem_stride)*info.elem_size
}
data := uintptr(v.data) + uintptr(offset)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
@@ -2294,7 +2414,11 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix
for col in 0..<info.column_count {
if col > 0 { io.write_string(fi.writer, ", ", &fi.n) }
offset := (row + col*info.elem_stride)*info.elem_size
offset: int
switch info.layout {
case .Column_Major: offset = (row + col*info.elem_stride)*info.elem_size
case .Row_Major: offset = (col + row*info.elem_stride)*info.elem_size
}
data := uintptr(v.data) + uintptr(offset)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
@@ -2308,6 +2432,96 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix
fmt_write_indent(fi)
}
}
fmt_bit_field :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Bit_Field, type_name: string) {
read_bits :: proc(ptr: [^]byte, offset, size: uintptr) -> (res: u64) {
for i in 0..<size {
j := i+offset
B := ptr[j/8]
k := j&7
if B & (u8(1)<<k) != 0 {
res |= u64(1)<<u64(i)
}
}
return
}
handle_bit_field_tag :: proc(data: rawptr, info: reflect.Type_Info_Bit_Field, idx: int, verb: ^rune) -> (do_continue: bool) {
tag := info.tags[idx]
if vt, ok := reflect.struct_tag_lookup(reflect.Struct_Tag(tag), "fmt"); ok {
value := strings.trim_space(string(vt))
switch value {
case "": return false
case "-": return true
}
r, w := utf8.decode_rune_in_string(value)
value = value[w:]
if value == "" || value[0] == ',' {
verb^ = r
}
}
return false
}
io.write_string(fi.writer, type_name if len(type_name) != 0 || verb == 'w' else "bit_field", &fi.n)
io.write_byte(fi.writer, '{', &fi.n)
hash := fi.hash; defer fi.hash = hash
indent := fi.indent; defer fi.indent -= 1
do_trailing_comma := hash
fi.indent += 1
if hash {
io.write_byte(fi.writer, '\n', &fi.n)
}
defer {
if hash {
for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
}
io.write_byte(fi.writer, '}', &fi.n)
}
field_count := -1
for name, i in info.names {
field_verb := verb
if handle_bit_field_tag(v.data, info, i, &field_verb) {
continue
}
field_count += 1
if !do_trailing_comma && field_count > 0 {
io.write_string(fi.writer, ", ")
}
if hash {
fmt_write_indent(fi)
}
io.write_string(fi.writer, name, &fi.n)
io.write_string(fi.writer, " = ", &fi.n)
bit_offset := info.bit_offsets[i]
bit_size := info.bit_sizes[i]
value := read_bits(([^]byte)(v.data), bit_offset, bit_size)
type := info.types[i]
if !reflect.is_unsigned(runtime.type_info_core(type)) {
// Sign Extension
m := u64(1<<(bit_size-1))
value = (value ~ m) - m
}
fmt_value(fi, any{&value, type.id}, field_verb)
if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) }
}
}
// Formats a value based on its type and formatting verb
//
// Inputs:
@@ -2467,11 +2681,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
defer fi.record_level -= 1
if fi.hash {
io.write_string(fi.writer, "[\n", &fi.n)
io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n)
io.write_byte(fi.writer, '\n', &fi.n)
defer {
io.write_byte(fi.writer, '\n', &fi.n)
fmt_write_indent(fi)
io.write_byte(fi.writer, ']', &fi.n)
io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
}
indent := fi.indent
fi.indent += 1
@@ -2495,8 +2710,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
io.write_string(fi.writer, ",\n", &fi.n)
}
} else {
io.write_byte(fi.writer, '[', &fi.n)
defer io.write_byte(fi.writer, ']', &fi.n)
io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n)
defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
for i in 0..<info.count {
if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
@@ -2561,38 +2776,61 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Map:
if verb != 'v' {
switch verb {
case:
fmt_bad_verb(fi, verb)
return
}
io.write_string(fi.writer, "map[", &fi.n)
defer io.write_byte(fi.writer, ']', &fi.n)
fi.record_level += 1
defer fi.record_level -= 1
m := (^mem.Raw_Map)(v.data)
if m != nil {
if info.map_info == nil {
return
case 'v', 'w':
if verb == 'v' {
io.write_string(fi.writer, "map", &fi.n)
}
map_cap := uintptr(runtime.map_cap(m^))
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
j := 0
for bucket_index in 0..<map_cap {
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
io.write_byte(fi.writer, '[' if verb != 'w' else '{', &fi.n)
defer io.write_byte(fi.writer, ']' if verb != 'w' else '}', &fi.n)
if j > 0 {
io.write_string(fi.writer, ", ", &fi.n)
hash := fi.hash; defer fi.hash = hash
indent := fi.indent; defer fi.indent -= 1
do_trailing_comma := hash
fi.indent += 1
if hash {
io.write_byte(fi.writer, '\n', &fi.n)
}
defer {
if hash {
for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
}
j += 1
}
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
m := (^mem.Raw_Map)(v.data)
if m != nil {
if info.map_info == nil {
return
}
map_cap := uintptr(runtime.map_cap(m^))
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
j := 0
for bucket_index in 0..<map_cap {
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
io.write_string(fi.writer, "=", &fi.n)
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
if !do_trailing_comma && j > 0 { io.write_string(fi.writer, ", ") }
if hash {
fmt_write_indent(fi)
}
j += 1
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, verb)
if hash {
io.write_string(fi.writer, " = ", &fi.n)
} else {
io.write_string(fi.writer, "=", &fi.n)
}
fmt_arg(fi, any{rawptr(value), info.value.id}, verb)
if do_trailing_comma { io.write_string(fi.writer, ",\n", &fi.n) }
}
}
}
@@ -2636,6 +2874,9 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Matrix:
fmt_matrix(fi, v, verb, info)
case runtime.Type_Info_Bit_Field:
fmt_bit_field(fi, v, verb, info, "")
}
}
// Formats a complex number based on the given formatting verb
@@ -2644,11 +2885,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
// - fi: A pointer to an Info struct containing formatting information.
// - c: The complex128 value to be formatted.
// - bits: The number of bits in the complex number (32 or 64).
// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H').
// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H', 'w').
//
fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
switch verb {
case 'f', 'F', 'v', 'h', 'H':
case 'f', 'F', 'v', 'h', 'H', 'w':
r, i := real(c), imag(c)
fmt_float(fi, r, bits/2, verb)
if !fi.plus && i >= 0 {
@@ -2668,11 +2909,11 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
// - fi: A pointer to an Info struct containing formatting information.
// - q: The quaternion256 value to be formatted.
// - bits: The number of bits in the quaternion number (64, 128, or 256).
// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H').
// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H', 'w').
//
fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) {
switch verb {
case 'f', 'F', 'v', 'h', 'H':
case 'f', 'F', 'v', 'h', 'H', 'w':
r, i, j, k := real(q), imag(q), jmag(q), kmag(q)
fmt_float(fi, r, bits/4, verb)
+16 -8
View File
@@ -30,7 +30,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ", flush := true) -> int {
return wprintln(w, ..args, sep=sep, flush=flush)
}
// fprintf formats according to the specified format string and writes to fd
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true, newline := false) -> int {
buf: [1024]byte
b: bufio.Writer
defer bufio.writer_flush(&b)
@@ -38,7 +38,11 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
w := bufio.writer_to_writer(&b)
return wprintf(w, fmt, ..args, flush=flush)
return wprintf(w, fmt, ..args, flush=flush, newline=newline)
}
// fprintfln formats according to the specified format string and writes to fd, followed by a newline.
fprintfln :: proc(fd: os.Handle, fmt: string, args: ..any, flush := true) -> int {
return fprintf(fd, fmt, ..args, flush=flush, newline=true)
}
fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info, flush := true) -> (n: int, err: io.Error) {
buf: [1024]byte
@@ -62,15 +66,19 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid, flush := true) -> (n: int, err:
}
// print formats using the default print settings and writes to os.stdout
print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) }
print :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stdout, ..args, sep=sep, flush=flush) }
// println formats using the default print settings and writes to os.stdout
println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) }
println :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stdout, ..args, sep=sep, flush=flush) }
// printf formats according to the specified format string and writes to os.stdout
printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) }
printf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush) }
// printfln formats according to the specified format string and writes to os.stdout, followed by a newline.
printfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stdout, fmt, ..args, flush=flush, newline=true) }
// eprint formats using the default print settings and writes to os.stderr
eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) }
eprint :: proc(args: ..any, sep := " ", flush := true) -> int { return fprint(os.stderr, ..args, sep=sep, flush=flush) }
// eprintln formats using the default print settings and writes to os.stderr
eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) }
eprintln :: proc(args: ..any, sep := " ", flush := true) -> int { return fprintln(os.stderr, ..args, sep=sep, flush=flush) }
// eprintf formats according to the specified format string and writes to os.stderr
eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) }
eprintf :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush) }
// eprintfln formats according to the specified format string and writes to os.stderr, followed by a newline.
eprintfln :: proc(fmt: string, args: ..any, flush := true) -> int { return fprintf(os.stderr, fmt, ..args, flush=flush, newline=true) }
+4 -4
View File
@@ -1,7 +1,7 @@
package hash
@(optimization_mode="speed")
crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
crc64_ecma_182 :: proc "contextless" (data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
result = seed
#no_bounds_check for b in data {
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
@@ -15,7 +15,7 @@ crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds
Based on Mark Adler's v1.4 implementation in C under the ZLIB license.
*/
@(optimization_mode="speed")
crc64_xz :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
crc64_xz :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
data := data
result := ~u64le(seed)
@@ -53,7 +53,7 @@ crc64_xz :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
Generator polynomial: x^64 + x^4 + x^3 + x + 1
*/
@(optimization_mode="speed")
crc64_iso_3306 :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
crc64_iso_3306 :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
result := seed
@@ -70,7 +70,7 @@ crc64_iso_3306 :: proc(data: []byte, seed := u64(0)) -> u64 #no_bounds_check {
return result
}
crc64_iso_3306_inverse :: proc(data: []byte, seed := u64(0)) -> u64 {
crc64_iso_3306_inverse :: proc "contextless" (data: []byte, seed := u64(0)) -> u64 {
result := #force_inline crc64_iso_3306(data, ~seed)
return ~result
}
+2 -2
View File
@@ -3,7 +3,7 @@ package hash
import "base:intrinsics"
@(optimization_mode="speed")
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
crc := ~seed
buffer := raw_data(data)
length := len(data)
@@ -323,7 +323,7 @@ crc32_table := [8][256]u32{
/*
@(optimization_mode="speed")
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 {
crc32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
result := ~u32(seed);
#no_bounds_check for b in data {
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
+12 -12
View File
@@ -4,7 +4,7 @@ import "core:mem"
import "base:intrinsics"
@(optimization_mode="speed")
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
adler32 :: proc "contextless" (data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
ADLER_CONST :: 65521
@@ -47,7 +47,7 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
}
@(optimization_mode="speed")
djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
djb2 :: proc "contextless" (data: []byte, seed := u32(5381)) -> u32 {
hash: u32 = seed
for b in data {
hash = (hash << 5) + hash + u32(b) // hash * 33 + u32(b)
@@ -55,7 +55,7 @@ djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
return hash
}
djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check {
djbx33a :: proc "contextless" (data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check {
state := [4]u32{seed, seed, seed, seed}
s: u32 = 0
@@ -74,7 +74,7 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
// If you have a choice, prefer fnv32a
@(optimization_mode="speed")
fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
fnv32_no_a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed
for b in data {
h = (h * 0x01000193) ~ u32(b)
@@ -87,7 +87,7 @@ fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessar
// If you have a choice, prefer fnv64a
@(optimization_mode="speed")
fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
fnv64_no_a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed
for b in data {
h = (h * 0x100000001b3) ~ u64(b)
@@ -95,7 +95,7 @@ fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
return h
}
@(optimization_mode="speed")
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
fnv32a :: proc "contextless" (data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed
for b in data {
h = (h ~ u32(b)) * 0x01000193
@@ -104,7 +104,7 @@ fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
}
@(optimization_mode="speed")
fnv64a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
fnv64a :: proc "contextless" (data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed
for b in data {
h = (h ~ u64(b)) * 0x100000001b3
@@ -113,7 +113,7 @@ fnv64a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
}
@(optimization_mode="speed")
jenkins :: proc(data: []byte, seed := u32(0)) -> u32 {
jenkins :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
hash: u32 = seed
for b in data {
hash += u32(b)
@@ -127,7 +127,7 @@ jenkins :: proc(data: []byte, seed := u32(0)) -> u32 {
}
@(optimization_mode="speed")
murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
murmur32 :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
c1_32: u32 : 0xcc9e2d51
c2_32: u32 : 0x1b873593
@@ -178,7 +178,7 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
@(optimization_mode="speed")
murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
murmur64a :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
m :: 0xc6a4a7935bd1e995
r :: 47
@@ -219,7 +219,7 @@ murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
@(optimization_mode="speed")
murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
murmur64b :: proc "contextless" (data: []byte, seed := u64(0x9747b28c)) -> u64 {
m :: 0x5bd1e995
r :: 24
@@ -287,7 +287,7 @@ murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
}
@(optimization_mode="speed")
sdbm :: proc(data: []byte, seed := u32(0)) -> u32 {
sdbm :: proc "contextless" (data: []byte, seed := u32(0)) -> u32 {
hash: u32 = seed
for b in data {
hash = u32(b) + (hash<<6) + (hash<<16) - hash
+4 -4
View File
@@ -1,6 +1,6 @@
package hash
ginger_hash8 :: proc(x: u8) -> u8 {
ginger_hash8 :: proc "contextless" (x: u8) -> u8 {
h := x * 251
h += ~(x << 3)
h ~= (x >> 1)
@@ -11,7 +11,7 @@ ginger_hash8 :: proc(x: u8) -> u8 {
}
ginger_hash16 :: proc(x: u16) -> u16 {
ginger_hash16 :: proc "contextless" (x: u16) -> u16 {
z := (x << 8) | (x >> 8)
h := z
h += ~(z << 5)
@@ -24,14 +24,14 @@ ginger_hash16 :: proc(x: u16) -> u16 {
}
ginger8 :: proc(data: []byte) -> u8 {
ginger8 :: proc "contextless" (data: []byte) -> u8 {
h := ginger_hash8(0)
for b in data {
h ~= ginger_hash8(b)
}
return h
}
ginger16 :: proc(data: []byte) -> u16 {
ginger16 :: proc "contextless" (data: []byte) -> u16 {
h := ginger_hash16(0)
for b in data {
h ~= ginger_hash16(u16(b))
+1 -1
View File
@@ -129,7 +129,7 @@ XXH3_create_state :: proc(allocator := context.allocator) -> (res: ^XXH3_state,
}
XXH3_destroy_state :: proc(state: ^XXH3_state, allocator := context.allocator) -> (err: Error) {
free(state)
free(state, allocator)
return .None
}
+9 -9
View File
@@ -19,15 +19,15 @@ xxh_u32 :: u32
XXH32_DEFAULT_SEED :: XXH32_hash(0)
XXH32_state :: struct {
total_len_32: XXH32_hash, /*!< Total length hashed, modulo 2^32 */
large_len: XXH32_hash, /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */
v1: XXH32_hash, /*!< First accumulator lane */
v2: XXH32_hash, /*!< Second accumulator lane */
v3: XXH32_hash, /*!< Third accumulator lane */
v4: XXH32_hash, /*!< Fourth accumulator lane */
mem32: [4]XXH32_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */
memsize: XXH32_hash, /*!< Amount of data in @ref mem32 */
reserved: XXH32_hash, /*!< Reserved field. Do not read or write to it, it may be removed. */
total_len_32: XXH32_hash, /*!< Total length hashed, modulo 2^32 */
large_len: XXH32_hash, /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */
v1: XXH32_hash, /*!< First accumulator lane */
v2: XXH32_hash, /*!< Second accumulator lane */
v3: XXH32_hash, /*!< Third accumulator lane */
v4: XXH32_hash, /*!< Fourth accumulator lane */
mem32: [4]XXH32_hash, /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */
memsize: XXH32_hash, /*!< Amount of data in @ref mem32 */
reserved: XXH32_hash, /*!< Reserved field. Do not read or write to it, it may be removed. */
}
XXH32_canonical :: struct {
+18 -18
View File
@@ -651,7 +651,7 @@ alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator :=
// We have keyed alpha.
o: GA_Pixel
for p in inp {
if p == key.r {
if p.r == key.r {
o = GA_Pixel{0, key.g}
} else {
o = GA_Pixel{p.r, 255}
@@ -710,7 +710,7 @@ alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator :=
// We have keyed alpha.
o: GA_Pixel_16
for p in inp {
if p == key.r {
if p.r == key.r {
o = GA_Pixel_16{0, key.g}
} else {
o = GA_Pixel_16{p.r, 65535}
@@ -842,11 +842,11 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
bg := G_Pixel{}
if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
// Background is RGB 16-bit, take just the red channel's topmost byte.
bg = u8(temp_bg.r >> 8)
bg.r = u8(temp_bg.r >> 8)
}
for p in inp {
out[0] = bg if p == key else p
out[0] = bg if p.r == key else p
out = out[1:]
}
@@ -865,8 +865,8 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
for p in inp {
a := f32(p.g) / 255.0
c := ((1.0 - a) * bg + a * f32(p.r))
out[0] = u8(c)
out = out[1:]
out[0].r = u8(c)
out = out[1:]
}
} else if .alpha_premultiply in options {
@@ -874,14 +874,14 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
for p in inp {
a := f32(p.g) / 255.0
c := f32(p.r) * a
out[0] = u8(c)
out = out[1:]
out[0].r = u8(c)
out = out[1:]
}
} else {
// Just drop alpha on the floor.
for p in inp {
out[0] = p.r
out = out[1:]
out[0].r = p.r
out = out[1:]
}
}
@@ -951,11 +951,11 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
bg := G_Pixel_16{}
if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
// Background is RGB 16-bit, take just the red channel.
bg = temp_bg.r
bg.r = temp_bg.r
}
for p in inp {
out[0] = bg if p == key else p
out[0] = bg if p.r == key else p
out = out[1:]
}
@@ -974,8 +974,8 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
for p in inp {
a := f32(p.g) / 65535.0
c := ((1.0 - a) * bg + a * f32(p.r))
out[0] = u16(c)
out = out[1:]
out[0].r = u16(c)
out = out[1:]
}
} else if .alpha_premultiply in options {
@@ -983,14 +983,14 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
for p in inp {
a := f32(p.g) / 65535.0
c := f32(p.r) * a
out[0] = u16(c)
out = out[1:]
out[0].r = u16(c)
out = out[1:]
}
} else {
// Just drop alpha on the floor.
for p in inp {
out[0] = p.r
out = out[1:]
out[0].r = p.r
out = out[1:]
}
}
+6 -6
View File
@@ -199,8 +199,8 @@ save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context
for x in 0 ..< img.width {
i := y * img.width + x
for c in 0 ..< img.channels {
i := i * img.channels + c
fmt.sbprintf(&data, "%i ", pixels[i])
j := i * img.channels + c
fmt.sbprintf(&data, "%i ", pixels[j])
}
fmt.sbprint(&data, "\n")
}
@@ -213,8 +213,8 @@ save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context
for x in 0 ..< img.width {
i := y * img.width + x
for c in 0 ..< img.channels {
i := i * img.channels + c
fmt.sbprintf(&data, "%i ", pixels[i])
j := i * img.channels + c
fmt.sbprintf(&data, "%i ", pixels[j])
}
fmt.sbprint(&data, "\n")
}
@@ -283,7 +283,7 @@ _parse_header_pnm :: proc(data: []byte) -> (header: Header, length: int, err: Er
current_field := 0
current_value := header_fields[0]
parse_loop: for d, i in data[SIG_LENGTH:] {
parse_loop: for d in data[SIG_LENGTH:] {
length += 1
// handle comments
@@ -728,4 +728,4 @@ _register :: proc() {
_ = destroy(img)
}
image.register(.NetPBM, loader, destroyer)
}
}
-2
View File
@@ -4,7 +4,6 @@ Multi_Reader :: struct {
readers: [dynamic]Reader,
}
@(private)
_multi_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
if mode == .Query {
return query_utility({.Read, .Query})
@@ -58,7 +57,6 @@ Multi_Writer :: struct {
writers: [dynamic]Writer,
}
@(private)
_multi_writer_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
if mode == .Query {
return query_utility({.Write, .Query})
+6 -1
View File
@@ -777,6 +777,11 @@ int_from_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, alloca
*/
INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{}
@(init, private)
_init_constants :: proc() {
initialize_constants()
}
initialize_constants :: proc() -> (res: int) {
internal_set( INT_ZERO, 0); INT_ZERO.flags = {.Immutable}
internal_set( INT_ONE, 1); INT_ONE.flags = {.Immutable}
@@ -788,7 +793,7 @@ initialize_constants :: proc() -> (res: int) {
*/
internal_set( INT_NAN, 1); INT_NAN.flags = {.Immutable, .NaN}
internal_set( INT_INF, 1); INT_INF.flags = {.Immutable, .Inf}
internal_set( INT_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf}
internal_set(INT_MINUS_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf}
return _DEFAULT_MUL_KARATSUBA_CUTOFF
}
+9 -18
View File
@@ -1181,28 +1181,18 @@ internal_cmp_digit :: internal_compare_digit
*/
internal_int_compare_magnitude :: #force_inline proc(a, b: ^Int) -> (comparison: int) {
assert_if_nil(a, b)
/*
Compare based on used digits.
*/
// Compare based on used digits.
if a.used != b.used {
if a.used > b.used {
return +1
}
return -1
return +1 if a.used > b.used else -1
}
/*
Same number of used digits, compare based on their value.
*/
// Same number of used digits, compare based on their value.
#no_bounds_check for n := a.used - 1; n >= 0; n -= 1 {
if a.digit[n] != b.digit[n] {
if a.digit[n] > b.digit[n] {
return +1
}
return -1
return +1 if a.digit[n] > b.digit[n] else -1
}
}
return 0
}
internal_compare_magnitude :: proc { internal_int_compare_magnitude, }
@@ -2046,9 +2036,9 @@ internal_int_inverse_modulo :: proc(dest, a, b: ^Int, allocator := context.alloc
if internal_is_positive(a) && internal_eq(b, 1) { return internal_zero(dest) }
/*
`b` cannot be negative and has to be > 1
`b` cannot be negative and b has to be > 1
*/
if internal_is_negative(b) || internal_gt(b, 1) { return .Invalid_Argument }
if internal_is_negative(b) || !internal_gt(b, 1) { return .Invalid_Argument }
/*
If the modulus is odd we can use a faster routine instead.
@@ -2057,6 +2047,7 @@ internal_int_inverse_modulo :: proc(dest, a, b: ^Int, allocator := context.alloc
return _private_inverse_modulo(dest, a, b)
}
internal_int_invmod :: internal_int_inverse_modulo
internal_invmod :: proc{ internal_int_inverse_modulo, }
/*
@@ -2954,4 +2945,4 @@ internal_zero_unused :: proc { internal_int_zero_unused, }
/*
========================== End of low-level routines ==========================
*/
*/
+6 -3
View File
@@ -44,7 +44,7 @@ internal_int_prime_is_divisible :: proc(a: ^Int, allocator := context.allocator)
Computes res == G**X mod P.
Assumes `res`, `G`, `X` and `P` to not be `nil` and for `G`, `X` and `P` to have been initialized.
*/
internal_int_exponent_mod :: proc(res, G, X, P: ^Int, allocator := context.allocator) -> (err: Error) {
internal_int_power_modulo :: proc(res, G, X, P: ^Int, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
dr: int
@@ -112,6 +112,9 @@ internal_int_exponent_mod :: proc(res, G, X, P: ^Int, allocator := context.alloc
*/
return _private_int_exponent_mod(res, G, X, P, 0)
}
internal_int_exponent_mod :: internal_int_power_modulo
internal_int_powmod :: internal_int_power_modulo
internal_powmod :: proc { internal_int_power_modulo, }
/*
Kronecker/Legendre symbol (a|p)
@@ -1109,7 +1112,7 @@ internal_int_prime_next_prime :: proc(a: ^Int, trials: int, bbs_style: bool, all
Generate the restable.
*/
for x := 1; x < _PRIME_TAB_SIZE; x += 1 {
res_tab = internal_mod(a, _private_prime_table[x]) or_return
res_tab = cast(type_of(res_tab))(internal_mod(a, _private_prime_table[x]) or_return)
}
for {
@@ -1411,4 +1414,4 @@ number_of_rabin_miller_trials :: proc(bit_size: int) -> (number_of_trials: int)
case:
return 2 /* For keysizes bigger than 10_240 use always at least 2 Rounds */
}
}
}
+3354 -3401
View File
File diff suppressed because it is too large Load Diff
+168 -21
View File
@@ -39,7 +39,6 @@ init_from_f64 :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), val: f64) {
}
init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fraction: Backing) {
i, f := math.modf(val)
x.i = fraction
x.i &= 1<<Fraction_Width - 1
x.i |= integer
@@ -103,37 +102,51 @@ round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
return (x.i + (1 << (Fraction_Width - 1))) >> Fraction_Width
}
@(require_results)
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
Integer_Width :: 8*size_of(Backing) - Fraction_Width
x := x
buf: [48]byte
i := 0
if x.i < 0 {
if !intrinsics.type_is_unsigned(Backing) && x.i == min(Backing) {
// edge case handling for signed numbers
buf[i] = '-'
i += 1
x.i = -x.i
}
integer := x.i >> Fraction_Width
fraction := x.i & (1<<Fraction_Width - 1)
s := strconv.append_uint(buf[i:], u64(integer), 10)
i += len(s)
if fraction != 0 {
buf[i] = '.'
i += 1
for fraction > 0 {
fraction *= 10
buf[i] = byte('0' + (fraction>>Fraction_Width))
i += copy(buf[i:], _power_of_two_table[Integer_Width])
} else {
if x.i < 0 {
buf[i] = '-'
i += 1
fraction &= 1<<Fraction_Width - 1
x.i = -x.i
}
when size_of(Backing) < 16 {
T :: u64
append_uint :: strconv.append_uint
} else {
T :: u128
append_uint :: strconv.append_u128
}
integer := T(x.i) >> Fraction_Width
fraction := T(x.i) & (1<<Fraction_Width - 1)
s := append_uint(buf[i:], integer, 10)
i += len(s)
if fraction != 0 {
buf[i] = '.'
i += 1
for fraction > 0 {
fraction *= 10
buf[i] = byte('0' + (fraction>>Fraction_Width) % 10)
i += 1
fraction &= 1<<Fraction_Width - 1
}
}
}
n := copy(dst, buf[:i])
return string(dst[:i])
}
@@ -147,3 +160,137 @@ to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.a
copy(str, s)
return string(str)
}
@(private)
_power_of_two_table := [129]string{
"0.5",
"1",
"2",
"4",
"8",
"16",
"32",
"64",
"128",
"256",
"512",
"1024",
"2048",
"4096",
"8192",
"16384",
"32768",
"65536",
"131072",
"262144",
"524288",
"1048576",
"2097152",
"4194304",
"8388608",
"16777216",
"33554432",
"67108864",
"134217728",
"268435456",
"536870912",
"1073741824",
"2147483648",
"4294967296",
"8589934592",
"17179869184",
"34359738368",
"68719476736",
"137438953472",
"274877906944",
"549755813888",
"1099511627776",
"2199023255552",
"4398046511104",
"8796093022208",
"17592186044416",
"35184372088832",
"70368744177664",
"140737488355328",
"281474976710656",
"562949953421312",
"1125899906842624",
"2251799813685248",
"4503599627370496",
"9007199254740992",
"18014398509481984",
"36028797018963968",
"72057594037927936",
"144115188075855872",
"288230376151711744",
"576460752303423488",
"1152921504606846976",
"2305843009213693952",
"4611686018427387904",
"9223372036854775808",
"18446744073709551616",
"36893488147419103232",
"73786976294838206464",
"147573952589676412928",
"295147905179352825856",
"590295810358705651712",
"1180591620717411303424",
"2361183241434822606848",
"4722366482869645213696",
"9444732965739290427392",
"18889465931478580854784",
"37778931862957161709568",
"75557863725914323419136",
"151115727451828646838272",
"302231454903657293676544",
"604462909807314587353088",
"1208925819614629174706176",
"2417851639229258349412352",
"4835703278458516698824704",
"9671406556917033397649408",
"19342813113834066795298816",
"38685626227668133590597632",
"77371252455336267181195264",
"154742504910672534362390528",
"309485009821345068724781056",
"618970019642690137449562112",
"1237940039285380274899124224",
"2475880078570760549798248448",
"4951760157141521099596496896",
"9903520314283042199192993792",
"19807040628566084398385987584",
"39614081257132168796771975168",
"79228162514264337593543950336",
"158456325028528675187087900672",
"316912650057057350374175801344",
"633825300114114700748351602688",
"1267650600228229401496703205376",
"2535301200456458802993406410752",
"5070602400912917605986812821504",
"10141204801825835211973625643008",
"20282409603651670423947251286016",
"40564819207303340847894502572032",
"81129638414606681695789005144064",
"162259276829213363391578010288128",
"324518553658426726783156020576256",
"649037107316853453566312041152512",
"1298074214633706907132624082305024",
"2596148429267413814265248164610048",
"5192296858534827628530496329220096",
"10384593717069655257060992658440192",
"20769187434139310514121985316880384",
"41538374868278621028243970633760768",
"83076749736557242056487941267521536",
"166153499473114484112975882535043072",
"332306998946228968225951765070086144",
"664613997892457936451903530140172288",
"1329227995784915872903807060280344576",
"2658455991569831745807614120560689152",
"5316911983139663491615228241121378304",
"10633823966279326983230456482242756608",
"21267647932558653966460912964485513216",
"42535295865117307932921825928971026432",
"85070591730234615865843651857942052864",
"170141183460469231731687303715884105728",
}
+3 -1
View File
@@ -326,6 +326,7 @@ ln :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
@(require_results)
log2 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
INVLN2 :: 1.4426950408889634073599246810018921374266459541529859341354494069
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = INVLN2 * math.ln(x[i])
@@ -338,6 +339,7 @@ log2 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
@(require_results)
log10 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
INVLN10 :: 0.4342944819032518276511289189166050822943970058036665661144537831
when IS_ARRAY(T) {
for i in 0..<len(T) {
out[i] = INVLN10 * math.ln(x[i])
@@ -355,7 +357,7 @@ log :: proc "contextless" (x, b: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
out[i] = math.ln(x[i]) / math.ln(cast(ELEM_TYPE(T))b[i])
}
} else {
out = INVLN10 * math.ln(x) / math.ln(cast(ELEM_TYPE(T))b)
out = math.ln(x) / math.ln(cast(ELEM_TYPE(T))b)
}
return
}
+56 -21
View File
@@ -172,13 +172,23 @@ projection :: proc "contextless" (x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E)
}
@(require_results)
identity :: proc "contextless" ($T: typeid/[$N][N]$E) -> (m: T) #no_bounds_check {
identity_array_based_matrix :: proc "contextless" ($T: typeid/[$N][N]$E) -> (m: T) #no_bounds_check {
for i in 0..<N {
m[i][i] = E(1)
}
return m
}
@(require_results)
identity_matrix :: proc "contextless" ($T: typeid/matrix[$N, N]$E) -> T #no_bounds_check {
return 1
}
identity :: proc{
identity_array_based_matrix,
identity_matrix,
}
transpose :: intrinsics.transpose
@(require_results)
@@ -267,11 +277,36 @@ to_ptr :: proc{vector_to_ptr, matrix_to_ptr}
vector_angle_between :: proc "contextless" (a, b: $V/[$N]$E) -> E {
a0 := normalize0(a)
b0 := normalize0(b)
return math.acos(dot(a0, b0))
}
quaternion64_angle_between :: proc "contextless" (a, b: $Q/quaternion64) -> f16 {
c := normalize0(conj(a) * b)
return math.acos(c.w)
}
quaternion128_angle_between :: proc "contextless" (a, b: $Q/quaternion128) -> f32 {
c := normalize0(conj(a) * b)
return math.acos(c.w)
}
quaternion256_angle_between :: proc "contextless" (a, b: $Q/quaternion256) -> f64 {
c := normalize0(conj(a) * b)
return math.acos(c.w)
}
angle_between :: proc{
vector_angle_between,
quaternion64_angle_between,
quaternion128_angle_between,
quaternion256_angle_between,
}
// Splines
@(require_results)
vector_slerp :: proc "contextless" (x, y: $T/[$N]$E, a: E) -> T {
vector_slerp :: proc "contextless" (x, y: $T/[$N]$E, a: E) -> T #no_bounds_check {
cos_alpha := dot(x, y)
alpha := math.acos(cos_alpha)
sin_alpha := math.sin(alpha)
@@ -283,7 +318,7 @@ vector_slerp :: proc "contextless" (x, y: $T/[$N]$E, a: E) -> T {
}
@(require_results)
catmull_rom :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
catmull_rom :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T #no_bounds_check {
s2 := s*s
s3 := s2*s
@@ -296,7 +331,7 @@ catmull_rom :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
}
@(require_results)
hermite :: proc "contextless" (v1, t1, v2, t2: $T/[$N]$E, s: E) -> T {
hermite :: proc "contextless" (v1, t1, v2, t2: $T/[$N]$E, s: E) -> T #no_bounds_check {
s2 := s*s
s3 := s2*s
@@ -309,7 +344,7 @@ hermite :: proc "contextless" (v1, t1, v2, t2: $T/[$N]$E, s: E) -> T {
}
@(require_results)
cubic :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
cubic :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T #no_bounds_check {
return ((v1 * s + v2) * s + v3) * s + v4
}
@@ -390,12 +425,12 @@ inverse :: proc{
}
@(require_results)
hermitian_adjoint :: proc "contextless" (m: $M/matrix[$N, N]$T) -> M where intrinsics.type_is_complex(T), N >= 1 {
hermitian_adjoint :: proc "contextless" (m: $M/matrix[$N, N]$T) -> M where intrinsics.type_is_complex(T), N >= 1 #no_bounds_check {
return conj(transpose(m))
}
@(require_results)
trace :: proc "contextless" (m: $M/matrix[$N, N]$T) -> (trace: T) {
trace :: proc "contextless" (m: $M/matrix[$N, N]$T) -> (trace: T) #no_bounds_check {
for i in 0..<N {
trace += m[i, i]
}
@@ -403,7 +438,7 @@ trace :: proc "contextless" (m: $M/matrix[$N, N]$T) -> (trace: T) {
}
@(require_results)
matrix_minor :: proc "contextless" (m: $M/matrix[$N, N]$T, #any_int row, column: int) -> (minor: T) where N > 1 {
matrix_minor :: proc "contextless" (m: $M/matrix[$N, N]$T, #any_int row, column: int) -> (minor: T) where N > 1 #no_bounds_check {
K :: int(N-1)
cut_down: matrix[K, K]T
for col_idx in 0..<K {
@@ -419,25 +454,25 @@ matrix_minor :: proc "contextless" (m: $M/matrix[$N, N]$T, #any_int row, column:
@(require_results)
matrix1x1_determinant :: proc "contextless" (m: $M/matrix[1, 1]$T) -> (det: T) {
matrix1x1_determinant :: proc "contextless" (m: $M/matrix[1, 1]$T) -> (det: T) #no_bounds_check {
return m[0, 0]
}
@(require_results)
matrix2x2_determinant :: proc "contextless" (m: $M/matrix[2, 2]$T) -> (det: T) {
matrix2x2_determinant :: proc "contextless" (m: $M/matrix[2, 2]$T) -> (det: T) #no_bounds_check {
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
@(require_results)
matrix3x3_determinant :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
matrix3x3_determinant :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) #no_bounds_check {
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
return a + b + c
}
@(require_results)
matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) #no_bounds_check {
a := adjugate(m)
#no_bounds_check for i in 0..<4 {
for i in 0..<4 {
det += m[0, i] * a[0, i]
}
return
@@ -447,13 +482,13 @@ matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
@(require_results)
matrix1x1_adjugate :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
matrix1x1_adjugate :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bounds_check {
y = x
return
}
@(require_results)
matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
y[0, 0] = +x[1, 1]
y[0, 1] = -x[1, 0]
y[1, 0] = -x[0, 1]
@@ -462,7 +497,7 @@ matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
}
@(require_results)
matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -477,7 +512,7 @@ matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
@(require_results)
matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -488,13 +523,13 @@ matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
}
@(require_results)
matrix1x1_inverse_transpose :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
matrix1x1_inverse_transpose :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bounds_check {
y[0, 0] = 1/x[0, 0]
return
}
@(require_results)
matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
when intrinsics.type_is_integer(T) {
y[0, 0] = +x[1, 1] / d
@@ -557,13 +592,13 @@ matrix4x4_inverse_transpose :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y:
}
@(require_results)
matrix1x1_inverse :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
matrix1x1_inverse :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bounds_check {
y[0, 0] = 1/x[0, 0]
return
}
@(require_results)
matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
when intrinsics.type_is_integer(T) {
y[0, 0] = +x[1, 1] / d
+144 -101
View File
@@ -584,7 +584,7 @@ angle_axis_from_quaternion :: proc {
@(require_results)
quaternion_from_forward_and_up_f16 :: proc "contextless" (forward, up: Vector3f16) -> Quaternionf16 {
quaternion_from_forward_and_up_f16 :: proc "contextless" (forward, up: Vector3f16) -> Quaternionf16 #no_bounds_check {
f := normalize(forward)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -628,7 +628,7 @@ quaternion_from_forward_and_up_f16 :: proc "contextless" (forward, up: Vector3f1
return normalize(q)
}
@(require_results)
quaternion_from_forward_and_up_f32 :: proc "contextless" (forward, up: Vector3f32) -> Quaternionf32 {
quaternion_from_forward_and_up_f32 :: proc "contextless" (forward, up: Vector3f32) -> Quaternionf32 #no_bounds_check {
f := normalize(forward)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -672,7 +672,7 @@ quaternion_from_forward_and_up_f32 :: proc "contextless" (forward, up: Vector3f3
return normalize(q)
}
@(require_results)
quaternion_from_forward_and_up_f64 :: proc "contextless" (forward, up: Vector3f64) -> Quaternionf64 {
quaternion_from_forward_and_up_f64 :: proc "contextless" (forward, up: Vector3f64) -> Quaternionf64 #no_bounds_check {
f := normalize(forward)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -886,7 +886,7 @@ quaternion_squad :: proc{
@(require_results)
quaternion_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (q: Quaternionf16) {
quaternion_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (q: Quaternionf16) #no_bounds_check {
m3: Matrix3f16 = ---
m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
@@ -894,7 +894,7 @@ quaternion_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (q: Quatern
return quaternion_from_matrix3(m3)
}
@(require_results)
quaternion_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (q: Quaternionf32) {
quaternion_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (q: Quaternionf32) #no_bounds_check {
m3: Matrix3f32 = ---
m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
@@ -902,7 +902,7 @@ quaternion_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (q: Quatern
return quaternion_from_matrix3(m3)
}
@(require_results)
quaternion_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (q: Quaternionf64) {
quaternion_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (q: Quaternionf64) #no_bounds_check {
m3: Matrix3f64 = ---
m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
@@ -917,7 +917,7 @@ quaternion_from_matrix4 :: proc{
@(require_results)
quaternion_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (q: Quaternionf16) {
quaternion_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (q: Quaternionf16) #no_bounds_check {
four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
@@ -967,7 +967,7 @@ quaternion_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (q: Quatern
return
}
@(require_results)
quaternion_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (q: Quaternionf32) {
quaternion_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (q: Quaternionf32) #no_bounds_check {
four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
@@ -1017,7 +1017,7 @@ quaternion_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (q: Quatern
return
}
@(require_results)
quaternion_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (q: Quaternionf64) {
quaternion_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (q: Quaternionf64) #no_bounds_check {
four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
@@ -1147,7 +1147,7 @@ quaternion_between_two_vector3 :: proc{
@(require_results)
matrix2_inverse_transpose_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
matrix2_inverse_transpose_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) #no_bounds_check {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1157,7 +1157,7 @@ matrix2_inverse_transpose_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matri
return c
}
@(require_results)
matrix2_inverse_transpose_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
matrix2_inverse_transpose_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) #no_bounds_check {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1167,7 +1167,7 @@ matrix2_inverse_transpose_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matri
return c
}
@(require_results)
matrix2_inverse_transpose_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) {
matrix2_inverse_transpose_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) #no_bounds_check {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1184,15 +1184,15 @@ matrix2_inverse_transpose :: proc{
@(require_results)
matrix2_determinant_f16 :: proc "contextless" (m: Matrix2f16) -> f16 {
matrix2_determinant_f16 :: proc "contextless" (m: Matrix2f16) -> f16 #no_bounds_check {
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
@(require_results)
matrix2_determinant_f32 :: proc "contextless" (m: Matrix2f32) -> f32 {
matrix2_determinant_f32 :: proc "contextless" (m: Matrix2f32) -> f32 #no_bounds_check {
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
@(require_results)
matrix2_determinant_f64 :: proc "contextless" (m: Matrix2f64) -> f64 {
matrix2_determinant_f64 :: proc "contextless" (m: Matrix2f64) -> f64 #no_bounds_check {
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
matrix2_determinant :: proc{
@@ -1203,7 +1203,7 @@ matrix2_determinant :: proc{
@(require_results)
matrix2_inverse_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
matrix2_inverse_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) #no_bounds_check {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1213,7 +1213,7 @@ matrix2_inverse_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
return c
}
@(require_results)
matrix2_inverse_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
matrix2_inverse_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) #no_bounds_check {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1223,7 +1223,7 @@ matrix2_inverse_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
return c
}
@(require_results)
matrix2_inverse_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) {
matrix2_inverse_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) #no_bounds_check {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1240,7 +1240,7 @@ matrix2_inverse :: proc{
@(require_results)
matrix2_adjoint_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
matrix2_adjoint_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) #no_bounds_check {
c[0, 0] = +m[1, 1]
c[1, 0] = -m[0, 1]
c[0, 1] = -m[1, 0]
@@ -1248,7 +1248,7 @@ matrix2_adjoint_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
return c
}
@(require_results)
matrix2_adjoint_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
matrix2_adjoint_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) #no_bounds_check {
c[0, 0] = +m[1, 1]
c[1, 0] = -m[0, 1]
c[0, 1] = -m[1, 0]
@@ -1256,7 +1256,7 @@ matrix2_adjoint_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
return c
}
@(require_results)
matrix2_adjoint_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) {
matrix2_adjoint_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) #no_bounds_check {
c[0, 0] = +m[1, 1]
c[1, 0] = -m[0, 1]
c[0, 1] = -m[1, 0]
@@ -1271,7 +1271,44 @@ matrix2_adjoint :: proc{
@(require_results)
matrix3_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matrix3f16) {
matrix2_rotate_f16 :: proc "contextless" (angle_radians: f16) -> Matrix2f16 {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
return Matrix2f16{
c, -s,
s, c,
}
}
@(require_results)
matrix2_rotate_f32 :: proc "contextless" (angle_radians: f32) -> Matrix2f32 {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
return Matrix2f32{
c, -s,
s, c,
}
}
@(require_results)
matrix2_rotate_f64 :: proc "contextless" (angle_radians: f64) -> Matrix2f64 {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
return Matrix2f64{
c, -s,
s, c,
}
}
matrix2_rotate :: proc{
matrix2_rotate_f16,
matrix2_rotate_f32,
matrix2_rotate_f64,
}
@(require_results)
matrix3_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matrix3f16) #no_bounds_check {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1296,7 +1333,7 @@ matrix3_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matr
return m
}
@(require_results)
matrix3_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matrix3f32) {
matrix3_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matrix3f32) #no_bounds_check {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1321,7 +1358,7 @@ matrix3_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matr
return m
}
@(require_results)
matrix3_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (m: Matrix3f64) {
matrix3_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (m: Matrix3f64) #no_bounds_check {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1372,21 +1409,21 @@ matrix3_inverse :: proc{
@(require_results)
matrix3_determinant_f16 :: proc "contextless" (m: Matrix3f16) -> f16 {
matrix3_determinant_f16 :: proc "contextless" (m: Matrix3f16) -> f16 #no_bounds_check {
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
return a + b + c
}
@(require_results)
matrix3_determinant_f32 :: proc "contextless" (m: Matrix3f32) -> f32 {
matrix3_determinant_f32 :: proc "contextless" (m: Matrix3f32) -> f32 #no_bounds_check {
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
return a + b + c
}
@(require_results)
matrix3_determinant_f64 :: proc "contextless" (m: Matrix3f64) -> f64 {
matrix3_determinant_f64 :: proc "contextless" (m: Matrix3f64) -> f64 #no_bounds_check {
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
@@ -1400,7 +1437,7 @@ matrix3_determinant :: proc{
@(require_results)
matrix3_adjoint_f16 :: proc "contextless" (m: Matrix3f16) -> (adjoint: Matrix3f16) {
matrix3_adjoint_f16 :: proc "contextless" (m: Matrix3f16) -> (adjoint: Matrix3f16) #no_bounds_check {
adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1413,7 +1450,7 @@ matrix3_adjoint_f16 :: proc "contextless" (m: Matrix3f16) -> (adjoint: Matrix3f1
return adjoint
}
@(require_results)
matrix3_adjoint_f32 :: proc "contextless" (m: Matrix3f32) -> (adjoint: Matrix3f32) {
matrix3_adjoint_f32 :: proc "contextless" (m: Matrix3f32) -> (adjoint: Matrix3f32) #no_bounds_check {
adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1426,7 +1463,7 @@ matrix3_adjoint_f32 :: proc "contextless" (m: Matrix3f32) -> (adjoint: Matrix3f3
return adjoint
}
@(require_results)
matrix3_adjoint_f64 :: proc "contextless" (m: Matrix3f64) -> (adjoint: Matrix3f64) {
matrix3_adjoint_f64 :: proc "contextless" (m: Matrix3f64) -> (adjoint: Matrix3f64) #no_bounds_check {
adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1466,21 +1503,21 @@ matrix3_inverse_transpose :: proc{
@(require_results)
matrix3_scale_f16 :: proc "contextless" (s: Vector3f16) -> (m: Matrix3f16) {
matrix3_scale_f16 :: proc "contextless" (s: Vector3f16) -> (m: Matrix3f16) #no_bounds_check {
m[0, 0] = s[0]
m[1, 1] = s[1]
m[2, 2] = s[2]
return m
}
@(require_results)
matrix3_scale_f32 :: proc "contextless" (s: Vector3f32) -> (m: Matrix3f32) {
matrix3_scale_f32 :: proc "contextless" (s: Vector3f32) -> (m: Matrix3f32) #no_bounds_check {
m[0, 0] = s[0]
m[1, 1] = s[1]
m[2, 2] = s[2]
return m
}
@(require_results)
matrix3_scale_f64 :: proc "contextless" (s: Vector3f64) -> (m: Matrix3f64) {
matrix3_scale_f64 :: proc "contextless" (s: Vector3f64) -> (m: Matrix3f64) #no_bounds_check {
m[0, 0] = s[0]
m[1, 1] = s[1]
m[2, 2] = s[2]
@@ -1494,7 +1531,7 @@ matrix3_scale :: proc{
@(require_results)
matrix3_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) -> (rot: Matrix3f16) {
matrix3_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) -> (rot: Matrix3f16) #no_bounds_check {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
@@ -1516,7 +1553,7 @@ matrix3_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) ->
return rot
}
@(require_results)
matrix3_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) -> (rot: Matrix3f32) {
matrix3_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) -> (rot: Matrix3f32) #no_bounds_check {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
@@ -1607,7 +1644,7 @@ matrix3_look_at :: proc{
@(require_results)
matrix4_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matrix4f16) {
matrix4_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matrix4f16) #no_bounds_check {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1635,7 +1672,7 @@ matrix4_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matr
return m
}
@(require_results)
matrix4_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matrix4f32) {
matrix4_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matrix4f32) #no_bounds_check {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1663,7 +1700,7 @@ matrix4_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matr
return m
}
@(require_results)
matrix4_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (m: Matrix4f64) {
matrix4_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (m: Matrix4f64) #no_bounds_check {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1746,7 +1783,7 @@ matrix4_inverse :: proc{
@(require_results)
matrix4_minor_f16 :: proc "contextless" (m: Matrix4f16, c, r: int) -> f16 {
matrix4_minor_f16 :: proc "contextless" (m: Matrix4f16, c, r: int) -> f16 #no_bounds_check {
cut_down: Matrix3f16
for i in 0..<3 {
col := i if i < c else i+1
@@ -1758,7 +1795,7 @@ matrix4_minor_f16 :: proc "contextless" (m: Matrix4f16, c, r: int) -> f16 {
return matrix3_determinant(cut_down)
}
@(require_results)
matrix4_minor_f32 :: proc "contextless" (m: Matrix4f32, c, r: int) -> f32 {
matrix4_minor_f32 :: proc "contextless" (m: Matrix4f32, c, r: int) -> f32 #no_bounds_check {
cut_down: Matrix3f32
for i in 0..<3 {
col := i if i < c else i+1
@@ -1770,7 +1807,7 @@ matrix4_minor_f32 :: proc "contextless" (m: Matrix4f32, c, r: int) -> f32 {
return matrix3_determinant(cut_down)
}
@(require_results)
matrix4_minor_f64 :: proc "contextless" (m: Matrix4f64, c, r: int) -> f64 {
matrix4_minor_f64 :: proc "contextless" (m: Matrix4f64, c, r: int) -> f64 #no_bounds_check {
cut_down: Matrix3f64
for i in 0..<3 {
col := i if i < c else i+1
@@ -1817,7 +1854,7 @@ matrix4_cofactor :: proc{
@(require_results)
matrix4_adjoint_f16 :: proc "contextless" (m: Matrix4f16) -> (adjoint: Matrix4f16) {
matrix4_adjoint_f16 :: proc "contextless" (m: Matrix4f16) -> (adjoint: Matrix4f16) #no_bounds_check {
for i in 0..<4 {
for j in 0..<4 {
adjoint[i][j] = matrix4_cofactor(m, i, j)
@@ -1826,7 +1863,7 @@ matrix4_adjoint_f16 :: proc "contextless" (m: Matrix4f16) -> (adjoint: Matrix4f1
return
}
@(require_results)
matrix4_adjoint_f32 :: proc "contextless" (m: Matrix4f32) -> (adjoint: Matrix4f32) {
matrix4_adjoint_f32 :: proc "contextless" (m: Matrix4f32) -> (adjoint: Matrix4f32) #no_bounds_check {
for i in 0..<4 {
for j in 0..<4 {
adjoint[i][j] = matrix4_cofactor(m, i, j)
@@ -1835,7 +1872,7 @@ matrix4_adjoint_f32 :: proc "contextless" (m: Matrix4f32) -> (adjoint: Matrix4f3
return
}
@(require_results)
matrix4_adjoint_f64 :: proc "contextless" (m: Matrix4f64) -> (adjoint: Matrix4f64) {
matrix4_adjoint_f64 :: proc "contextless" (m: Matrix4f64) -> (adjoint: Matrix4f64) #no_bounds_check {
for i in 0..<4 {
for j in 0..<4 {
adjoint[i][j] = matrix4_cofactor(m, i, j)
@@ -1851,7 +1888,7 @@ matrix4_adjoint :: proc{
@(require_results)
matrix4_determinant_f16 :: proc "contextless" (m: Matrix4f16) -> (determinant: f16) {
matrix4_determinant_f16 :: proc "contextless" (m: Matrix4f16) -> (determinant: f16) #no_bounds_check {
adjoint := matrix4_adjoint(m)
for i in 0..<4 {
determinant += m[i][0] * adjoint[i][0]
@@ -1859,7 +1896,7 @@ matrix4_determinant_f16 :: proc "contextless" (m: Matrix4f16) -> (determinant: f
return
}
@(require_results)
matrix4_determinant_f32 :: proc "contextless" (m: Matrix4f32) -> (determinant: f32) {
matrix4_determinant_f32 :: proc "contextless" (m: Matrix4f32) -> (determinant: f32) #no_bounds_check {
adjoint := matrix4_adjoint(m)
for i in 0..<4 {
determinant += m[i][0] * adjoint[i][0]
@@ -1867,7 +1904,7 @@ matrix4_determinant_f32 :: proc "contextless" (m: Matrix4f32) -> (determinant: f
return
}
@(require_results)
matrix4_determinant_f64 :: proc "contextless" (m: Matrix4f64) -> (determinant: f64) {
matrix4_determinant_f64 :: proc "contextless" (m: Matrix4f64) -> (determinant: f64) #no_bounds_check {
adjoint := matrix4_adjoint(m)
for i in 0..<4 {
determinant += m[i][0] * adjoint[i][0]
@@ -1882,7 +1919,7 @@ matrix4_determinant :: proc{
@(require_results)
matrix4_inverse_transpose_f16 :: proc "contextless" (m: Matrix4f16) -> (inverse_transpose: Matrix4f16) {
matrix4_inverse_transpose_f16 :: proc "contextless" (m: Matrix4f16) -> (inverse_transpose: Matrix4f16) #no_bounds_check {
adjoint := matrix4_adjoint(m)
determinant: f16 = 0
for i in 0..<4 {
@@ -1897,7 +1934,7 @@ matrix4_inverse_transpose_f16 :: proc "contextless" (m: Matrix4f16) -> (inverse_
return
}
@(require_results)
matrix4_inverse_transpose_f32 :: proc "contextless" (m: Matrix4f32) -> (inverse_transpose: Matrix4f32) {
matrix4_inverse_transpose_f32 :: proc "contextless" (m: Matrix4f32) -> (inverse_transpose: Matrix4f32) #no_bounds_check {
adjoint := matrix4_adjoint(m)
determinant: f32 = 0
for i in 0..<4 {
@@ -1912,7 +1949,7 @@ matrix4_inverse_transpose_f32 :: proc "contextless" (m: Matrix4f32) -> (inverse_
return
}
@(require_results)
matrix4_inverse_transpose_f64 :: proc "contextless" (m: Matrix4f64) -> (inverse_transpose: Matrix4f64) {
matrix4_inverse_transpose_f64 :: proc "contextless" (m: Matrix4f64) -> (inverse_transpose: Matrix4f64) #no_bounds_check {
adjoint := matrix4_adjoint(m)
determinant: f64 = 0
for i in 0..<4 {
@@ -1934,7 +1971,7 @@ matrix4_inverse_transpose :: proc{
@(require_results)
matrix4_translate_f16 :: proc "contextless" (v: Vector3f16) -> Matrix4f16 {
matrix4_translate_f16 :: proc "contextless" (v: Vector3f16) -> Matrix4f16 #no_bounds_check {
m := MATRIX4F16_IDENTITY
m[3][0] = v[0]
m[3][1] = v[1]
@@ -1942,7 +1979,7 @@ matrix4_translate_f16 :: proc "contextless" (v: Vector3f16) -> Matrix4f16 {
return m
}
@(require_results)
matrix4_translate_f32 :: proc "contextless" (v: Vector3f32) -> Matrix4f32 {
matrix4_translate_f32 :: proc "contextless" (v: Vector3f32) -> Matrix4f32 #no_bounds_check {
m := MATRIX4F32_IDENTITY
m[3][0] = v[0]
m[3][1] = v[1]
@@ -1950,7 +1987,7 @@ matrix4_translate_f32 :: proc "contextless" (v: Vector3f32) -> Matrix4f32 {
return m
}
@(require_results)
matrix4_translate_f64 :: proc "contextless" (v: Vector3f64) -> Matrix4f64 {
matrix4_translate_f64 :: proc "contextless" (v: Vector3f64) -> Matrix4f64 #no_bounds_check {
m := MATRIX4F64_IDENTITY
m[3][0] = v[0]
m[3][1] = v[1]
@@ -1965,7 +2002,7 @@ matrix4_translate :: proc{
@(require_results)
matrix4_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) -> Matrix4f16 {
matrix4_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) -> Matrix4f16 #no_bounds_check {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
@@ -1992,7 +2029,7 @@ matrix4_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) ->
return rot
}
@(require_results)
matrix4_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) -> Matrix4f32 {
matrix4_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) -> Matrix4f32 #no_bounds_check {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
@@ -2019,7 +2056,7 @@ matrix4_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) ->
return rot
}
@(require_results)
matrix4_rotate_f64 :: proc "contextless" (angle_radians: f64, v: Vector3f64) -> Matrix4f64 {
matrix4_rotate_f64 :: proc "contextless" (angle_radians: f64, v: Vector3f64) -> Matrix4f64 #no_bounds_check {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
@@ -2053,7 +2090,7 @@ matrix4_rotate :: proc{
@(require_results)
matrix4_scale_f16 :: proc "contextless" (v: Vector3f16) -> (m: Matrix4f16) {
matrix4_scale_f16 :: proc "contextless" (v: Vector3f16) -> (m: Matrix4f16) #no_bounds_check {
m[0][0] = v[0]
m[1][1] = v[1]
m[2][2] = v[2]
@@ -2061,7 +2098,7 @@ matrix4_scale_f16 :: proc "contextless" (v: Vector3f16) -> (m: Matrix4f16) {
return
}
@(require_results)
matrix4_scale_f32 :: proc "contextless" (v: Vector3f32) -> (m: Matrix4f32) {
matrix4_scale_f32 :: proc "contextless" (v: Vector3f32) -> (m: Matrix4f32) #no_bounds_check {
m[0][0] = v[0]
m[1][1] = v[1]
m[2][2] = v[2]
@@ -2069,7 +2106,7 @@ matrix4_scale_f32 :: proc "contextless" (v: Vector3f32) -> (m: Matrix4f32) {
return
}
@(require_results)
matrix4_scale_f64 :: proc "contextless" (v: Vector3f64) -> (m: Matrix4f64) {
matrix4_scale_f64 :: proc "contextless" (v: Vector3f64) -> (m: Matrix4f64) #no_bounds_check {
m[0][0] = v[0]
m[1][1] = v[1]
m[2][2] = v[2]
@@ -2188,7 +2225,7 @@ matrix4_look_at_from_fru :: proc{
@(require_results)
matrix4_perspective_f16 :: proc "contextless" (fovy, aspect, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
matrix4_perspective_f16 :: proc "contextless" (fovy, aspect, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) #no_bounds_check {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2203,7 +2240,7 @@ matrix4_perspective_f16 :: proc "contextless" (fovy, aspect, near, far: f16, fli
return
}
@(require_results)
matrix4_perspective_f32 :: proc "contextless" (fovy, aspect, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) {
matrix4_perspective_f32 :: proc "contextless" (fovy, aspect, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) #no_bounds_check {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2218,7 +2255,7 @@ matrix4_perspective_f32 :: proc "contextless" (fovy, aspect, near, far: f32, fli
return
}
@(require_results)
matrix4_perspective_f64 :: proc "contextless" (fovy, aspect, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) {
matrix4_perspective_f64 :: proc "contextless" (fovy, aspect, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) #no_bounds_check {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2241,7 +2278,7 @@ matrix4_perspective :: proc{
@(require_results)
matrix_ortho3d_f16 :: proc "contextless" (left, right, bottom, top, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
matrix_ortho3d_f16 :: proc "contextless" (left, right, bottom, top, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) #no_bounds_check {
m[0, 0] = +2 / (right - left)
m[1, 1] = +2 / (top - bottom)
m[2, 2] = +2 / (far - near)
@@ -2257,7 +2294,7 @@ matrix_ortho3d_f16 :: proc "contextless" (left, right, bottom, top, near, far: f
return
}
@(require_results)
matrix_ortho3d_f32 :: proc "contextless" (left, right, bottom, top, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) {
matrix_ortho3d_f32 :: proc "contextless" (left, right, bottom, top, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) #no_bounds_check {
m[0, 0] = +2 / (right - left)
m[1, 1] = +2 / (top - bottom)
m[2, 2] = +2 / (far - near)
@@ -2273,7 +2310,7 @@ matrix_ortho3d_f32 :: proc "contextless" (left, right, bottom, top, near, far: f
return
}
@(require_results)
matrix_ortho3d_f64 :: proc "contextless" (left, right, bottom, top, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) {
matrix_ortho3d_f64 :: proc "contextless" (left, right, bottom, top, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) #no_bounds_check {
m[0, 0] = +2 / (right - left)
m[1, 1] = +2 / (top - bottom)
m[2, 2] = +2 / (far - near)
@@ -2297,7 +2334,7 @@ matrix_ortho3d :: proc{
@(require_results)
matrix4_infinite_perspective_f16 :: proc "contextless" (fovy, aspect, near: f16, flip_z_axis := true) -> (m: Matrix4f16) {
matrix4_infinite_perspective_f16 :: proc "contextless" (fovy, aspect, near: f16, flip_z_axis := true) -> (m: Matrix4f16) #no_bounds_check {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2312,7 +2349,7 @@ matrix4_infinite_perspective_f16 :: proc "contextless" (fovy, aspect, near: f16,
return
}
@(require_results)
matrix4_infinite_perspective_f32 :: proc "contextless" (fovy, aspect, near: f32, flip_z_axis := true) -> (m: Matrix4f32) {
matrix4_infinite_perspective_f32 :: proc "contextless" (fovy, aspect, near: f32, flip_z_axis := true) -> (m: Matrix4f32) #no_bounds_check {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2327,7 +2364,7 @@ matrix4_infinite_perspective_f32 :: proc "contextless" (fovy, aspect, near: f32,
return
}
@(require_results)
matrix4_infinite_perspective_f64 :: proc "contextless" (fovy, aspect, near: f64, flip_z_axis := true) -> (m: Matrix4f64) {
matrix4_infinite_perspective_f64 :: proc "contextless" (fovy, aspect, near: f64, flip_z_axis := true) -> (m: Matrix4f64) #no_bounds_check {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2350,19 +2387,19 @@ matrix4_infinite_perspective :: proc{
@(require_results)
matrix2_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix2f16) {
matrix2_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix2f16) #no_bounds_check {
m[0, 0], m[1, 0] = f, 0
m[0, 1], m[1, 1] = 0, f
return
}
@(require_results)
matrix2_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix2f32) {
matrix2_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix2f32) #no_bounds_check {
m[0, 0], m[1, 0] = f, 0
m[0, 1], m[1, 1] = 0, f
return
}
@(require_results)
matrix2_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix2f64) {
matrix2_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix2f64) #no_bounds_check {
m[0, 0], m[1, 0] = f, 0
m[0, 1], m[1, 1] = 0, f
return
@@ -2375,21 +2412,21 @@ matrix2_from_scalar :: proc{
@(require_results)
matrix3_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix3f16) {
matrix3_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix3f16) #no_bounds_check {
m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
return
}
@(require_results)
matrix3_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix3f32) {
matrix3_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix3f32) #no_bounds_check {
m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
return
}
@(require_results)
matrix3_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix3f64) {
matrix3_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix3f64) #no_bounds_check {
m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
@@ -2403,7 +2440,7 @@ matrix3_from_scalar :: proc{
@(require_results)
matrix4_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix4f16) {
matrix4_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix4f16) #no_bounds_check {
m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
@@ -2411,7 +2448,7 @@ matrix4_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix4f16) {
return
}
@(require_results)
matrix4_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix4f32) {
matrix4_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix4f32) #no_bounds_check {
m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
@@ -2419,7 +2456,7 @@ matrix4_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix4f32) {
return
}
@(require_results)
matrix4_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix4f64) {
matrix4_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix4f64) #no_bounds_check {
m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
@@ -2434,19 +2471,19 @@ matrix4_from_scalar :: proc{
@(require_results)
matrix2_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix2f16) {
matrix2_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix2f16) #no_bounds_check {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
@(require_results)
matrix2_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix2f32) {
matrix2_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix2f32) #no_bounds_check {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
@(require_results)
matrix2_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix2f64) {
matrix2_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix2f64) #no_bounds_check {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
@@ -2459,19 +2496,19 @@ matrix2_from_matrix3 :: proc{
@(require_results)
matrix2_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (r: Matrix2f16) {
matrix2_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (r: Matrix2f16) #no_bounds_check {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
@(require_results)
matrix2_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (r: Matrix2f32) {
matrix2_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (r: Matrix2f32) #no_bounds_check {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
@(require_results)
matrix2_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (r: Matrix2f64) {
matrix2_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (r: Matrix2f64) #no_bounds_check {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
@@ -2484,21 +2521,21 @@ matrix2_from_matrix4 :: proc{
@(require_results)
matrix3_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix3f16) {
matrix3_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix3f16) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
return
}
@(require_results)
matrix3_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix3f32) {
matrix3_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix3f32) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
return
}
@(require_results)
matrix3_from_matrix2_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix3f64) {
matrix3_from_matrix2_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix3f64) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
@@ -2512,21 +2549,21 @@ matrix3_from_matrix2 :: proc{
@(require_results)
matrix3_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (r: Matrix3f16) {
matrix3_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (r: Matrix3f16) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return
}
@(require_results)
matrix3_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (r: Matrix3f32) {
matrix3_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (r: Matrix3f32) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return
}
@(require_results)
matrix3_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (r: Matrix3f64) {
matrix3_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (r: Matrix3f64) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
@@ -2540,7 +2577,7 @@ matrix3_from_matrix4 :: proc{
@(require_results)
matrix4_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix4f16) {
matrix4_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix4f16) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
@@ -2548,7 +2585,7 @@ matrix4_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix4f16
return
}
@(require_results)
matrix4_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix4f32) {
matrix4_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix4f32) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
@@ -2556,7 +2593,7 @@ matrix4_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix4f32
return
}
@(require_results)
matrix4_from_matrix2_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix4f64) {
matrix4_from_matrix2_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix4f64) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
@@ -2571,7 +2608,7 @@ matrix4_from_matrix2 :: proc{
@(require_results)
matrix4_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix4f16) {
matrix4_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix4f16) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
@@ -2579,7 +2616,7 @@ matrix4_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix4f16
return
}
@(require_results)
matrix4_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix4f32) {
matrix4_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix4f32) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
@@ -2587,7 +2624,7 @@ matrix4_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix4f32
return
}
@(require_results)
matrix4_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix4f64) {
matrix4_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix4f64) #no_bounds_check {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
@@ -2673,7 +2710,8 @@ to_quaternion :: proc{
@(require_results)
matrix2_orthonormalize_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix2f16) {
matrix2_orthonormalize_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix2f16) #no_bounds_check {
r = m
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
@@ -2683,7 +2721,8 @@ matrix2_orthonormalize_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix2f
return
}
@(require_results)
matrix2_orthonormalize_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix2f32) {
matrix2_orthonormalize_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix2f32) #no_bounds_check {
r = m
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
@@ -2693,7 +2732,8 @@ matrix2_orthonormalize_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix2f
return
}
@(require_results)
matrix2_orthonormalize_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix2f64) {
matrix2_orthonormalize_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix2f64) #no_bounds_check {
r = m
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
@@ -2710,7 +2750,8 @@ matrix2_orthonormalize :: proc{
@(require_results)
matrix3_orthonormalize_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix3f16) {
matrix3_orthonormalize_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix3f16) #no_bounds_check {
r = m
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
@@ -2725,7 +2766,8 @@ matrix3_orthonormalize_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix3f
return
}
@(require_results)
matrix3_orthonormalize_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix3f32) {
matrix3_orthonormalize_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix3f32) #no_bounds_check {
r = m
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
@@ -2740,7 +2782,8 @@ matrix3_orthonormalize_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix3f
return
}
@(require_results)
matrix3_orthonormalize_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix3f64) {
matrix3_orthonormalize_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix3f64) #no_bounds_check {
r = m
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
+164 -31
View File
@@ -644,42 +644,175 @@ trunc :: proc{
}
@(require_results)
round_f16 :: proc "contextless" (x: f16) -> f16 {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
@(require_results)
round_f16le :: proc "contextless" (x: f16le) -> f16le {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
@(require_results)
round_f16be :: proc "contextless" (x: f16be) -> f16be {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
round_f16 :: proc "contextless" (x: f16) -> f16 {
// origin: Go /src/math/floor.go
//
// Copyright (c) 2009 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
mask :: F16_MASK
shift :: F16_SHIFT
bias :: F16_BIAS
bits := transmute(u16)x
e := (bits >> shift) & mask
if e < bias {
bits &= 0x8000
if e == bias - 1 {
bits |= transmute(u16)f16(1)
}
} else if e < bias + shift {
half :: 1 << (shift - 1)
mantissa :: (1 << shift) - 1
e -= bias
bits += half >> e
bits &~= mantissa >> e
}
return transmute(f16)bits
}
@(require_results) round_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(round_f16(f16(x))) }
@(require_results) round_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(round_f16(f16(x))) }
@(require_results)
round_f32 :: proc "contextless" (x: f32) -> f32 {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
round_f32 :: proc "contextless" (x: f32) -> f32 {
// origin: Go /src/math/floor.go
//
// Copyright (c) 2009 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
mask :: F32_MASK
shift :: F32_SHIFT
bias :: F32_BIAS
bits := transmute(u32)x
e := (bits >> shift) & mask
if e < bias {
bits &= 0x8000_0000
if e == bias - 1 {
bits |= transmute(u32)f32(1)
}
} else if e < bias + shift {
half :: 1 << (shift - 1)
mantissa :: (1 << shift) - 1
e -= bias
bits += half >> e
bits &~= mantissa >> e
}
return transmute(f32)bits
}
@(require_results) round_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(round_f32(f32(x))) }
@(require_results) round_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(round_f32(f32(x))) }
@(require_results)
round_f32le :: proc "contextless" (x: f32le) -> f32le {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
@(require_results)
round_f32be :: proc "contextless" (x: f32be) -> f32be {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
@(require_results)
round_f64 :: proc "contextless" (x: f64) -> f64 {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
@(require_results)
round_f64le :: proc "contextless" (x: f64le) -> f64le {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
@(require_results)
round_f64be :: proc "contextless" (x: f64be) -> f64be {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
round_f64 :: proc "contextless" (x: f64) -> f64 {
// origin: Go /src/math/floor.go
//
// Copyright (c) 2009 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
mask :: F64_MASK
shift :: F64_SHIFT
bias :: F64_BIAS
bits := transmute(u64)x
e := (bits >> shift) & mask
if e < bias {
bits &= 0x8000_0000_0000_0000
if e == bias - 1 {
bits |= transmute(u64)f64(1)
}
} else if e < bias + shift {
half :: 1 << (shift - 1)
mantissa :: (1 << shift) - 1
e -= bias
bits += half >> e
bits &~= mantissa >> e
}
return transmute(f64)bits
}
@(require_results) round_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(round_f64(f64(x))) }
@(require_results) round_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(round_f64(f64(x))) }
round :: proc{
round_f16, round_f16le, round_f16be,
round_f32, round_f32le, round_f32be,
@@ -2353,4 +2486,4 @@ INF_F64 :: f64(0h7FF0_0000_0000_0000)
NEG_INF_F64 :: f64(0hFFF0_0000_0000_0000)
SNAN_F64 :: f64(0h7FF0_0000_0000_0001)
QNAN_F64 :: f64(0h7FF8_0000_0000_0001)
QNAN_F64 :: f64(0h7FF8_0000_0000_0001)
+20
View File
@@ -834,3 +834,23 @@ choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
}
return array[int63_max(n, r)]
}
@(require_results)
choice_enum :: proc($T: typeid, r: ^Rand = nil) -> T
where
intrinsics.type_is_enum(T),
size_of(T) <= 8,
len(T) == cap(T) /* Only allow contiguous enum types */
{
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
u64(max(T)) > u64(max(i64)) {
i := uint64(r) % u64(len(T))
i += u64(min(T))
return T(i)
} else {
i := int63_max(i64(len(T)), r)
i += i64(min(T))
return T(i)
}
}
+15 -50
View File
@@ -83,11 +83,7 @@ free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_locatio
}
free_with_size :: proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil || allocator.procedure == nil {
return nil
}
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
return err
return runtime.mem_free_with_size(ptr, byte_count, allocator, loc)
}
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
@@ -130,19 +126,19 @@ query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_locatio
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return free_with_size(raw_data(str), len(str), allocator, loc)
return runtime.delete_string(str, allocator, loc)
}
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return free((^byte)(str), allocator, loc)
return runtime.delete_cstring(str, allocator, loc)
}
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
return free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
return runtime.delete_dynamic_array(array, loc)
}
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
return runtime.delete_slice(array, allocator, loc)
}
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
return runtime.map_free_dynamic(transmute(Raw_Map)m, runtime.map_info(T), loc)
return runtime.delete_map(m, loc)
}
@@ -161,71 +157,40 @@ new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location)
}
@(require_results)
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
data := alloc_bytes(size_of(T), alignment, allocator, loc) or_return
t = (^T)(raw_data(data))
return
return runtime.new_aligned(T, alignment, allocator, loc)
}
@(require_results)
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
backing := alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return
t = (^T)(raw_data(backing))
if t != nil {
t^ = data
return t, nil
}
return nil, .Out_Of_Memory
return runtime.new_clone(data, allocator, loc)
}
@(require_results)
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) {
runtime.make_slice_error_loc(loc, len)
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return
if data == nil && size_of(E) != 0 {
return
}
slice = transmute(T)Raw_Slice{raw_data(data), len}
return
return runtime.make_aligned(T, len, alignment, allocator, loc)
}
@(require_results)
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_aligned(T, len, align_of(E), allocator, loc)
return runtime.make_slice(T, len, allocator, loc)
}
@(require_results)
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc)
return runtime.make_dynamic_array(T, allocator, loc)
}
@(require_results)
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
return runtime.make_dynamic_array(T, len, allocator, loc)
}
@(require_results)
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) {
runtime.make_dynamic_array_error_loc(loc, len, cap)
data := alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator}
if data == nil && size_of(E) != 0 {
s.len, s.cap = 0, 0
}
array = transmute(T)s
return
return runtime.make_dynamic_array(T, len, cap, allocator, loc)
}
@(require_results)
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1<<runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) {
runtime.make_map_expr_error_loc(loc, cap)
context.allocator = allocator
err = reserve_map(&m, cap, loc)
return
return runtime.make_map(T, cap, allocator, loc)
}
@(require_results)
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
runtime.make_slice_error_loc(loc, len)
data := alloc_bytes(size_of(E)*len, align_of(E), allocator, loc) or_return
if data == nil && size_of(E) != 0 {
return
}
mp = cast(T)raw_data(data)
return
return runtime.make_multi_pointer(T, len, allocator, loc)
}
make :: proc{

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