Compare commits

..

615 Commits

Author SHA1 Message Date
gingerBill b4ca044ae0 Merge pull request #3846 from Feoramund/add-more-net-tests
Add more `core:net` tests
2024-07-01 17:56:07 +01:00
gingerBill a2b23de0a7 Sanity check for a nullptr 2024-07-01 12:24:46 +01:00
gingerBill 42ff711114 Fix #3515 2024-07-01 12:22:18 +01:00
gingerBill 544959326b Add intrinsics.type_struct_has_implicit_padding #3844 2024-07-01 12:13:35 +01:00
gingerBill 942f3f5220 Add non_zero_* variants for #soa dynamic array 2024-07-01 12:04:29 +01:00
gingerBill 8ddfcf174d Add #soa support to append directly 2024-07-01 11:51:28 +01:00
gingerBill 4086a62167 Fix showing error when not meant to 2024-07-01 11:50:35 +01:00
gingerBill 3a43928184 Add #no_broadcast to append_soa 2024-07-01 11:47:33 +01:00
gingerBill 7f6fe91896 Add #soa forms to reserve, clear, and resize 2024-07-01 11:37:21 +01:00
gingerBill 11617af8f5 Add support for make(#soa[]T) etc 2024-07-01 11:34:50 +01:00
gingerBill 4a2b87672c Unify #soa code for structs and arrays 2024-07-01 11:11:06 +01:00
gingerBill fb0b95bcad Merge pull request #3760 from Feoramund/refactor-show-error-on-line
Refactor `show_error_on_line`
2024-07-01 11:03:29 +01:00
gingerBill 1a4edad63e Merge pull request #3848 from laytan/wasm-c-abi-indirect-multifield-struct
make structs with multiple fields always return indirect in wasm c abi
2024-07-01 11:02:49 +01:00
Laytan Laats ef6a73c7ef fix not setting cc before using it in abi computations 2024-07-01 01:30:44 +02:00
Laytan Laats e3dde8caf8 make structs with multiple fields always return indirect in wasm c abi 2024-07-01 00:50:10 +02:00
Feoramund 8e64265aed Replace unneeded transmute 2024-06-30 16:39:22 -04:00
Feoramund 1e7dfd0e34 Add more core:net tests
- UDP send & read
- DNS resolution
- Nonblocking socket option
2024-06-30 16:39:22 -04:00
gingerBill 13ace1dac9 Merge pull request #3839 from flysand7/tlhelp-fix
[sys/windows]: Fix the value for TH32CS_SNAPPROCESS constant
2024-06-30 20:15:33 +01:00
Jeroen van Rijn efd933e221 Merge pull request #3842 from karl-zylinski/fix-poly-bitset-size
Fix for bit_set[$T] proc params always being generated as i8
2024-06-30 21:10:42 +02:00
Karl Zylinski e0face1ac8 Make types like bit_set[$T] have their upper and lower bits overwritten by upper/lower from the source types when checking if polymorphic type is assignable. This fixes an issue where an i8 was always generated for bit_sets with generic elements, because it couldn't figure out upper/lower when check_bit_set_type was run. Fixes #2860 2024-06-30 20:36:51 +02:00
Jeroen van Rijn 4fbd22b668 Merge pull request #3840 from H0mTanks/master
Add missing enum member for WSAEWOULDBLOCK in TCP_Send_Error for windows.
2024-06-30 11:29:31 +02:00
h0mtanks 2b615b09f0 fixed whitespace 2024-06-30 13:47:11 +05:30
h0mtanks d1603ebac4 added missing would_block enum member 2024-06-30 13:42:55 +05:30
flysand7 561b96b20d [sys/windows]: Fix the value for TH32CS_SNAPPROCESS constant 2024-06-30 13:34:08 +11:00
gingerBill afe24a0d4d Merge pull request #3838 from flysand7/windows-tlhelp
[core/windows]: Bind tlhelp.h functions from kernel32.dll
2024-06-30 02:52:35 +01:00
flysand7 2737ccab3d [core/windows]: Bind tlhelp.h functions from kernel32.dll 2024-06-30 12:35:42 +11:00
Feoramund 8ed5cb283b Re-implement the error squiggles with visual width 2024-06-29 18:55:12 -04:00
Feoramund 8b305a4c67 Add UCG library to Odin compiler 2024-06-29 18:42:56 -04:00
gingerBill 80592f0f51 Add -disallow-do in CI 2024-06-29 22:50:23 +01:00
gingerBill af3401631a Remove the -disallow-do aspect from -strict-style 2024-06-29 22:48:43 +01:00
gingerBill 5637ed9ecd Merge pull request #3836 from laytan/run-wasi-demo-in-ci
wasi: make the demo run on wasi and run it in CI
2024-06-29 22:30:31 +01:00
Laytan Laats 604551eb2d wasi: make the demo run on wasi and run it in CI 2024-06-29 23:15:31 +02:00
Jeroen van Rijn 476d0087c8 Fix bug in div3 fast path. 2024-06-29 23:14:12 +02:00
gingerBill 34fce83d66 Improve -strict-style rules for if-else statements 2024-06-29 20:04:34 +01:00
gingerBill b1a1da6618 Add -vet-tabs 2024-06-29 19:54:31 +01:00
gingerBill e296d6fb90 Fix loads of indentation issues with mixing spaces and tabs 2024-06-29 19:50:51 +01:00
gingerBill 90244a0849 Fix accidental deletion in the -strict-style help comment 2024-06-29 19:41:45 +01:00
gingerBill f4abdf716e Move microarch stuff to a separate file 2024-06-29 19:29:20 +01:00
gingerBill f64d1df90b Add note regarding -strict-style 2024-06-29 19:27:26 +01:00
gingerBill 888bf28076 -strict-style implies -disallow-do 2024-06-29 19:26:11 +01:00
gingerBill 103eccf104 More style improvements 2024-06-29 19:23:58 +01:00
gingerBill 663661db53 Update core to -strict-style 2024-06-29 19:16:32 +01:00
gingerBill 835e8bf87a Update -strict-style 2024-06-29 19:15:33 +01:00
gingerBill 2187f3e7ff -strict-style enforce 1TBS (mostly) 2024-06-29 19:14:24 +01:00
gingerBill 5413a8b744 Even more style fixes 2024-06-29 19:11:36 +01:00
gingerBill 3f9a58808c More style improvements 2024-06-29 19:07:34 +01:00
gingerBill e8517e2694 -strict-style: enforce case to be in the same column as switch 2024-06-29 18:56:45 +01:00
gingerBill 96330996a6 Fix indentation 2024-06-29 18:51:39 +01:00
gingerBill b0cbda4ee0 Fix indentation of tests 2024-06-29 18:51:28 +01:00
gingerBill 930c929294 Indentation fixes 2024-06-29 18:49:57 +01:00
gingerBill 7f05b4caf2 Fix generation for unicode entities 2024-06-29 18:43:09 +01:00
gingerBill 6db748b4a4 Remove debug message 2024-06-29 16:18:43 +01:00
gingerBill a935ade0d2 Keep -sanitize:address happy with bit_field loads 2024-06-29 16:18:07 +01:00
gingerBill ff2d042313 Remove unneeded transmute 2024-06-29 13:54:15 +01:00
gingerBill 913c08a33e Add bit_field to demo.odin 2024-06-29 12:56:46 +01:00
gingerBill c88a1bef91 Add another -vet-cast check 2024-06-29 12:40:04 +01:00
gingerBill 706adb1232 Update for transmute -vet-cast 2024-06-29 12:16:43 +01:00
gingerBill b9861a0cf6 cast to transmute 2024-06-29 12:14:21 +01:00
gingerBill e46d772b6d Add check for integer casts 2024-06-29 12:13:41 +01:00
gingerBill c18c0a3364 Adhere to -vet-cast 2024-06-29 12:07:21 +01:00
gingerBill d6b8544f50 Add internal flag for testing stuff 2024-06-29 12:04:31 +01:00
gingerBill 4b71c47fd5 Check for unneeded transmute with -vet-cast 2024-06-29 12:02:31 +01:00
gingerBill 704530497b Rename -vet-identical-cast to -vet-cast; with minor code clean up 2024-06-29 11:44:45 +01:00
gingerBill 4be0fc05bb Merge pull request #3832 from Feoramund/vet-identical-cast
Add `-vet-identical-cast`
2024-06-29 11:37:35 +01:00
gingerBill 52aa7085e4 Use f32 as the immediate type for addition and subtraction for complex32/quaternion64 2024-06-29 11:09:54 +01:00
gingerBill 9113f389d8 Merge pull request #3831 from Feoramund/fix-darwin-test-runner-cancel
Let Darwin safely panic in a test
2024-06-29 11:01:01 +01:00
gingerBill 55e9b8d994 Use explicit calling conventions 2024-06-29 10:48:41 +01:00
gingerBill ee2a0c4010 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-06-29 10:47:15 +01:00
gingerBill f974002839 Use f32 operations rather than f16 in complex32 and quaternion64 to improve accuracy and performance 2024-06-29 10:47:09 +01:00
gingerBill 83b575aec2 Merge pull request #3833 from karl-zylinski/consistent-default-dynamic-array-capacity
Consistent dynamic array default capacity and avoid unnecessary dynamic array allocations
2024-06-29 10:38:22 +01:00
gingerBill 906afa4154 Allow for when x in y { (minor oversight in syntax) 2024-06-29 10:13:15 +01:00
Karl Zylinski 679f9b4e41 Made default capacity of dynamic arrays more consistent.
Before this if you do `arr: [dynamic]int` and then append to arr, then it will have capacity 8.
But if you did `arr := make([dynamic]int, context.temp_allocator)` then arr would have capacity 16.

Now both `arr: [dynamic]int` and `arr := make([dynamic]int, context.temp_allocator)` will resut in arr having zero 0. The only reason to use `make` without an explicit len or cap now is because you want to set it up for a non-default allocator. After the first call to `append` it will now in both cases have capacity 8.

I also updated the documentation on the strings builder, both to reflect this, and also to fix it incorrectly saying that len would be 'max(16,len)', which wasn't true even before these changes.
2024-06-29 08:57:21 +02:00
Feoramund b3caae6db4 Keep -vet happy 2024-06-28 23:14:03 -04:00
Feoramund 6496432b80 Add -vet-identical-cast 2024-06-28 23:14:03 -04:00
Feoramund 0ea0fac2f9 Call pthread_cancel on Darwin, with advisory comment 2024-06-28 20:47:37 -04:00
Feoramund 13539d3be1 Catch SIGTRAP in the test runner
Fixes `panic` for Darwin.
2024-06-28 19:43:03 -04:00
Feoramund 929cc48703 Merge Darwin signals into other UNIX-likes
They're all the same.
2024-06-28 19:03:43 -04:00
Jeroen van Rijn 06652bebce Merge pull request #3829 from Feoramund/fix-vetted-test-name
Let `-vet` be used with `-define:ODIN_TEST_NAMES`
2024-06-28 23:06:25 +02:00
Feoramund 574342af6f Let -vet be used with -define:ODIN_TEST_NAMES 2024-06-28 15:58:22 -04:00
Jeroen van Rijn 007832488d Merge pull request #3827 from Feoramund/require-runtime-linalg-import
Require `base:runtime` import in `core:math/linalg`
2024-06-28 17:44:10 +02:00
Feoramund 2af121752a Require base:runtime import in core:math/linalg 2024-06-28 11:34:35 -04:00
gingerBill 0c8924ea85 Merge pull request #3822 from jasonKercher/os2-rebase
os2 linux round 2
2024-06-28 15:43:18 +01:00
jason 6a894195cb revert os2/process 2024-06-28 09:45:22 -04:00
jason dc954307d7 fix assumption about std handles in os2/file.odin 2024-06-28 07:55:33 -04:00
jason a15cbc474d change error strings to an enumerated array in rodata; print_error takes a file argument 2024-06-28 07:45:24 -04:00
gingerBill 37afd469c6 Merge pull request #3339 from axxeny/patch-1
fix comments in core:container/queue.
2024-06-28 12:00:34 +01:00
gingerBill 853173a11b Merge pull request #3517 from timosperisen/patch-1
Update builder.odin
2024-06-28 12:00:23 +01:00
gingerBill a1ae6f161b Merge pull request #3502 from ARtemachka/master
Fix typo in core_builtin
2024-06-28 12:00:10 +01:00
gingerBill 883f6c129a Merge pull request #3643 from korvahkh/fix-omitempty-comma
encoding/json: Fix struct marshal() emitting comma after omitted field
2024-06-28 11:50:31 +01:00
gingerBill 861d51b760 Merge pull request #3825 from Valakor/wgpu
wgpu_native fixes
2024-06-28 11:49:35 +01:00
gingerBill 17efb87eef Add -custom-attribute 2024-06-28 11:44:39 +01:00
gingerBill 1747fdc3f0 Fix signature 2024-06-28 11:07:54 +01:00
gingerBill bef3ca98f0 Fix signature 2024-06-28 11:01:57 +01:00
gingerBill e15c5c4692 Fix missing import 2024-06-28 10:32:16 +01:00
gingerBill 9a4ffa79db Add missing libraries to examples/all 2024-06-28 10:17:00 +01:00
gingerBill ec38215842 Fix #3803 2024-06-28 10:09:46 +01:00
gingerBill 67e9a6fd9b Improve error reporting on "Failed to parse fail" and show the line error if possible 2024-06-28 10:04:08 +01:00
gingerBill 4824050c99 Merge pull request #3792 from Feoramund/core-uuid
Add `core:encoding/uuid`
2024-06-28 09:49:23 +01:00
gingerBill 35651cfc17 Improve tokenization for false integer literals but not effect possible float literals 2024-06-28 09:24:34 +01:00
gingerBill dde7cb6e7f Improve error message for #3754 2024-06-28 09:21:33 +01:00
gingerBill 862a04376f Improve tokenizing wrong number literals 2024-06-28 09:16:01 +01:00
gingerBill 5a9698e8cb Properly fix #3820 2024-06-28 09:08:57 +01:00
gingerBill 0c8a81d298 Fix #3819 2024-06-28 08:57:31 +01:00
gingerBill 06ff08b9cf Fix #3820 2024-06-28 08:53:05 +01:00
gingerBill 52ea63f89c Fix #3471 2024-06-28 08:43:25 +01:00
gingerBill f883cd5053 Initialize default context in heap_allocator_other.odin 2024-06-28 08:33:12 +01:00
Matthew Pohlmann 4bf9de7237 SupportedLimitsExtras.chain should be a ChainedStructOut to properly chain from SupportedLimits 2024-06-27 21:53:16 -07:00
Matthew Pohlmann 0a504c2647 Some fixes to wgpu_native.
1. LogCallback should have the "c" calling convention and takes a
   userdata pointer like other callbacks.
2. DevicePoll's wrappedSubmissionIndex argument is an optional in/out
   parameter. The previous wrapper method was broken since it passed a
   non-nil structure to the underlying API with invalid members.
2024-06-27 21:41:48 -07:00
Jeroen van Rijn d31f88bfaa Merge pull request #3823 from laytan/enforce-global-foreign-import-variables
disallow non-global foreign import of variables on wasm
2024-06-28 02:11:50 +02:00
Laytan Laats b79d7e6917 disallow non-global foreign import of variables on wasm 2024-06-28 01:34:09 +02:00
Jeroen van Rijn ed7d34beb0 Avoid sporadic wasm crash 2024-06-28 00:39:51 +02:00
Jeroen van Rijn aba274cf75 Merge pull request #3821 from Kelimion/clang_override
Allow `ODIN_CLANG_PATH=clang-path` overrides for link stage.
2024-06-27 23:36:39 +02:00
Jeroen van Rijn 140ee036ce wasm error is no longer relevant 2024-06-27 23:29:36 +02:00
Jeroen van Rijn 58cd75350b Allow ODIN_CLANG_PATH=clang-path overrides
Link using `clang`, unless overridden by `ODIN_CLANG_PATH` environment variable.

Fixes #3783
2024-06-27 23:21:58 +02:00
jason f24f72c280 convert all to use sys/linux over sys/unix; new implementations for pipe, process and env 2024-06-27 17:14:48 -04:00
jason f22754fc90 sys/linux: fix some syscalls and types; add more to Sig_Action and Sig_Info; Pid int->i32 2024-06-27 16:29:47 -04:00
Jeroen van Rijn a0a48bfe34 Fix alignment. 2024-06-27 21:56:26 +02:00
Jeroen van Rijn 9f8b84c212 Merge pull request #3815 from laytan/tlsf-fixes
tlsf: destroy first pool & properly zero memory
2024-06-27 19:50:24 +02:00
Laytan Laats 1c199f52d6 tlsf: destroy first pool & properly zero memory 2024-06-27 19:27:07 +02:00
gingerBill 521182a100 Merge pull request #3813 from tadeohepperle/fix-vulkan-codegen-arrays-as-proc-args
Fix `vender:vulkan` codegen for procedures with fixed size arrays are arguments
2024-06-27 16:34:17 +01:00
Jeroen van Rijn aa27cd4b0b Allow core:odin to parse @(require) import 2024-06-27 16:14:16 +02:00
gingerBill b1d06ea03f Merge pull request #3808 from karl-zylinski/fix-append-elem-max-confusion
Replace `max(8, 1)` in _append_elem with just `8` and a comment.
2024-06-26 21:14:08 +01:00
Jeroen van Rijn e37afa3ada Merge pull request #3814 from Kelimion/fix_fixed
Fix core:math/fixed.
2024-06-26 20:46:46 +02:00
Tadeo hepperle b834bd32f5 fix typo 2024-06-26 20:31:43 +02:00
Jeroen van Rijn 3726f0b73c Merge pull request #3812 from Feoramund/add-table-decorations
Add `write_decorated_table` to `text/table`
2024-06-26 20:15:11 +02:00
Jeroen van Rijn c33bf7673f Fix core:math/fixed. 2024-06-26 20:12:55 +02:00
Tadeo hepperle 67e69f7196 Merge branch 'master' into fix-vulkan-codegen-arrays-as-proc-args 2024-06-26 19:31:00 +02:00
Tadeo hepperle 18dadd94e3 fix proc parsing 2024-06-26 19:30:21 +02:00
Feoramund ba354e0524 Add documentation for write_decorated_table 2024-06-26 12:53:34 -04:00
Feoramund f13d30ad23 Add write_decorated_table API 2024-06-26 12:50:43 -04:00
Jeroen van Rijn 8d1827838f Merge pull request #3807 from karl-zylinski/ctprint
Added fmt.ctprint
2024-06-25 22:52:48 +02:00
Karl Zylinski f227a40652 Replace max(8, 1) in _append_elem with just 8 and a comment. 2024-06-25 22:48:14 +02:00
Karl Zylinski 4e2d12c540 Added fmt.ctprint 2024-06-25 22:32:20 +02:00
Jeroen van Rijn 5d1d98cef3 Merge pull request #3806 from NicknEma/windows_fixes
Update CommandLineToArgvW return type
2024-06-25 12:22:27 +02:00
NicknEma 845613c404 Update CommandLineToArgvW return type
It now returns a multi-pointer so it can be iterated without any casting.
2024-06-25 12:10:17 +02:00
gingerBill 93441a043a Make runtime.heap_alloc contextless 2024-06-25 09:38:49 +01:00
gingerBill c098739484 Remove @(warning) and #warning(...) 2024-06-25 09:36:59 +01:00
Jeroen van Rijn e42029c5ed wgpu ld -> dl 2024-06-25 01:35:26 +02:00
Jeroen van Rijn d97fe41834 Mark deux 2024-06-24 22:45:12 +02:00
Jeroen van Rijn d3d73590d3 Fix CSV comments. 2024-06-24 22:43:14 +02:00
Jeroen van Rijn 185e39e53d Merge pull request #3791 from Feoramund/fix-text-table-alignment
Refactor `core:text/table`
2024-06-24 21:59:33 +02:00
Feoramund 8b05ec1765 Add string caching example documentation 2024-06-24 15:11:43 -04:00
Feoramund fbd609fa37 Add complete example showcasing Unicode support 2024-06-24 15:04:45 -04:00
Feoramund f325a08e57 Make core:text/table examples more complete
They should be completely copy-and-paste friendly now, without the
assumption of someone using `using table`.
2024-06-24 15:04:45 -04:00
Feoramund 7a43404ea1 Add Width_Proc documentation 2024-06-24 15:04:45 -04:00
Feoramund 7da96c484d Remove table.dirty 2024-06-24 14:28:05 -04:00
Feoramund e397bdf11d Let WIDTH_PROC be specified as proc argument to write_*_table 2024-06-24 13:38:59 -04:00
Feoramund 67b4cb0038 Crunch the ranges 2024-06-24 13:18:54 -04:00
Jeroen van Rijn 8eafd9eb82 wgpu import 2024-06-23 22:19:29 +02:00
Feoramund ca58d7771b Use new API from_nanoseconds in uuid 2024-06-22 18:36:42 -04:00
Feoramund 8a4a3ed66e Change how Time is constructed in uuid 2024-06-22 18:21:32 -04:00
Feoramund 4481f9c695 Clarify some uuid legacy documentation 2024-06-22 18:21:32 -04:00
Feoramund 5a75cac5b9 Add API for creating custom version 8 UUIDs 2024-06-22 18:21:32 -04:00
Feoramund e9b882be05 Add vendor-specific version 8 UUID generation (hashing) 2024-06-22 18:21:32 -04:00
Feoramund 859cbf7d72 Test if v1 and v6 UUID node is set correctly 2024-06-22 18:21:32 -04:00
Feoramund d559feb701 Add uuid test for timestamps 2024-06-22 18:21:32 -04:00
Feoramund f6344577d3 Add UUID sorting tests 2024-06-22 18:21:32 -04:00
Feoramund 339b2b23f6 Add unsafe_write to uuid package 2024-06-22 18:21:32 -04:00
Feoramund 8b8f8c7f7d Address minor organizational issues 2024-06-22 18:21:32 -04:00
Feoramund 95a9c9b016 Fix indentation 2024-06-22 18:21:32 -04:00
Feoramund 9b3a104640 Add buffer-based to_string to uuid package 2024-06-22 18:21:32 -04:00
Feoramund 9b265b2309 Improve time-related API in uuid package
- Let timestamps be specified by the user.
- Change `time_v*` to `raw_time_v*` and implement an API that returns
  timestamps from the `time` package.
2024-06-22 18:21:32 -04:00
Feoramund ea771d0cb7 Update uuid package documentation 2024-06-22 18:21:31 -04:00
Feoramund fcdba334ea Require CSPRNG in UUID generation where applicable 2024-06-22 18:21:31 -04:00
Feoramund 3aa232a894 Move v3 and v5 UUID procs to uuid/legacy 2024-06-22 18:21:31 -04:00
Feoramund 9866b54d59 Add version 6 UUID generation 2024-06-22 18:21:31 -04:00
Feoramund 525bfca4ef Add version 1 UUID generation 2024-06-22 18:21:31 -04:00
Feoramund 4cfbd83b10 Add version 7 UUID generation 2024-06-22 18:21:31 -04:00
Feoramund fee81985b4 Make UUID namespaces @(rodata) 2024-06-22 18:21:31 -04:00
Feoramund 6da99b888a Make UUID Identfier only a distinct byte array 2024-06-22 18:21:31 -04:00
Feoramund 31873ed466 Fix wrong comments 2024-06-22 18:21:31 -04:00
Feoramund 4dacddd85e Add core:encoding/uuid 2024-06-22 18:21:27 -04:00
Jeroen van Rijn 3af9d31bd5 Add time.from_nanoseconds 2024-06-23 00:19:24 +02:00
Jeroen van Rijn dae9a8dfff Merge pull request #3796 from Feoramund/fix-lack-of-str-arg-or-cont
Fix print segfault by missing argument in `or_*` shadowed error
2024-06-22 17:57:02 +02:00
Feoramund f58eded5d2 Fix print segfault by missing argument in or_* shadowed error 2024-06-22 11:45:43 -04:00
Jeroen van Rijn 13c58948f4 Merge pull request #3793 from Kelimion/build_test_only
Add `-build-mode:test`
2024-06-21 16:12:13 +02:00
Jeroen van Rijn 9f696a646f Add -build-mode:test. 2024-06-21 16:02:55 +02:00
gingerBill bad4a6237e Delete misc/roadmap.md 2024-06-21 00:02:12 +01:00
Feoramund bf44a94065 Update core:text/table documentation 2024-06-20 18:22:03 -04:00
Feoramund 82d92dc46c Use log.error instead of eprintf 2024-06-20 17:53:45 -04:00
Feoramund b81458073e Add new API to core:text/table
- `header/row_of_values`, same `header/row`, more verbose name
- `aligned_header/row_of_values`, set alignment for an entire row
- `header/row_of_aligned_values`, set alignment per value
2024-06-20 17:53:45 -04:00
Feoramund 585747bbbf Clarify error message 2024-06-20 17:53:34 -04:00
Feoramund 99c955d124 Remove unneeded loc argument 2024-06-20 15:26:00 -04:00
Feoramund fd28199178 Optimize printing of markdown tables
Check only once if the table has a header row, instead of every row.
2024-06-20 15:01:09 -04:00
Feoramund dd099d9dd6 Add Unicode support to core:text/table 2024-06-20 15:01:09 -04:00
Feoramund 2241ca8e72 Use tabs to indent HTML tables 2024-06-20 13:06:32 -04:00
Feoramund b66b960e7e Don't build HTML tables
`build` only recalculates length and width information, and this is not
needed for HTML tables.
2024-06-20 13:06:32 -04:00
Feoramund 4a3684c5e8 Rename write_ascii_table to write_plain_table 2024-06-20 13:06:28 -04:00
Jeroen van Rijn 91cc006e8f Fix unaligned store for rand 2024-06-20 17:54:37 +02:00
Jeroen van Rijn ac4577ca66 Merge pull request #3790 from Feoramund/optimize-common-rng-dest-size
Optimize default RNG for the common case
2024-06-20 17:41:13 +02:00
Feoramund 1dfc89567e Optimize default RNG for the common case 2024-06-20 11:27:51 -04:00
gingerBill 28e33d86de Merge pull request #3776 from IllusionMan1212/os2-read-fix
fix(os2): check for 0 bytes read and return EOF
2024-06-20 15:37:41 +01:00
gingerBill dab3c832e0 Add #warning(<string>) builtin compile time procedure 2024-06-20 15:32:30 +01:00
Jeroen van Rijn 94a8c38d86 Add GetNumberOfConsoleInputEvents 2024-06-20 15:19:25 +02:00
gingerBill 8c760e140f Move import to a windows specific file 2024-06-20 12:38:09 +01:00
gingerBill 3db7780a2c Add vendor:wgpu to examples/all 2024-06-20 12:20:06 +01:00
gingerBill 8b446fc88d Fix wgpu paths for Windows 2024-06-20 12:18:26 +01:00
gingerBill c9884906d9 Add #directory 2024-06-20 12:15:55 +01:00
gingerBill 5dc98336a8 Merge pull request #3524 from Feoramund/freebsd-amd64-syscall-errno
Add `intrinsics.syscall_bsd`
2024-06-20 11:47:01 +01:00
gingerBill 23351ca8be Merge pull request #3137 from laytan/show-defineable
Add flags to show/export defineable values and warn if a -define is unused in the project
2024-06-20 11:45:52 +01:00
gingerBill 56383e45a7 Fix #3768 2024-06-20 11:44:00 +01:00
gingerBill 7184792f7a Merge pull request #3740 from laytan/wgpu
Add `vendor:wgpu`
2024-06-20 11:39:58 +01:00
gingerBill d4aa6c3288 Merge pull request #3788 from Beefster09/builder-utils
Add to_cstring for Builders
2024-06-20 11:34:03 +01:00
gingerBill 7703bc595c Merge pull request #3789 from Feoramund/unicode-annex-11
Measure `East_Asian_Width` during grapheme decoding
2024-06-20 11:32:06 +01:00
Feoramund e620645a03 Measure East_Asian_Width during grapheme decoding 2024-06-19 22:35:36 -04:00
Justin Snyder 1a6bb59125 drop unnecessary defer 2024-06-19 18:25:42 -06:00
Justin Snyder 271782d2f4 fix typo
apparently i had a moment of dyslexia
2024-06-19 17:53:34 -06:00
Justin Snyder 1b7c4c2efe trim added null byte 2024-06-19 17:35:16 -06:00
Jeroen van Rijn 4380934283 Merge pull request #3787 from jasonKercher/more-windows-comm
Add more bindings for comm ports and console events
2024-06-19 23:32:12 +02:00
Jeroen van Rijn 21806e5fa5 Merge pull request #3786 from fabiansperber/fix-d3d11-infoqueue
Fix d3d11 IInfoQueue_VTable & Add LoadLibraryExW
2024-06-19 23:26:40 +02:00
jason 93e67f6bec make -strict-style happy 2024-06-19 17:15:09 -04:00
jason 6b1e76985c move DCB to types.odin; add more bindings for comm functions 2024-06-19 17:04:18 -04:00
Fabian Sperber 0e9bf86123 Fix d3d11 IInfoQueue_VTable & Add LoadLibraryExW
- wrong order & was missing PushStorageFilter & PushRetrievalFilter
2024-06-19 22:22:26 +02:00
Justin Snyder 8f2c4a7ecf Add builder to_cstring 2024-06-19 11:55:19 -06:00
jason e6d84d18d3 Merge remote-tracking branch 'origin/master' into more-windows-comm 2024-06-19 12:33:13 -04:00
jason a9b6d28291 save before merging from upstream 2024-06-19 12:33:05 -04:00
Jeroen van Rijn 2797dc6452 Merge pull request #3785 from mgavioli/new_i18n_API
Separate I18N calls for immutable and for pluraliseable strings. Fixes #3687
2024-06-19 11:50:24 +02:00
Jeroen van Rijn 322b7f1f2f Spell check i18n.odin. 2024-06-19 11:41:58 +02:00
Jeroen van Rijn e0de52efa1 Spell check doc.odin. 2024-06-19 11:15:17 +02:00
Maurizio M. Gavioli 53755824fb Separate the I18N calls for immutable strings and for pluraliseable strings.
Also update tests.
2024-06-19 10:10:26 +02:00
Jeroen van Rijn 7064166da2 Merge pull request #3767 from Feoramund/use-rng-context-in-test-runner
Setup `context.random_generator` in test runner
2024-06-19 09:51:13 +02:00
Feoramund d4803583ff Work around Windows test failure
I am uncertain why this works, but it does. Previously, `rtti_test` was
failing due to non-zero data appearing in the `l_buggy` `Buggy_Struct`.

The issue was caused by calling `runtime.default_random_generator` with
a pointer to the state, somehow. The pointer could be on the stack or in
the heap; it did not matter.

I found two workarounds.

- One is to move the RNG setup behind the call to `free_all`.
- The other is to construct the random generator manually.

Despite my digging and testing, I could find no reason as to why this
works or what the fundamental issue was to begin with. If anyone comes
upon this in the future with direct access to a Windows machine, I
recommend stepping through the program with a debugger to investigate
more deeply into why this happens.
2024-06-18 23:21:04 -04:00
Jeroen van Rijn dff8a9153e Merge pull request #3781 from Feoramund/add-missing-test-imports
Add missing imports to `core` test suite
2024-06-18 18:20:02 +02:00
Feoramund e3f4772d01 Fix removal of temporary file in core:flags test 2024-06-18 11:06:49 -04:00
IllusionMan1212 c987b964c8 fix(os2): check for 0 bytes read and return EOF 2024-06-18 17:03:54 +02:00
Feoramund 6b25d17ef9 Add missing imports to core test suite 2024-06-18 10:38:43 -04:00
gingerBill 3d9db56410 Minor formatting changes to unicode stuff 2024-06-18 12:53:56 +01:00
gingerBill 02cd53d42c Merge pull request #3700 from Feoramund/core-flags
Add package `core:flags`
2024-06-18 12:48:57 +01:00
gingerBill de23965ecb Merge pull request #3775 from Feoramund/unicode-graphemes
Add grapheme analysis facilities to `core:unicode`
2024-06-18 12:48:31 +01:00
gingerBill e8c17ac356 Merge pull request #3778 from Feoramund/fix-2824
Allow custom `main` when `-no-entry-point` is set
2024-06-18 12:46:33 +01:00
gingerBill dc03e8380d Merge pull request #3779 from karl-zylinski/clarify-usage-of-temp-allocator-arena
Clarify when the arena in base:runtime should be used
2024-06-18 12:45:43 +01:00
Jeroen van Rijn aad7ddf2d1 Merge pull request #3780 from destroycomputers/fix/rbtree/key-ordering-constraint
Loosen constraint on the Key type in rb & avl tree
2024-06-18 13:42:36 +02:00
destroycomputers de44dd5412 Loosen constraint on the Key type in rb & avl tree
The current constraint on the `init_ordered' function accepts only
numeric types. While one still can init a non-numerically-keyed tree by
passing a comparator explicitly, the reason to disallow non-numeric
ordered types is nowhere to be found.
2024-06-18 13:31:45 +02:00
Karl Zylinski 11e586494b Clarify that the arena in base:runtime shouldn't be used for anything but the default temp allocator. This is done by renaming the file in which it lives, and also by improving the comment above Arena :: struct {. This should avoid some confusion where people end up using the Arena in base:runtime because they thought it was the 'default arena'. 2024-06-18 12:48:16 +02:00
Jeroen van Rijn dd364c0c0f Merge pull request #3777 from Feoramund/fix-3047
Forbid private test cases
2024-06-18 12:09:58 +02:00
Feoramund 0196cc46e3 Allow custom main when -no-entry-point is set 2024-06-18 01:49:55 -04:00
Feoramund eb5a66c944 Forbid private test cases 2024-06-18 01:28:05 -04:00
Feoramund 9e4899d35c Add tests for decode_grapheme_clusters 2024-06-17 21:57:32 -04:00
Feoramund 63973f431e Make core:unicode letter procs @(require_results) 2024-06-17 21:57:32 -04:00
Feoramund 1620a69398 Add decode_grapheme_clusters to core:unicode/utf8 2024-06-17 21:57:32 -04:00
Feoramund 1a93dfd28f Fix indentation 2024-06-17 13:24:47 -04:00
gingerBill f280ba8511 Merge pull request #3756 from matias-eduardo/empty_structs_hash_fmt
Handle empty structs in hash (#v) fmt
2024-06-16 19:54:28 +01:00
Jeroen van Rijn a66ce4f871 Merge pull request #3773 from Kelimion/res_path_bug
Fix `rc.exe` bug with double-quoted `.res` path.
2024-06-16 19:27:13 +02:00
Jeroen van Rijn fe3baae7a6 Merge pull request #3772 from Feoramund/fix-constant-wrong-type-bitset
Fix `bit_set` construction crash on constant non-integer field
2024-06-16 19:15:50 +02:00
Jeroen van Rijn eebc0dd026 Fix rc.exe bug with double-quoted .res path. 2024-06-16 19:11:54 +02:00
Feoramund 78a5a27212 Fix bit_set construction crash on constant non-integer field 2024-06-16 12:27:42 -04:00
Jeroen van Rijn 339bafe6ff Merge pull request #3770 from skaman/fix-slice-unique
Fix `slice.unique` wrong result
2024-06-16 13:48:21 +02:00
Jeroen van Rijn 0268be1925 Add tests for slice.unique and slice.unique_proc 2024-06-16 13:41:25 +02:00
Sandro Cavazzoni e41878a64f Fix slice.unique wrong result
When you try to make this array unique `[]int{1, 2, 4, 4, 5}` you get
`[]int{1, 4, 5}` instead of `[]int{1, 2, 4, 5}`.
Our index `i` should be increased even with both indices `i` and `j`
have the same value
2024-06-16 11:36:20 +02:00
Feoramund 8b31cddaba Keep -vet happy 2024-06-15 15:49:05 -04:00
Feoramund aab5338134 Add tests for core:math/rand 2024-06-15 15:49:05 -04:00
Feoramund 1a52cf1f1c Use test's random generator
This removes the `create` calls when a test was only setting up a
generator, and it replaces them with `reset` when run in a loop.
2024-06-15 15:49:05 -04:00
Feoramund 5dfd303fd1 Setup default context.random_generator for tests 2024-06-15 15:44:56 -04:00
gingerBill a619ea3bcd Merge pull request #3766 from Feoramund/fix-rng-state
Fix `default_random_generator_proc` not using state
2024-06-15 20:36:10 +01:00
Feoramund 53140dca15 Fix default_random_generator_proc not using state 2024-06-15 12:54:30 -04:00
Jeroen van Rijn ed99e77433 Merge pull request #3765 from Feoramund/add-short-test-logs
Add `ODIN_TEST_SHORT_LOGS` define
2024-06-15 18:01:49 +02:00
gingerBill bacb915ff8 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-06-15 16:24:07 +01:00
gingerBill e41ad2bf16 to_random_generator -> random_generator 2024-06-15 16:24:01 +01:00
gingerBill 4c7469a264 Merge pull request #3764 from Feoramund/test-runner-fixes
Fix half-implemented features in the test runner
2024-06-15 16:19:58 +01:00
Feoramund 753516c392 Add ODIN_TEST_SHORT_LOGS define
Strips out the procedure, date, and time information, for when you just
need to know the file, line, and message.
2024-06-15 11:18:40 -04:00
Feoramund f353adc7fb Prefer log.error over fail_now in this case 2024-06-15 10:53:21 -04:00
Feoramund bb823d5ba0 Make testing.fail_now divergent
This is in line with the old way it worked on Windows.
2024-06-15 10:53:21 -04:00
gingerBill 1c940e3968 Remove dead variable 2024-06-15 15:53:16 +01:00
gingerBill c97ffbecbc Fix rand in container tests 2024-06-15 15:50:35 +01:00
gingerBill 2380720fa2 Fix core:sync/chan for rand 2024-06-15 15:46:48 +01:00
Feoramund 784408358d Call cleanups after test signal 2024-06-15 10:46:47 -04:00
gingerBill 149ecafdef Remove the need for rand in core:math/big 2024-06-15 15:43:57 +01:00
gingerBill 7ec17ecf98 Update core:math/rand to use context.random_generator and remove rand.Rand 2024-06-15 15:33:24 +01:00
gingerBill 318d5e4a7e Add Reset mode 2024-06-15 15:17:23 +01:00
gingerBill eaec8a2bbf Add runtime.default_random_generator 2024-06-15 15:08:49 +01:00
gingerBill c2a01096c4 Remove unneeded reassignment of r 2024-06-15 14:49:09 +01:00
gingerBill 827cb24023 Add random_generator.odin 2024-06-15 14:47:37 +01:00
gingerBill dc4ec8638c Add runtime.Random_Generator interface 2024-06-15 14:45:57 +01:00
Jeroen van Rijn 94ec647923 Merge pull request #3750 from Kelimion/dcb_bitfield
Update kernel32 struct DCB.
2024-06-15 12:14:27 +02:00
Feoramund 51a013fcf1 Work around BSD lack of core:net support 2024-06-15 01:32:48 -04:00
Feoramund f28c6c3bba Keep -vet happy 2024-06-15 00:50:12 -04:00
Feoramund 53e2cdf7af Add core:flags to examples/all 2024-06-15 00:28:47 -04:00
Feoramund 76575e834b Raise error on spaced UNIX-style flag with no value 2024-06-15 00:24:59 -04:00
Feoramund 42a5a2cf17 Fix memory leak in core:flags validation 2024-06-15 00:01:33 -04:00
Feoramund c3acdeb310 Add -- parsing to UNIX-style core:flags
This will allow a user to indicate all arguments after `--` are parsed
into the variadic array.
2024-06-14 23:59:01 -04:00
Feoramund d4e2fa0377 Refactor show_error_on_line
This should adequately solve any issues with Unicode alignment by
sidestepping the issue entirely. With this change, we make use of the
built-in ANSI facilities of the terminal to underline the text.

If the terminal does not support underlining, there are still the
fallback bold markers at the start and end of error locations.
2024-06-14 18:28:47 -04:00
Feoramund 582154f20d Remove unused code from tokenizer 2024-06-14 15:44:50 -04:00
Jeroen van Rijn 908a6ff2d4 Elide unnecessary condition 2024-06-14 21:34:05 +02:00
Jeroen van Rijn 09b3f5a7a7 Merge pull request #3757 from thetarnav/patch-2
Add `#no_bounds_check` to `slice.equal`
2024-06-14 21:27:21 +02:00
Jeroen van Rijn fa4fbbe1ce Merge pull request #3758 from jones-josh/master
Fix big.shrink not actually shrinking
2024-06-14 20:27:03 +02:00
Josh Jones 759139089f Fix big.shrink not actually shrinking 2024-06-14 19:58:13 +02:00
Damian Tarnawski 8ba644dd79 Add #no_bounds_check to slice.equal 2024-06-14 19:45:54 +02:00
Feoramund 8626d38db1 Fix displaying emptiness when error is on first line 2024-06-14 13:02:54 -04:00
Jeroen van Rijn b19bf5bbda Merge pull request #3732 from FrancisTheCat/master
add missing functions to core:sys/windows
2024-06-14 17:57:23 +02:00
Feoramund ca481dc52d Fix displaying error on wrong line with token at EOL
Previously, this would get a token on text like "\n*\n" where `*`
is the token's position, and it would advance off that line.
2024-06-14 11:51:03 -04:00
Franz Höltermann c3302615a3 Merge branch 'master' of https://github.com/FrancisTheCat/Odin 2024-06-14 17:43:09 +02:00
Franz Höltermann 3c3f0f90c2 Fixed WinEventFlags 2024-06-14 17:41:21 +02:00
FrancisTheCat cd5fa8523f Merge branch 'odin-lang:master' into master 2024-06-14 16:34:21 +02:00
Franz Höltermann ec7b77fc0f Improved win32 bindings according to Kelimions suggestions with enums and bitsets 2024-06-14 16:32:41 +02:00
Matias Fernandez 4baf101f15 Handle empty structs in fmt with hash 2024-06-14 05:36:23 -04:00
Jeroen van Rijn ff4787070d Revert "Merge pull request #3744 from Mango0x45/master"
This reverts commit 45044de0b7, reversing
changes made to 20c17ba6f9.
2024-06-14 00:06:55 +02:00
Jeroen van Rijn 54a3cfb540 Merge pull request #3753 from Feoramund/fix-silent-token-fail
Fix compiler silently returning if `parse_packages` fails
2024-06-13 19:33:02 +02:00
Feoramund c560553c21 Fix compiler silently returning if parse_packages fails 2024-06-13 13:01:51 -04:00
Jeroen van Rijn 45044de0b7 Merge pull request #3744 from Mango0x45/master
Pad ‘^~~~^’-style diagnostic ranges properly
2024-06-13 17:34:17 +02:00
Jeroen van Rijn c9c7d3270d Merge branch 'master' of https://github.com/Mango0x45/Odin into pr/3744 2024-06-13 17:27:00 +02:00
Thomas Voss 9f7ac1469f Generate ranges of the correct length 2024-06-13 17:24:56 +02:00
Jeroen van Rijn ca9d1f940d Just change squiggle_extra type to i32. 2024-06-13 17:23:30 +02:00
Thomas Voss 9f190f3937 Generate ranges of the correct length 2024-06-13 17:00:13 +02:00
Thomas Voss 657c516360 Pad ‘^~~~^’-style diagnostic ranges properly 2024-06-13 16:23:34 +02:00
Jeroen van Rijn 20c17ba6f9 Merge pull request #3751 from Kelimion/custom_formatter
Add example of a custom formatter.
2024-06-13 11:42:38 +02:00
Jeroen van Rijn 3d4e23d741 Add example of a custom formatter. 2024-06-13 11:33:37 +02:00
Jeroen van Rijn 50564a301e Update kernel32 struct DCB. 2024-06-13 10:22:44 +02:00
Jeroen van Rijn f8f6f2dada Merge pull request #3749 from Feoramund/fix-w
Improve support for `%w`
2024-06-13 10:07:48 +02:00
korvahkh 104ca2ce22 Merge branch 'odin-lang:master' into fix-omitempty-comma 2024-06-13 01:27:44 +00:00
Feoramund abe5c2ca83 Add test for %w 2024-06-12 21:07:15 -04:00
Feoramund 0c9f487783 Fix and document %w verb for core:fmt 2024-06-12 20:35:50 -04:00
gingerBill a67df07392 Merge pull request #3742 from karl-zylinski/fix-raymath-matrix-to-float
Make rl.MatrixToFloatV do same thing in Odin as in C
2024-06-12 23:15:48 +01:00
Jeroen van Rijn 05972eb26b Merge pull request #3746 from jasonKercher/add-commtimeouts
add COMMTIMEOUTS, get/setCommTimeouts
2024-06-12 21:50:10 +02:00
gingerBill 4ccc473e97 Merge pull request #3745 from karl-zylinski/rlgl-add-missing-types
rlgl: Pull in raylib and expose missing types
2024-06-12 20:48:22 +01:00
gingerBill 90815452ca Merge pull request #3747 from Feoramund/fix-3739
Fix #3739
2024-06-12 20:48:02 +01:00
jasonkercher a804463a57 add COMMTIMEOUTS, get/setCommTimeouts 2024-06-12 15:31:59 -04:00
Feoramund 33270f14a4 Fix #3739 2024-06-12 15:31:43 -04:00
Karl Zylinski c7ea4ec71c rlgl: Pull in raylib and expose missing types, so it is the same as rlgl.h. This makes rlgl less stand-alone, but I left some notes in rlgl.odin how to easily make it stand-alone if one really wants to. 2024-06-12 21:13:12 +02:00
Karl Zylinski d37b5a7b67 Make rl.MatrixToFloatV transpose the matrix before transmuting it to [16]f32, so it does the same thing as the raymath version implemented in C. 2024-06-12 19:54:14 +02:00
Feoramund 1128bd1d7f Use syscall_bsd on NetBSD 2024-06-12 13:34:36 -04:00
Feoramund 20c32c807d Fix info.init_os_version on FreeBSD
It's possible for the return string to be longer than 128 characters
these days, so I've increased it to 1024, same as the other BSDs.

SYSCTL was otherwise erring out due to lack of buffer space.
2024-06-12 13:10:19 -04:00
Feoramund 5b5402fb23 Add intrinsics.syscall_bsd
This is a BSD-style syscall that checks for a high Carry Flag as the
error state. If the CF is high, the boolean return value is false, and
if it is low (no errors) then the boolean return value is true.
2024-06-12 13:07:13 -04:00
Laytan Laats 108b8feb35 Add vendor:wgpu 2024-06-12 17:22:02 +02:00
Jeroen van Rijn 603581aa53 Merge pull request #3738 from Kelimion/xml_attr
Xml attr
2024-06-12 14:06:40 +02:00
gingerBill c5f7788652 Check to see if matrices are exactly the same type 2024-06-12 12:54:57 +01:00
gingerBill f57c03c170 Improve matrix type hinting rules a little 2024-06-12 12:40:29 +01:00
Jeroen van Rijn 2fe961cbcd Fold XML attribute whitespace. 2024-06-12 13:30:00 +02:00
Jeroen van Rijn ebadff555d Update XML reader to normalize whitespace, part 1. 2024-06-12 12:52:48 +02:00
gingerBill cb45aa58fd Merge pull request #3731 from karl-zylinski/rlgl-subpackage
Move rlgl to vendor:raylib/rlgl
2024-06-12 11:51:16 +01:00
gingerBill e87c5bca58 Merge pull request #3737 from karl-zylinski/raylib-shader-location-index-any-int
Made rl.SetShaderValue etc take a c.int like in original raylib
2024-06-11 21:45:40 +01:00
Jeroen van Rijn 0398388a59 Merge pull request #3729 from Feoramund/fix-3460
Fix #3460
2024-06-11 20:32:37 +02:00
Karl Zylinski 1a22f82f94 Changed rl.SetShaderValue etc to take a c.int like in original raylib.h. You should be able to use other values than the ShaderLocationIndex enum, that enum is only for build in things in raylib. Added #any_int on those procs so you can pass both int and also a ShaderLocationIndex. 2024-06-11 18:03:20 +02:00
gingerBill 35a845b93f Fix indentation 2024-06-11 13:12:41 +01:00
gingerBill 3ff8952813 Replace panic(fmt.tprintf( antipattern with fmt.panicf 2024-06-11 13:11:14 +01:00
gingerBill 56c5e93fd1 Merge pull request #3728 from Feoramund/mem-any-int
Use `#any_int` for `reserve_*` and `resize_*` procs
2024-06-11 12:52:50 +01:00
gingerBill 39b166edd3 Merge pull request #3733 from Feoramund/ci-freebsd
Add FreeBSD to the CI
2024-06-11 12:51:42 +01:00
gingerBill eaf1d88287 Merge pull request #3734 from Feoramund/fix-3730
Fix #3730
2024-06-11 12:50:41 +01:00
gingerBill 1dc90103bd Make verification ignorable with a define flag 2024-06-11 12:30:24 +01:00
gingerBill 0b02c67cdf Minor clean up for backend 2024-06-11 12:19:52 +01:00
gingerBill 9b0e87544a Unify LLVMVerifyFunction invocations into on place 2024-06-11 12:07:22 +01:00
Feoramund 61c630bbf8 Fix #3730 2024-06-11 05:23:43 -04:00
Feoramund 0e6bcd0dbb Add FreeBSD to CI 2024-06-11 04:16:42 -04:00
Feoramund fc88de12c2 Implement absolute_path_from_handle for FreeBSD 2024-06-11 04:16:42 -04:00
Feoramund a3da796d54 Fix file_size on FreeBSD
It was using the generic UNIX `fstat` implemented in Odin, which is more
than what is needed here.

This also avoids the issue of needing a proper
`absolute_path_from_handle` implementation for it to work without error.
2024-06-11 03:55:41 -04:00
Feoramund 40e99ebb10 Fix file open O_* flags on FreeBSD 2024-06-11 03:55:41 -04:00
Feoramund c68560c573 Use correct __error link name for FreeBSD 2024-06-11 03:55:41 -04:00
Feoramund 76292c8ed5 Forbid all BSDs from running core:net tests
`net` is not yet implemented on them.
2024-06-11 03:55:41 -04:00
Franz Höltermann c6c00c706a Merge branch 'master' of https://github.com/odin-lang/Odin 2024-06-11 09:37:20 +02:00
Franz Höltermann 6454c6f087 Added some missing functions to core/sys/windows 2024-06-11 09:35:59 +02:00
Karl Zylinski c9e732d141 rlgl.RLGL_VERSION -> rlgl.VERSION 2024-06-11 08:59:30 +02:00
Karl Zylinski d2cd96c3c8 Made rlgl.odin work as a subpackage of raylib. So now you import vendor:raylib/rlgl. Instead of rl.rlBegin(rl.RL_TRIANGLES) you now type rlgl.Begin(rlgl.TRIANGLES). 2024-06-11 08:46:44 +02:00
Karl Zylinski 34af2bb8ad Moved rlgl.odin to subpackage 'raylib/rlgl' 2024-06-11 08:34:45 +02:00
Feoramund bd198aeada Fix #3460 2024-06-10 23:37:54 -04:00
Feoramund 50464bdce3 Use #any_int for reserve_* and resize_* procs
Resolves #3088
2024-06-10 20:51:58 -04:00
gingerBill f1779c85de Fix #3727 2024-06-10 18:50:53 +01:00
gingerBill 903e254e36 Merge pull request #3725 from laytan/add-forced-shutdown-to-test-runner
add forced shutdown to new test runner
2024-06-10 15:08:09 +01:00
gingerBill eef2aef021 Fix #3724 2024-06-10 15:07:45 +01:00
gingerBill c1e81dc14d Fix #3726 2024-06-10 15:05:24 +01:00
gingerBill fa3cae2bb0 Add intrinsics.procedure_of
```odin
foo :: proc(x: $T) { fmt.println(x) }
bar :: intrinsics.procedure_of(foo(int(123))) // parameters are never ran at compile time, similar to `size_of`
bar(333) // prints 333
```
2024-06-10 15:02:34 +01:00
Laytan Laats 71929f737b add forced shutdown to new test runner
Currently, a Ctrl+c starts a graceful shutdown of the tests and runner.
Sometimes tests get stuck and this would never complete.

This simply adds an extra step, if Ctrl+c is given for the second time,
just `os.exit` right away.
2024-06-10 15:35:23 +02:00
gingerBill 1945218f6d Improve parsing for label: #reverse for and label: #partial switch 2024-06-10 14:18:33 +01:00
Jeroen van Rijn bedeaa34d9 Merge pull request #3723 from Feoramund/use-final-march
Use `get_final_microarchitecture()` for `ODIN_MICROARCH_STRING`
2024-06-10 11:40:01 +02:00
Feoramund 71a812e7fe Use get_final_microarchitecture() for ODIN_MICROARCH_STRING 2024-06-10 05:30:16 -04:00
Jeroen van Rijn 741ee00b64 Merge pull request #3721 from Feoramund/add-tuning-consts
Add compilation-related constants
2024-06-10 10:31:12 +02:00
Jeroen van Rijn d3ae70264e Merge pull request #3720 from Feoramund/fix-force-inline-err-typo
Fix typo in a `#force_inline` error message
2024-06-10 10:25:09 +02:00
Jeroen van Rijn c8b376f07f Merge pull request #3719 from Feoramund/fix-orbranch-error
Fix `or_or_<branch>` error message
2024-06-10 10:21:05 +02:00
Feoramund ff7fcb6d38 Add compilation-related constants
`ODIN_VERSION_HASH` is the `git` SHA hash of the commit the Odin
compiler was built with.

`ODIN_MICROARCH_STRING` is the string passed to `-microarch` when
the program was built.

`ODIN_OPTIMIZATION_MODE` is an enum value of which optimization mode was
used to build the program.
2024-06-10 03:47:20 -04:00
Feoramund 45d1328a85 Fix typo in a #force_inline error message 2024-06-10 00:50:16 -04:00
Feoramund 8702bf00d5 Remove _ in Syntax_Error verbose message 2024-06-09 22:47:43 -04:00
Feoramund 9d28f2e18c Fix or_or_<branch> error messages 2024-06-09 22:46:45 -04:00
Jeroen van Rijn 5985c6e3df Merge pull request #3718 from Feoramund/add-missing-big-atoi-alias
Add missing `string_to_int` alias in `core:math/big`
2024-06-10 03:58:56 +02:00
Jeroen van Rijn 82e2d1916f Merge pull request #3717 from Feoramund/big-combo
Add permutation & combination procs to `core:math/big`
2024-06-10 03:47:55 +02:00
Feoramund 70820c2c40 Add missing string_to_int alias in core:math/big 2024-06-09 21:42:31 -04:00
Feoramund fe5c278fca Add test for core:math/big permutation & combination procs 2024-06-09 20:42:09 -04:00
Feoramund f0d65112b8 Rename math/big test package
This is in line with the other tests, and it does not seem to affect
building the library.
2024-06-09 20:42:07 -04:00
Feoramund 5fbd876db1 Add permutation & combination procedures to core:math/big 2024-06-09 20:42:04 -04:00
Jeroen van Rijn 4ea593bde0 Merge pull request #3716 from Feoramund/slice-permute
Add in-place permutation iterator to `core:slice`
2024-06-09 22:42:43 +02:00
Feoramund 047b505836 Add test for slice.permute 2024-06-09 16:15:43 -04:00
Feoramund 1f64d8d5bd Add slice.permute 2024-06-09 16:15:41 -04:00
gingerBill 7c529e990d Add -target:freestanding_arm32 (experimental) 2024-06-09 17:48:46 +01:00
gingerBill 828870004b Change indentation 2024-06-09 17:15:08 +01:00
Jeroen van Rijn 7e60e21934 Merge pull request #3714 from Kelimion/png_cleanup
PNG cleanup
2024-06-09 18:09:17 +02:00
Jeroen van Rijn 5be7d8e32d Clean up PNG code. 2024-06-09 17:59:59 +02:00
Jeroen van Rijn fc2ba81be0 Merge branch 'master' into png_cleanup 2024-06-09 17:13:43 +02:00
gingerBill edcbca51c3 Merge pull request #3684 from odin-lang/tlsf-allocator
`core:mem/tlsf` - "Two-Level Segregated Fit" memory allocator
2024-06-09 15:54:46 +01:00
Jeroen van Rijn 3c7e2659ac Merge branch 'master' into tlsf-allocator 2024-06-09 16:42:23 +02:00
Jeroen van Rijn 6b88d0a820 Use new blend helper 2024-06-09 16:37:27 +02:00
Jeroen van Rijn d2a2c1e74e Image: Add improved blending method and test it. 2024-06-09 16:10:06 +02:00
gingerBill ef7c6b9895 Re Fix #3713 2024-06-09 14:07:07 +01:00
gingerBill c17981ac38 Add edge case to error_operand_no_value 2024-06-09 14:02:01 +01:00
gingerBill 4b52f7fe2b Fix #3713 2024-06-09 13:35:22 +01:00
gingerBill 8fcfd8c506 Fix sign flag 2024-06-09 13:21:22 +01:00
gingerBill e0d0dc704c Make f32(u8) etc do an immediate cast to f32(u32(u8)) in code generation 2024-06-09 13:20:48 +01:00
Jeroen van Rijn 14f08ff02b Merge pull request #3712 from laytan/fix-large-ints-amd64sysv-abi
fix large ints amd64 sysv abi
2024-06-09 11:01:31 +02:00
Jeroen van Rijn 5d5ef78de9 Merge pull request #3711 from laytan/fix-swizzle-crash
fix swizzle crash due to wrong alignment
2024-06-09 11:00:15 +02:00
Jeroen van Rijn e595bdc805 Merge pull request #3710 from laytan/fix-unreachable-with-complex-param-or-return-inits
fix unreachable hit when param and/or return have complex inits
2024-06-09 10:59:26 +02:00
Laytan Laats 6d862cc4e5 fix unreachable hit when param and/or return have complex inits
Fixes #3630
2024-06-09 04:43:19 +02:00
Laytan Laats 3628154849 fix swizzle crash due to wrong alignment
Fixes #3691
2024-06-09 03:33:23 +02:00
Laytan Laats 9ad9236c3b fix large ints amd64 sysv abi
Fixes #3707
2024-06-09 02:47:05 +02:00
Jeroen van Rijn 0d698c7b53 Merge pull request #3709 from Feoramund/stop-low-swizzle
Prevent panic when `swizzle` called with < 2 indices
2024-06-08 23:14:55 +02:00
Feoramund 49f147cc86 Prevent panic when swizzle called with < 2 indices
The requirement for at least 2 indices has been sourced from
`lb_addr_swizzle` in `llvm_backend_general.cpp`, where there is an
assert to ensure the swizzle_count is `1 < n <= 4`.
2024-06-08 17:05:00 -04:00
Jeroen van Rijn e5bf6fd3aa Merge pull request #3708 from Feoramund/ignore-bmp-assets
Add `*.bmp` to core tests `.gitignore`
2024-06-08 22:45:08 +02:00
Feoramund 5c8f78a25a Add *.bmp to core tests .gitignore 2024-06-08 16:43:45 -04:00
Jeroen van Rijn 1f4cfd52fd Merge pull request #3706 from leidegre/os_read_dir_windows
Fixed error handling in read_dir on Windows
2024-06-08 17:14:49 +02:00
gingerBill be0774acc8 Add error message on return a constant slice value from a procedure 2024-06-08 16:07:28 +01:00
Jeroen van Rijn de7d3e2487 Update dir_windows.odin 2024-06-08 17:00:38 +02:00
gingerBill 7e994b6d21 Remove empty line preventing a suggestion from happening 2024-06-08 15:42:19 +01:00
John Leidegren 35e57fdef8 Fixed error handling in read_dir on Windows 2024-06-08 15:58:34 +02:00
gingerBill 371749d474 Make certain procedures "contextless" for microui 2024-06-08 14:48:04 +01:00
Jeroen van Rijn 575e5a255b Merge pull request #3704 from Feoramund/fix-fmt-p-verb
Fix `%p` pointers not printing leading `0x`
2024-06-07 23:00:29 +02:00
Jeroen van Rijn 323a6e0728 Nuke ancient comment. 2024-06-07 22:19:01 +02:00
Feoramund affd48c791 Add #caller_location to check in fmt tests
This makes it much easier to track down which line failed.
2024-06-07 16:08:37 -04:00
Feoramund b65589d036 Add %p tests for fmt 2024-06-07 16:07:11 -04:00
Feoramund 61c481bd81 Fix %p pointers not printing leading 0x 2024-06-07 15:27:43 -04:00
Jeroen van Rijn 96c06185dd Merge pull request #3701 from laytan/fix-thread-deadlock
Fix thread deadlock
2024-06-07 20:39:40 +02:00
Laytan Laats 00dfff7ee0 core/thread: fix a deadlock situation on unix 2024-06-07 20:28:09 +02:00
Laytan Laats b35e72c82b core/sync: fix wrong timeout calculation, time.Duration is ns already 2024-06-07 20:27:33 +02:00
Feoramund edb685f04b Add package core:flags 2024-06-07 13:16:13 -04:00
Jeroen van Rijn 7c977bb859 Merge pull request #3699 from laytan/macos-14.5
add MacOS 14.5 to 'core:sys/info' and 'odin report'
2024-06-07 17:44:09 +02:00
Laytan Laats 072825ac5a add MacOS 14.5 to 'core:sys/info' and 'odin report' 2024-06-07 17:42:41 +02:00
Jeroen van Rijn 4c45f7d3ae Merge pull request #3698 from laytan/utilize-odin-test-instead-of-scrips-to-run-tests
Utilise `odin test` instead of scripts to run tests
2024-06-07 17:36:23 +02:00
Jeroen van Rijn 11137cfec5 Merge pull request #3697 from laytan/add-error-message-for-unknown-test-log-level
add error message for unknown test log level
2024-06-07 17:11:48 +02:00
Laytan Laats e627fcb0e6 fix not printing Error: when terminal has no color support 2024-06-07 16:58:23 +02:00
Laytan Laats 29250f2657 fix regression in test_issue_2395 2024-06-07 16:33:38 +02:00
Laytan Laats 9122c20d4b update actions/checkout 2024-06-07 15:57:09 +02:00
Laytan Laats 0a528777e8 utilize odin test -all-packages instead of (make/bat) scripts for running tests 2024-06-07 15:50:00 +02:00
Laytan Laats f2be35f1f0 remove caught unused define 2024-06-07 15:22:14 +02:00
Laytan Laats ee93d7c05e add error message for unknown test log level
It would previously just be a compilation error about a missing
return statement.
2024-06-07 15:13:09 +02:00
Laytan Laats 315695b4f8 collect and show docs of defineables 2024-06-07 15:08:41 +02:00
gingerBill 68781f8dd3 Remove unnecessary Wait_Signal checks 2024-06-07 00:11:00 +01:00
gingerBill 1d99bc0f87 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-06-06 23:55:54 +01:00
gingerBill 7044a7d776 Try to fix a possible race condition with polymorphic record parameters 2024-06-06 23:55:48 +01:00
Jeroen van Rijn 4b6fe2baa7 Merge pull request #3696 from Kelimion/bmp_save
BMP save
2024-06-07 00:23:55 +02:00
Jeroen van Rijn ed060819f3 Test roundtripping BMP 2024-06-07 00:14:15 +02:00
Jeroen van Rijn 566119ff83 Add saving of 24 and 32-bit images to BMP format. 2024-06-06 23:53:15 +02:00
gingerBill 08612423b9 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-06-06 21:03:44 +01:00
gingerBill ff37a7435c Add parentheses around the foreign import paths 2024-06-06 21:03:35 +01:00
Laytan Laats fa08690686 explicit integer conversions 2024-06-06 19:49:51 +02:00
Laytan Laats b818a77131 check if -define is actually used 2024-06-06 19:46:01 +02:00
Laytan Laats 9a95049393 -show-defineables and -export-defineables 2024-06-06 19:44:54 +02:00
Jeroen van Rijn b196b84ef2 Merge pull request #3695 from laytan/fix-linking-on-weird-linuxes
fix linking on weird linuxes
2024-06-06 19:37:31 +02:00
Laytan e2eb3cdd8a fix linking on weird linuxes 2024-06-06 19:23:36 +02:00
gingerBill d0dbe9a1bd Merge branch 'master' of https://github.com/odin-lang/Odin 2024-06-06 17:59:19 +01:00
gingerBill 039bb8794a Improve matrix_align_of logic when it has invalid inputs. 2024-06-06 17:59:12 +01:00
gingerBill f010544eff Merge pull request #3609 from Kelimion/bmp
Add core:image/bmp
2024-06-06 16:29:35 +01:00
Jeroen van Rijn 4a290f47ad Re-add BMP test suite download. 2024-06-06 16:55:03 +02:00
Jeroen van Rijn 678fdae966 Rebased. 2024-06-06 16:32:18 +02:00
gingerBill 3a9b86628a Add @(rodata) and @(static, rodata) where appropriate 2024-06-06 15:23:52 +01:00
gingerBill bea47db495 Allow @(rodata) on @(static) variables 2024-06-06 15:20:47 +01:00
gingerBill 9ef43fc782 Add @(rodata) 2024-06-06 15:16:34 +01:00
Jeroen van Rijn 28ea9425fd Add core:mem tests. 2024-06-06 14:59:19 +02:00
Jeroen van Rijn 72f6b5479d Fix fls_uint 2024-06-06 14:42:19 +02:00
Jeroen van Rijn 0b6d73c86e Add original LICENSE 2024-06-06 14:29:28 +02:00
gingerBill 971229fe66 Fix #3686 2024-06-06 13:27:55 +01:00
gingerBill c4ef8e7f6c Merge branch 'master' of https://github.com/odin-lang/Odin 2024-06-06 13:02:16 +01:00
gingerBill 155516b897 Fix -ignore-warnings 2024-06-06 13:02:08 +01:00
Jeroen van Rijn 9b66b0c8e6 Merge pull request #3694 from rope-hmg/master
Updated SDL_CreateTexture to take `PixelFormatEnum` instead of `u32`
2024-06-06 13:03:16 +02:00
Hector 483015fe57 Updated SDL_CreateTexture to take PixelFormatEnum instead of u32 2024-06-06 11:47:59 +01:00
gingerBill a73741d3b7 Merge pull request #3690 from laytan/orcas
Runtime support for orca
2024-06-06 11:03:51 +01:00
Jeroen van Rijn fb2849e02e Merge pull request #3693 from colrdavidson/x11-title-change-support
Add support functions for title changes, cursor config, and dnd
2024-06-06 11:48:16 +02:00
Colin Davidson 70592630a4 add support for title changes, cursor config, and dnd 2024-06-06 02:39:48 -07:00
Jeroen van Rijn fcfc1cb97f Nightly ZIP level 9 2024-06-05 22:26:03 +02:00
Jeroen van Rijn cdd90a9a0b Merge pull request #3688 from Kelimion/nightly
Rewrite upload_b2 nightly action against B2 SDK
2024-06-05 21:11:25 +02:00
Jeroen van Rijn 460ffe1aee Rewrite upload_b2 nightly action against B2 SDK 2024-06-05 21:04:35 +02:00
laytan 08382cb05d orca windows 2024-06-05 19:28:40 +02:00
Laytan Laats 8455e159f5 improve orca target 2024-06-05 20:57:39 +02:00
gingerBill 76d3be7912 Merge pull request #3683 from andreas-jonsson/test-make-cleanup
Minor cleanup of makefiles and scripts
2024-06-05 15:44:27 +01:00
gingerBill 97f0f72080 Merge pull request #3685 from markersniffen/sniffen
D3D11.odin - fix VIDEO_SUPPORT flag
2024-06-05 15:43:55 +01:00
gingerBill a667878d23 Merge branch 'master' of https://github.com/odin-lang/Odin 2024-06-05 15:43:21 +01:00
gingerBill cbabcb0907 Fix #3682 2024-06-05 15:43:15 +01:00
gingerBill fac9ce5d83 Change to init from create 2024-06-05 15:30:35 +01:00
gingerBill d3342c2381 core:mem/tlsf - "Two-Level Segregated Fit" memory allocator 2024-06-05 15:15:39 +01:00
Mark Sniffen cd4375061c D3D11 - fixed VIDEO_SUPPORT flag 2024-06-05 10:11:18 -04:00
Andreas T Jonsson f0e98372fb Minor cleanup of makefiles and scripts 2024-06-05 15:37:02 +02:00
gingerBill a747e47582 Merge pull request #3675 from Feoramund/fix-partial-infinity
Fix partial parsing of `infinity`
2024-06-05 12:48:44 +01:00
gingerBill adcda88501 Merge pull request #3680 from andreas-jonsson/netbsd-arm64
ARM64 support for NetBSD
2024-06-05 12:47:29 +01:00
gingerBill e7190aab41 Merge pull request #3681 from andreas-jonsson/netbsd-lwp-self
Proper thread identification on NetBSD
2024-06-05 12:47:23 +01:00
Andreas T Jonsson 929437c7bc Fixed broken import 2024-06-05 11:17:13 +02:00
Andreas T Jonsson ed6667ebf2 Propper thread identification on NetBSD 2024-06-05 11:06:14 +02:00
Andreas T Jonsson 58f07698e8 Added arm64 support for NetBSD 2024-06-05 10:18:47 +02:00
Jeroen van Rijn c406bbb6e3 Merge pull request #3679 from Feoramund/fix-typos
Fix some typos
2024-06-05 09:30:10 +02:00
Feoramund b2f9f0af68 Fix some typos 2024-06-04 19:01:30 -04:00
Feoramund 25feff3eb4 Permit parsing of incomplete infinity but do not return true
To clarify, `parse_f64` will indeed take `infi` to mean `+Inf` and
return that as the value, but it will not return `ok = true`. It treats
it as `inf` followed by any other trailing character.

`parse_f64_prefix` is the lenient one which will return true so long as
it finds some meaningful value.
2024-06-04 18:55:13 -04:00
Feoramund 1fc6ff91b2 Add test for infinity with trailing characters 2024-06-04 18:55:13 -04:00
gingerBill 2c580aa6fb Merge pull request #3676 from laytan/fix-passing-pointer-to-constant-to-non-odin-cc
fix passing pointer to constant in non-odin cc
2024-06-04 23:12:46 +01:00
Jeroen van Rijn f0a6fb4057 Merge pull request #3678 from Feoramund/fix-p-f64-p-doc
Strike incorrect note from `parse_f64_prefix` doc
2024-06-05 00:02:34 +02:00
Jeroen van Rijn 448c0d8dd3 Merge pull request #3677 from Feoramund/parse-compquat
Add `parse_complex/quaternion*` to `core:strconv`
2024-06-04 23:58:43 +02:00
Feoramund b4cfae222c Strike incorrect note from parse_f64_prefix doc 2024-06-04 17:50:19 -04:00
Feoramund 265e6aa781 Add parse_complex/quaternion* to core:strconv 2024-06-04 17:12:55 -04:00
Jeroen van Rijn 9d234998c0 Update create_nightly_json 2024-06-04 22:44:49 +02:00
Jeroen van Rijn c2ddf4266f Update delete_old_binaries.py 2024-06-04 22:26:51 +02:00
gingerBill a0b1b8d1c3 Merge pull request #3673 from laytan/implement-foreign-import-improvements-on-vendor
Implement `#exists(path)` and use it to provide good errors for common missing vendor libraries
2024-06-04 19:57:09 +01:00
Laytan Laats dbaf1a1ce0 compile stb_vorbis 2024-06-04 20:26:21 +02:00
Feoramund c656a9e4cd Fix and subsume test_issue_2087 into strconv test suite
The full "infinity" strings were expected to be partial consumes, but
this is not the case. That has been fixed and the relevant extra tests
from that file have been added to this test suite.

Fixes #2670
2024-06-04 14:23:48 -04:00
Laytan Laats cd99625dd3 ci: compile needed libraries 2024-06-04 20:21:52 +02:00
Feoramund 7d670f6562 Add initial test suite for core:strconv 2024-06-04 14:20:31 -04:00
laytan 2a526058b3 fix passing pointer to constant in non-odin cc 2024-06-04 20:15:47 +02:00
Laytan Laats 3e159736cd use #exists to provide good errors for common missing libraries 2024-06-04 20:13:51 +02:00
Feoramund d33668fa91 Fix partial parsing of "infinity" in parse_f64_prefix
It was previously reporting an invalid number of characters parsed for
any string other than "inf", "+inf", or "-inf".
2024-06-04 13:24:46 -04:00
Laytan Laats b47a15733d implement #exists(path) 2024-06-04 19:06:13 +02:00
Laytan Laats 4f5b2bd127 fix crash when you have 2 #load_directory calls with the same path 2024-06-04 19:01:50 +02:00
gingerBill 3b7100f8e5 raygui: change c.int to their correct enum types 2024-06-04 16:17:49 +01:00
gingerBill 6b386631dd Improve error message suggestion for passing enums to integers 2024-06-04 16:16:27 +01:00
gingerBill 11b1a48bf0 Fix extra_linker_flags for raylib on windows 2024-06-04 15:54:57 +01:00
gingerBill 72ce111a95 Keep -vet happy 2024-06-04 15:51:19 +01:00
gingerBill 606608c02b Utilize foreign import constant strings for foreign imports 2024-06-04 15:49:17 +01:00
gingerBill eacf2918ad Merge pull request #3469 from hodgka/raylib_resizable_doc_patch
Fixed typo in raylib documentation
2024-06-04 15:41:15 +01:00
gingerBill afcccfdec5 Merge pull request #3615 from puzzleddev/export-dependencies
Add build flag to export dependencies.
2024-06-04 15:40:54 +01:00
gingerBill 60a064984d Merge pull request #3670 from colrdavidson/x11-tweaks
Correct X11 prefix issues
2024-06-04 15:02:03 +01:00
gingerBill 0504c12f04 Merge pull request #3672 from laytan/fix-old-llvm-debug-info
fix debug info IR error on LLVM < 13
2024-06-04 15:01:30 +01:00
Laytan Laats 7d29389834 fix debug info IR error on LLVM < 13 2024-06-04 14:41:42 +02:00
Colin Davidson 52ba3357ee oops, missed shuffling a few x-funcs 2024-06-04 04:48:24 -07:00
Jeroen van Rijn 78a67ef31e Merge pull request #3667 from Feoramund/fix-fmt-compquat-sign
Fix printing of duplicate signs in complex and quaternion types
2024-06-04 13:32:13 +02:00
Jeroen van Rijn f9d59ef6d4 Merge branch 'master' into fix-fmt-compquat-sign 2024-06-04 13:25:57 +02:00
Jeroen van Rijn c3b94b9e1d Merge pull request #3646 from Feoramund/multi-test
Refactor the test runner
2024-06-04 13:23:52 +02:00
Feoramund eb93779f63 Fix duplicate sign printing of complex and quaternion types
Negative zero wasn't being detected (so it would appear as `+-0`), and
`+Inf` was appearing as `++Inf` when imaginary.
2024-06-03 19:51:17 -04:00
Feoramund d2a9122176 Add fmt tests for printing complex and quaternion types 2024-06-03 19:48:09 -04:00
Feoramund 0ff130d82b Fix ad hoc printf in test runner signal handler 2024-06-02 23:36:04 -04:00
Feoramund fa29974dab Use Warning log level for reporting memory leaks
Works well with `-define:ODIN_TEST_LOG_LEVEL=warning`.
2024-06-02 23:21:44 -04:00
Feoramund 7764ab2ab0 Prevent test runner deadlock on NetBSD
Add `pthread_testcancel` to `core:sys/unix`
2024-06-02 21:27:36 -04:00
Feoramund 6a5633df2d Fix wrong PTHREAD_CANCEL_ASYNCHRONOUS on FreeBSD and OpenBSD
The test runner was deadlocking when a test raised a signal on FreeBSD.

This is untested on OpenBSD, but I have referenced this file:
https://github.com/openbsd/src/blob/master/include/pthread.h
2024-06-02 20:41:25 -04:00
Feoramund c8539fe411 Revert "Disable NetBSD tests until 'undefined reference to stdout' is solved."
This reverts commit 21a1ddfbae.
2024-06-02 19:29:27 -04:00
Feoramund ac9484206b Fix STDIN, STDOUT, STDERR handles for BSDs
Tested on FreeBSD 14.0 and NetBSD 10.0

OpenBSD is untested, but link names were sourced from:
https://github.com/openbsd/src/blob/master/include/stdio.h

According to this, OpenBSD shares the same layout as NetBSD.

FreeBSD has the same as Darwin in this regard.
2024-06-02 19:29:27 -04:00
Feoramund f77ce359ce Be pedantic about not overwriting Odin errors
I was encountering bounds-check error messages being overwritten during
a test, if the test failed for another reason and sent a log message.

The original intent of having this check inside of the above `if` block
was that if a test sent an error message, then it was assumed an
overwrite would be safe, but it's completely possible for a test to fail
for a legitimate reason, then do an unrelated bounds check somewhere
else that would be buried under the animation.

This change will make sure that, no matter what, the progress display
will not trigger a clear if a signal was raised. There's still no
guarantee that bounds-check messages will be printed properly, and it's
best to redirect STDERR.

The only way that can be fixed is if they get a similar hook to
`context.assertion_failure_proc`.
2024-06-02 15:51:26 -04:00
Feoramund 3f1249c27e Tell user about ODIN_TEST_RANDOM_SEED option 2024-06-02 15:34:13 -04:00
Feoramund 8d8c42e962 Use T.seed in tests where applicable 2024-06-02 15:30:23 -04:00
Jeroen van Rijn 60d0c03134 Strip old test runner back out of internal, issues and vendor 2024-06-02 21:15:25 +02:00
Jeroen van Rijn 9d8d864400 Plug leak in AES tests. 2024-06-02 21:12:24 +02:00
Jeroen van Rijn 21a1ddfbae Disable NetBSD tests until 'undefined reference to stdout' is solved. 2024-06-02 21:03:22 +02:00
Feoramund 890fe07c6e Disable FANCY_OUTPUT in Odin test scripts
This should tidy up the CI output logs a bit.
2024-06-02 14:54:32 -04:00
Feoramund d581dbbec5 Keep test runner main thread from using 100% of a CPU core 2024-06-02 14:54:32 -04:00
Feoramund 5e3e958574 Add -define:ODIN_TEST_LOG_LEVEL to set lowest log level 2024-06-02 14:54:32 -04:00
Feoramund 6a9203328b Log thread count at test run start
Provides a helpful info message about the option to change how many
threads are used per run.
2024-06-02 14:54:32 -04:00
Feoramund 5db65aa796 Make it easier to learn about ODIN_TEST_CLIPBOARD 2024-06-02 14:54:32 -04:00
Feoramund cb00b8022b Add note about SIGSEGV edge case on UNIX-likes 2024-06-02 14:54:32 -04:00
Feoramund 4875f745c8 Remove Windows test runner in favor of libc implementation 2024-06-02 14:54:32 -04:00
Feoramund ccdbd4b6ce Simplify casts in mem.Rollback_Stack procs 2024-06-02 14:54:32 -04:00
Feoramund 9dcf345795 Set thread pool is_running to false on shutdown 2024-06-02 14:54:32 -04:00
Feoramund 21064fbb60 Clear thread pool task data on restart 2024-06-02 14:54:32 -04:00
Feoramund 45fa9d8148 Expand documentation comment for ODIN_TEST_NAMES 2024-06-02 14:54:32 -04:00
Feoramund cb8faf5b74 Remove -test-name in favor of test runner option
`-define:ODIN_TEST_NAMES=...` is capable of selecting test by package
and name or name only, with the ability to access packages included by
`-all-packages`.
2024-06-02 14:54:32 -04:00
Feoramund 6a5d51f0d6 Use more concise way of satisfying -vet 2024-06-02 14:54:31 -04:00
Feoramund 433ca538bf Be specific about platforms not implementing test runner signal handler 2024-06-02 14:54:31 -04:00
Feoramund d1723664a7 Catch SIGILL, SIGFPE, SIGSEGV in the test runner 2024-06-02 14:54:31 -04:00
Feoramund fb37572c4c Rename signal_handler.odin to signal_handler_libc.odin 2024-06-02 14:54:31 -04:00
Jeroen van Rijn 6050bc3bf6 Add missing benchmarks build.bat. 2024-06-02 14:54:31 -04:00
Jeroen van Rijn 306169699c Update ci.yml 2024-06-02 14:54:31 -04:00
Jeroen van Rijn 3354212f8e Update ci.yml
Disable benchmark on Windows for a moment.
2024-06-02 14:54:31 -04:00
Jeroen van Rijn 54dae06ad1 Update CI 2024-06-02 14:54:31 -04:00
Jeroen van Rijn 8d93379e29 Factor benchmarks out into tests\benchmark\<pkg> 2024-06-02 14:54:29 -04:00
Jeroen van Rijn 62b7d8de97 Port tests\core\net 2024-06-02 14:47:09 -04:00
Jeroen van Rijn dacb0f7786 Port tests\core\thread 2024-06-02 14:47:09 -04:00
Jeroen van Rijn 5b1ffba915 Port testing\core\time 2024-06-02 14:47:09 -04:00
Jeroen van Rijn a406ff7063 Port tests\core\strings 2024-06-02 14:47:09 -04:00
Jeroen van Rijn 9ba02e888d Port tests\core\slice 2024-06-02 14:47:09 -04:00
Jeroen van Rijn ed0384c102 Port tests\core\runtime 2024-06-02 14:47:09 -04:00
Jeroen van Rijn 80b115748f Port tests\core\reflect 2024-06-02 14:47:09 -04:00
Jeroen van Rijn 9829a02571 Port tests\core\odin 2024-06-02 14:47:09 -04:00
Jeroen van Rijn 8383a45b62 Port tests\core\text\match 2024-06-02 14:47:09 -04:00
Jeroen van Rijn b0faab29e0 Port tests\core\math, math\linalg\glsl and math\noise 2024-06-02 14:47:09 -04:00
Jeroen van Rijn d7bfbe0552 Port testing\core\text\i18n 2024-06-02 14:47:08 -04:00
Jeroen van Rijn 39fd73fe17 Port testing\core\hash 2024-06-02 14:47:08 -04:00
Jeroen van Rijn 6f7c5a7577 Port tests\core\fmt 2024-06-02 14:47:08 -04:00
Jeroen van Rijn d334b8c72a Port tests\core\path\filepath 2024-06-02 14:47:08 -04:00
Jeroen van Rijn 9d0f4833bf Port tests\core\encoding\xml
Made them run in parallel as well.
2024-06-02 14:47:08 -04:00
Jeroen van Rijn 6641a6f6c9 Port tests\core\encoding\varint 2024-06-02 14:47:08 -04:00
Jeroen van Rijn 601df0e8f7 Port tests\core\encoding\json 2024-06-02 14:47:08 -04:00
Jeroen van Rijn 1b32e27aa4 Port tests\core\encoding\hxa
And fix a few leaks in `core:encoding/hxa` while at it.
2024-06-02 14:47:08 -04:00
Jeroen van Rijn 3404dea8ac Port tests\encoding\hex 2024-06-02 14:47:08 -04:00
Jeroen van Rijn 40b20fb473 Port tests\core\c\libc 2024-06-02 14:47:08 -04:00
Feoramund b74b956fda Remove unneeded import 2024-06-02 14:47:08 -04:00
Feoramund 1f6a6f2cd3 Support deterministic random seeding of tests
Add a new option `ODIN_TEST_RANDOM_SEED` which is picked from the cycle
counter at startup, if it's not specified by the user.

This number is sent to every test in the `T` struct and reset every test
(just in case).
2024-06-02 14:47:07 -04:00
Jeroen van Rijn a27b167218 Update tests\core\encoding\cbor to use new test runner.
It was leaky and required a substantial number of `loc := #caller_location` additions to parts of the core library to make it easier to track down how and where it leaked.

The tests now run fine multi-threaded.
2024-06-02 14:47:07 -04:00
Jeroen van Rijn 6a1649d8aa Update using new defaults for memory + reporting width 2024-06-02 14:47:07 -04:00
Feoramund 84ad71fdb3 Support ODIN_TEST_PROGRESS_WIDTH=0
This will automatically calculate how wide the progress bars should be
based on the package with the greatest number of tests.

The progress width is now capped to 100.
2024-06-02 14:47:07 -04:00
Feoramund 49fa66370f Report test memory usage only if there's an issue
Adds new option `ODIN_TEST_ALWAYS_REPORT_MEMORY`, for when you always
want to see the memory usage report.
2024-06-02 14:47:07 -04:00
Feoramund e1a3c0e21d Track memory in the test runner by default 2024-06-02 14:47:07 -04:00
Feoramund bf42e39b1c Be specific about int size for Rollback_Stack asserts
This should fix tests failing on 32-bit platforms.
2024-06-02 14:47:07 -04:00
Jeroen van Rijn c531427ee5 Update -define for crypto 2024-06-02 14:47:07 -04:00
Feoramund b7e1ae7073 Change test runner options to SCREAMING_SNAKE_CASE
This commit also changes the name of `test_select` to `ODIN_TEST_NAMES`,
to better conform with the already-existing `-test-name:<name>` option.
2024-06-02 14:47:07 -04:00
Feoramund e11f3d2520 Fix missing - for define in tests/core/build.bat 2024-06-02 14:47:07 -04:00
Feoramund dcfda195d2 Send terminal control code to STDOUT instead
`STDERR` might be redirected, and this code signals to the terminal to
show the cursor again. Otherwise, the cursor will be invisible.
2024-06-02 14:47:07 -04:00
Feoramund a1c5bebac7 Fix ANSI redraw eating last log line 2024-06-02 14:47:07 -04:00
Feoramund 89d8df28be Combine multi-line attributes onto one line 2024-06-02 14:47:07 -04:00
Feoramund dffc3af86c Remove safe_heap_allocator from test runner
I was under the impression that the default `context.allocator` was not
thread-safe, but I've been told that this is not the case.
2024-06-02 14:47:06 -04:00
Feoramund eadfbb1318 Forbid singleton allocations from shrinking their block offset 2024-06-02 14:47:06 -04:00
Feoramund 1afc235359 Use plain sort for internal_tests 2024-06-02 14:47:06 -04:00
Feoramund 09ef08f035 Add more sanity checking to mem.Rollback_Stack 2024-06-02 14:47:06 -04:00
Feoramund 0f675fa436 Use uintptr where applicable in mem.Rollback_Stack 2024-06-02 14:47:06 -04:00
Feoramund 568b746c98 Fix indentation 2024-06-02 14:47:06 -04:00
Jeroen van Rijn a0b2ea6d6e Update tests\core\crypto 2024-06-02 14:46:59 -04:00
Jeroen van Rijn a463e282db Update core:container tests 2024-06-02 14:34:31 -04:00
Jeroen van Rijn e3181c13c6 Update core:compress tests 2024-06-02 14:34:31 -04:00
Jeroen van Rijn 22c092f846 Delete duplicated flag. 2024-06-02 14:34:31 -04:00
Jeroen van Rijn eb3d6d7d75 Update core:image tests to use new runner. 2024-06-02 14:34:31 -04:00
Feoramund 852f694bee Get tests passing again
`T` no longer has a writer assigned to it.

`test_core_cbor.odin` has global state and is run with `odin test`,
so I've set it to use only one thread.
2024-06-02 14:34:31 -04:00
Feoramund 87ea4a2652 Temporarily disable Windows-specific test runner
I do not have a Windows machine to test the refactored test runner, and
I am unsure if it would even run correctly on Windows without this
disabled.
2024-06-02 14:34:31 -04:00
Feoramund d03024088a Remove unneeded code 2024-06-02 14:34:31 -04:00
Feoramund b6c4dfb68d Refactor the test runner
Changes

- Support multi-threaded testing.
- Support `set_fail_timeout` on all platforms.
- Display an animated progress report.
- Setup all tests with a context logger.
- Give all tests their own separate custom allocators.
- Support tracking test memory usage.
- Display a summary of the failed tests at the end.
- Let users select only specific tests to run.
- Support copying failed tests to the clipboard to run again.
- Support catching SIGINT (CTRL-C) to cancel early.
- Record context in cleanup procs.
- Write all log messages to STDERR for easy redirection.
- Possibly more I've forgotten.

New Options

- `-define:test_threads=N`: Specify thread count.
- `-define:test_thread_memory=B`: Specify initial memory block size in bytes to each thread.
- `-define:test_track_memory=true`: Track the memory usage of individual tests.
- `-define:test_fancy=false`: Disable animated progress report.
- `-define:test_select=package.test_name,...`: Run only select tests.
- `-define:test_clipboard=true`: Copy names of failed tests to the clipboard.
- `-define:test_progress_width=24`: Change the width of the animated progress bars.
2024-06-02 14:34:31 -04:00
Feoramund 95c2e020ff Share libc signal definitions with more platforms
I confirmed that these 3 platforms share the same signal definitions
from these sources.

Haiku: https://github.com/haiku/haiku/blob/master/headers/posix/signal.h
OpenBSD: https://github.com/openbsd/src/blob/master/sys/sys/signal.h
NetBSD: http://fxr.watson.org/fxr/source/sys/signal.h?v=NETBSD
2024-06-02 14:34:30 -04:00
Feoramund 50dffaf131 Add mem.Rollback_Stack 2024-06-02 14:34:30 -04:00
Feoramund fc4f6b87bb Add core:encoding/ansi package 2024-06-02 14:34:30 -04:00
Feoramund 8137b9dd75 Add mem.tracking_allocator_reset 2024-06-02 14:34:30 -04:00
Feoramund 558c330028 Add task-stopping functionality to thread.Pool 2024-06-02 14:34:30 -04:00
Feoramund 1875e7c36a Make log.do_*_header argument orders consistent 2024-06-02 14:34:30 -04:00
Feoramund 7d4da6eaa7 Fix trailing space with only .Date log option 2024-06-02 14:34:30 -04:00
Feoramund 8ae375dbff Move log timestamping out to do_time_header proc 2024-06-02 14:34:30 -04:00
korvahkh a7a6ff8c69 encoding/json: Fix struct marshal() emitting comma after omitted field
Previously:
```odin
package main

import "core:fmt"
import "core:encoding/json"

T :: struct {
	a: string `json:",omitempty"`,
	b: string,
}

main :: proc() {
	data, _ := json.marshal(T{b = "foo"})
	fmt.println(string(data))
}
```
would output:
```
{,"b":"foo"}
```

which is not correct.

Also cleaned up some whitespace while I'm at it.
2024-05-27 16:59:46 -05:00
tim4242 0cba33075f Add the option to export dependencies as JSON. 2024-05-24 13:44:38 +02:00
tim4242 8664b88c8f Improved depdendency formatting to support paths containing spaces 2024-05-24 01:20:45 +02:00
tim4242 7934e92d14 Initial dependency file generation 2024-05-24 01:04:41 +02:00
Timo 2918baa3e8 Update builder.odin
Modify documentation-text for procedure write_f64 :

change type f32 to f64
2024-04-29 20:22:53 +02:00
Artsiom Babukh 805bb69c6c Fix typo in core_builtin 2024-04-28 15:27:34 +00:00
alec hodgkinson 3b632b4d90 Fixed typo in raylib documentation 2024-04-22 15:13:52 -07:00
Arseniy Poroshin 77a0e50298 fix comments in core:container/queue. 2024-03-26 22:39:43 +00:00
421 changed files with 44142 additions and 15237 deletions
+112 -133
View File
@@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Build, Check, and Test
timeout-minutes: 25
timeout-minutes: 15
uses: vmactions/netbsd-vm@v1
with:
release: "10.0"
@@ -24,157 +24,141 @@ jobs:
/usr/sbin/pkg_add https://github.com/andreas-jonsson/llvm17-netbsd-bin/releases/download/pkgsrc-current/llvm-17.0.6.tgz
/usr/sbin/pkg_add https://github.com/andreas-jonsson/llvm17-netbsd-bin/releases/download/pkgsrc-current/clang-17.0.6.tgz
ln -s /usr/pkg/bin/python3.11 /usr/bin/python3
ln -s /usr/pkg/bin/bash /bin/bash
run: |
git config --global --add safe.directory $(pwd)
gmake release
./odin version
./odin report
./odin check examples/all -vet -strict-style -target:netbsd_amd64
(cd tests/core; gmake all_bsd)
(cd tests/internal; gmake all_bsd)
gmake -C vendor/stb/src
gmake -C vendor/cgltf/src
gmake -C vendor/miniaudio/src
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_amd64
./odin check examples/all -vet -strict-style -disallow-do -target:netbsd_arm64
./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false
./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
(cd tests/issues; ./run.sh)
build_linux:
name: Ubuntu Build, Check, and Test
build_freebsd:
name: FreeBSD Build, Check, and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Download LLVM
- uses: actions/checkout@v4
- name: Build, Check, and Test
timeout-minutes: 15
uses: vmactions/freebsd-vm@v1
with:
usesh: true
copyback: false
prepare: |
pkg install -y gmake git bash python3 libxml2 llvm17
run: |
# `set -e` is needed for test failures to register. https://github.com/vmactions/freebsd-vm/issues/72
set -e -x
git config --global --add safe.directory $(pwd)
gmake release
./odin version
./odin report
gmake -C vendor/stb/src
gmake -C vendor/cgltf/src
gmake -C vendor/miniaudio/src
./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false
./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
(cd tests/issues; ./run.sh)
ci:
strategy:
fail-fast: false
matrix:
# MacOS 13 runs on Intel, 14 runs on ARM
os: [ubuntu-latest, macos-13, macos-14]
runs-on: ${{ matrix.os }}
name: ${{ matrix.os == 'macos-14' && 'MacOS ARM' || (matrix.os == 'macos-13' && 'MacOS Intel' || 'Ubuntu') }} Build, Check, and Test
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Download LLVM (Linux)
if: matrix.os == 'ubuntu-latest'
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
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 run
run: ./odin run examples/demo
timeout-minutes: 10
- name: Odin run -debug
run: ./odin run examples/demo -debug
timeout-minutes: 10
- name: Odin check examples/all
run: ./odin check examples/all -strict-style
timeout-minutes: 10
- name: Core library tests
run: |
cd tests/core
make
timeout-minutes: 10
- name: Vendor library tests
run: |
cd tests/vendor
make
timeout-minutes: 10
- name: Odin internals tests
run: |
cd tests/internal
make
timeout-minutes: 10
- name: Odin check examples/all for Linux i386
run: ./odin check examples/all -vet -strict-style -target:linux_i386
timeout-minutes: 10
- name: Odin check examples/all for 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
- name: Odin check examples/all for OpenBSD amd64
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-13
steps:
- uses: actions/checkout@v1
- name: Download LLVM, and setup PATH
- name: Download LLVM (MacOS Intel)
if: matrix.os == 'macos-13'
run: |
brew install llvm@17
echo "/usr/local/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 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
- name: Download LLVM (MacOS ARM)
if: matrix.os == 'macos-14'
run: |
cd tests/core
make
timeout-minutes: 10
- name: Odin internals tests
run: |
cd tests/internal
make
timeout-minutes: 10
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
brew install llvm@17 wasmtime
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
- name: build odin
- 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: Compile needed Vendor
run: |
make -C vendor/stb/src
make -C vendor/cgltf/src
make -C vendor/miniaudio/src
- name: Odin check
run: ./odin check examples/demo -vet
timeout-minutes: 10
- name: Odin run
run: ./odin run examples/demo
timeout-minutes: 10
- name: Odin run -debug
run: ./odin run examples/demo -debug
timeout-minutes: 10
- name: Odin check examples/all
run: ./odin check examples/all -strict-style
timeout-minutes: 10
- name: Core library tests
- name: Normal Core library tests
run: ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
- name: Optimized Core library tests
run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false
- name: Vendor library tests
run: ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
- name: Internals tests
run: ./odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false
- name: Core library benchmarks
run: ./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
- name: GitHub Issue tests
run: |
cd tests/core
make
timeout-minutes: 10
- name: Odin internals tests
cd tests/issues
./run.sh
- name: Odin check examples/all for Linux i386
run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_i386
if: matrix.os == 'ubuntu-latest'
- name: Odin check examples/all for Linux arm64
run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_arm64
if: matrix.os == 'ubuntu-latest'
- name: Odin check examples/all for FreeBSD amd64
run: ./odin check examples/all -vet -strict-style -disallow-do -target:freebsd_amd64
if: matrix.os == 'ubuntu-latest'
- name: Odin check examples/all for OpenBSD amd64
run: ./odin check examples/all -vet -strict-style -disallow-do -target:openbsd_amd64
if: matrix.os == 'ubuntu-latest'
- name: Run demo on WASI WASM32
run: |
cd tests/internal
make
timeout-minutes: 10
./odin build examples/demo -target:wasi_wasm32 -vet -strict-style -disallow-do -out:demo.wasm
wasmtime ./demo.wasm
if: matrix.os == 'macos-14'
build_windows:
name: Windows Build, Check, and Test
runs-on: windows-2022
timeout-minutes: 15
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: build Odin
shell: cmd
run: |
@@ -182,72 +166,67 @@ jobs:
./build.bat 1
- name: Odin version
run: ./odin version
timeout-minutes: 1
- name: Odin report
run: ./odin report
timeout-minutes: 1
- name: Odin check
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/demo -vet
timeout-minutes: 10
- name: Odin run
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo
timeout-minutes: 10
- name: Odin run -debug
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo -debug
timeout-minutes: 10
- name: Odin check examples/all
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/all -strict-style
timeout-minutes: 10
- name: Core library tests
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\core
call build.bat
timeout-minutes: 10
odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
- name: Optimized core library tests
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false
- name: Core library benchmarks
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
- name: Vendor library tests
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\vendor
call build.bat
timeout-minutes: 10
odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
- name: Odin internals tests
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\internal
call build.bat
timeout-minutes: 10
odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false
- name: Odin documentation tests
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\documentation
call build.bat
timeout-minutes: 10
- name: core:math/big tests
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\core\math\big
call build.bat
timeout-minutes: 10
- name: Odin check examples/all for Windows 32bits
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/all -strict-style -target:windows_i386
timeout-minutes: 10
+13 -28
View File
@@ -11,7 +11,7 @@ jobs:
if: github.repository == 'odin-lang/Odin'
runs-on: windows-2022
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: build Odin
shell: cmd
run: |
@@ -45,7 +45,7 @@ jobs:
if: github.repository == 'odin-lang/Odin'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: (Linux) Download LLVM
run: |
wget https://apt.llvm.org/llvm.sh
@@ -79,7 +79,7 @@ jobs:
if: github.repository == 'odin-lang/Odin'
runs-on: macos-13
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Download LLVM and setup PATH
run: |
brew install llvm@17 dylibbundler
@@ -113,7 +113,7 @@ jobs:
if: github.repository == 'odin-lang/Odin'
runs-on: macos-14 # ARM machine
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Download LLVM and setup PATH
run: |
brew install llvm@17 dylibbundler
@@ -146,16 +146,16 @@ jobs:
runs-on: [ubuntu-latest]
needs: [build_windows, build_macos, build_macos_arm, build_ubuntu]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- uses: actions/setup-python@v2
with:
python-version: '3.8.x'
- name: Install B2 CLI
- name: Install B2 SDK
shell: bash
run: |
python -m pip install --upgrade pip
pip install --upgrade b2
pip install --upgrade b2sdk
- name: Display Python version
run: python -c "import sys; print(sys.version)"
@@ -188,24 +188,9 @@ jobs:
BUCKET: ${{ secrets.B2_BUCKET }}
DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
run: |
echo Authorizing B2 account
b2 account authorize "$APPID" "$APPKEY"
echo Uploading artifcates to B2
chmod +x ./ci/upload_create_nightly.sh
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/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"
echo Creating nightly.json
python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
echo Uploading nightly.json
b2 upload-file "$BUCKET" nightly.json nightly.json
echo Clear B2 account info
b2 clear-account
python3 ci/nightly.py artifact windows-amd64 windows_artifacts/
python3 ci/nightly.py artifact ubuntu-amd64 ubuntu_artifacts/dist.zip
python3 ci/nightly.py artifact macos-amd64 macos_artifacts/dist.zip
python3 ci/nightly.py artifact macos-arm64 macos_arm_artifacts/dist.zip
python3 ci/nightly.py prune
python3 ci/nightly.py json
+1 -1
View File
@@ -303,7 +303,7 @@ bin/
# - Linux/MacOS
odin
!odin/
odin.dSYM
**/*.dSYM
*.bin
demo.bin
libLLVM*.so*
+8 -1
View File
@@ -73,6 +73,8 @@ expect :: proc(val, expected_val: T) -> T ---
// Linux and Darwin Only
syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
// FreeBSD, NetBSD, et cetera
syscall_bsd :: proc(id: uintptr, args: ..uintptr) -> (uintptr, bool) ---
// Atomics
@@ -192,7 +194,8 @@ type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
type_proc_parameter_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
type_proc_return_type :: proc($T: typeid, index: int) -> typeid where type_is_proc(T) ---
type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) ---
type_struct_field_count :: proc($T: typeid) -> int where type_is_struct(T) ---
type_struct_has_implicit_padding :: proc($T: typeid) -> bool where type_is_struct(T) ---
type_polymorphic_record_parameter_count :: proc($T: typeid) -> typeid ---
type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V ---
@@ -295,6 +298,10 @@ simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
// if all listed features are supported.
has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) ---
// Returns the value of the procedure where `x` must be a call expression
procedure_of :: proc(x: $T) -> T where type_is_proc(T) ---
// WASM targets only
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
wasm_memory_size :: proc(index: uintptr) -> int ---
+52 -2
View File
@@ -397,11 +397,34 @@ Logger :: struct {
options: Logger_Options,
}
Random_Generator_Mode :: enum {
Read,
Reset,
Query_Info,
}
Random_Generator_Query_Info_Flag :: enum u32 {
Cryptographic,
Uniform,
External_Entropy,
Resettable,
}
Random_Generator_Query_Info :: distinct bit_set[Random_Generator_Query_Info_Flag; u32]
Random_Generator_Proc :: #type proc(data: rawptr, mode: Random_Generator_Mode, p: []byte)
Random_Generator :: struct {
procedure: Random_Generator_Proc,
data: rawptr,
}
Context :: struct {
allocator: Allocator,
temp_allocator: Allocator,
assertion_failure_proc: Assertion_Failure_Proc,
logger: Logger,
random_generator: Random_Generator,
user_ptr: rawptr,
user_index: int,
@@ -560,6 +583,19 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
*/
Odin_Sanitizer_Flags :: type_of(ODIN_SANITIZER_FLAGS)
/*
// Defined internally by the compiler
Odin_Optimization_Mode :: enum int {
None = -1,
Minimal = 0,
Size = 1,
Speed = 2,
Aggressive = 3,
}
ODIN_OPTIMIZATION_MODE // is a constant
*/
Odin_Optimization_Mode :: type_of(ODIN_OPTIMIZATION_MODE)
/////////////////////////////
// Init Startup Procedures //
@@ -695,13 +731,16 @@ __init_context :: proc "contextless" (c: ^Context) {
c.logger.procedure = default_logger_proc
c.logger.data = nil
c.random_generator.procedure = default_random_generator_proc
c.random_generator.data = nil
}
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
when ODIN_OS == .Freestanding {
// Do nothing
} else {
when !ODIN_DISABLE_ASSERT {
when ODIN_OS != .Orca && !ODIN_DISABLE_ASSERT {
print_caller_location(loc)
print_string(" ")
}
@@ -710,7 +749,18 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code
print_string(": ")
print_string(message)
}
print_byte('\n')
when ODIN_OS == .Orca {
assert_fail(
cstring(raw_data(loc.file_path)),
cstring(raw_data(loc.procedure)),
loc.line,
"",
cstring(raw_data(orca_stderr_buffer[:orca_stderr_buffer_idx])),
)
} else {
print_byte('\n')
}
}
trap()
}
+61 -18
View File
@@ -65,7 +65,7 @@ copy :: proc{copy_slice, copy_from_string}
// 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`.
// Note: If you want the elements to remain in their order, use `ordered_remove`.
// Note: If the index is out of bounds, this procedure will panic.
@builtin
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
@@ -79,7 +79,7 @@ unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_loca
// `ordered_remove` 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`.
// Note: If the elements do not have to remain in their order, prefer `unordered_remove`.
// Note: If the index is out of bounds, this procedure will panic.
@builtin
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
@@ -163,21 +163,43 @@ pop_front_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bo
// `clear` will set the length of a passed dynamic array or map to `0`
@builtin
clear :: proc{clear_dynamic_array, clear_map}
clear :: proc{
clear_dynamic_array,
clear_map,
clear_soa_dynamic_array,
}
// `reserve` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
@builtin
reserve :: proc{reserve_dynamic_array, reserve_map}
reserve :: proc{
reserve_dynamic_array,
reserve_map,
reserve_soa,
}
@builtin
non_zero_reserve :: proc{non_zero_reserve_dynamic_array}
non_zero_reserve :: proc{
non_zero_reserve_dynamic_array,
non_zero_reserve_soa,
}
// `resize` will try to resize memory of a passed dynamic array to the requested element count (setting the `len`, and possibly `cap`).
@builtin
resize :: proc{resize_dynamic_array}
resize :: proc{
resize_dynamic_array,
resize_soa,
}
@builtin
non_zero_resize :: proc{non_zero_resize_dynamic_array}
non_zero_resize :: proc{
non_zero_resize_dynamic_array,
non_zero_resize_soa,
}
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
@builtin
@@ -268,7 +290,7 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
return
}
DEFAULT_RESERVE_CAPACITY :: 16
DEFAULT_DYNAMIC_ARRAY_CAPACITY :: 8
@(require_results)
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
@@ -295,7 +317,7 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo
// Note: Prefer using the procedure group `make`.
@(builtin, require_results)
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
return make_dynamic_array_len_cap(T, 0, 0, allocator, loc)
}
// `make_dynamic_array_len` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
@@ -364,6 +386,11 @@ make :: proc{
make_dynamic_array_len_cap,
make_map,
make_multi_pointer,
make_soa_slice,
make_soa_dynamic_array,
make_soa_dynamic_array_len,
make_soa_dynamic_array_len_cap,
}
@@ -383,7 +410,7 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
//
// Note: Prefer the procedure group `reserve`
@builtin
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) -> Allocator_Error {
reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) if m != nil else nil
}
@@ -423,7 +450,8 @@ _append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero:
return 1, nil
} else {
if cap(array) < len(array)+1 {
cap := 2 * cap(array) + max(8, 1)
// Same behavior as _append_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
cap := 2 * cap(array) + DEFAULT_DYNAMIC_ARRAY_CAPACITY
// do not 'or_return' here as it could be a partial success
if should_zero {
@@ -472,7 +500,7 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l
return arg_len, nil
} else {
if cap(array) < len(array)+arg_len {
cap := 2 * cap(array) + max(8, arg_len)
cap := 2 * cap(array) + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
// do not 'or_return' here as it could be a partial success
if should_zero {
@@ -540,8 +568,23 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
}
// The append built-in procedure appends elements to the end of a dynamic array
@builtin append :: proc{append_elem, append_elems, append_elem_string}
@builtin non_zero_append :: proc{non_zero_append_elem, non_zero_append_elems, non_zero_append_elem_string}
@builtin append :: proc{
append_elem,
append_elems,
append_elem_string,
append_soa_elem,
append_soa_elems,
}
@builtin non_zero_append :: proc{
non_zero_append_elem,
non_zero_append_elems,
non_zero_append_elem_string,
non_zero_append_soa_elem,
non_zero_append_soa_elems,
}
@builtin
@@ -721,12 +764,12 @@ _reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: i
}
@builtin
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
return _reserve_dynamic_array(array, capacity, true, loc)
}
@builtin
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
return _reserve_dynamic_array(array, capacity, false, loc)
}
@@ -773,12 +816,12 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int,
}
@builtin
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
return _resize_dynamic_array(array, length, true, loc=loc)
}
@builtin
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
return _resize_dynamic_array(array, length, false, loc=loc)
}
+64 -50
View File
@@ -55,7 +55,7 @@ raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Sl
if array == nil {
return nil
}
field_count := uintptr(intrinsics.type_struct_field_count(E))
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
footer = (^Raw_SOA_Footer_Slice)(uintptr(array) + field_count*size_of(rawptr))
return
}
@@ -64,12 +64,7 @@ raw_soa_footer_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) -> (footer: ^Ra
if array == nil {
return nil
}
field_count: uintptr
when intrinsics.type_is_array(E) {
field_count = len(E)
} else {
field_count = uintptr(intrinsics.type_struct_field_count(E))
}
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
footer = (^Raw_SOA_Footer_Dynamic_Array)(uintptr(array) + field_count*size_of(rawptr))
return
}
@@ -98,7 +93,7 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
ti = type_info_base(ti)
si := &ti.variant.(Type_Info_Struct)
field_count := uintptr(intrinsics.type_struct_field_count(E))
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
total_size := 0
for i in 0..<field_count {
@@ -147,7 +142,7 @@ make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.al
@(builtin, require_results)
make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
context.allocator = allocator
reserve_soa(&array, DEFAULT_RESERVE_CAPACITY, loc) or_return
reserve_soa(&array, 0, loc) or_return
return array, nil
}
@@ -187,8 +182,28 @@ resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_locat
return nil
}
@builtin
non_zero_resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
if array == nil {
return nil
}
non_zero_reserve_soa(array, length, loc) or_return
footer := raw_soa_footer(array)
footer.len = length
return nil
}
@builtin
reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
return _reserve_soa(array, capacity, true, loc)
}
@builtin
non_zero_reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
return _reserve_soa(array, capacity, false, loc)
}
_reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, zero_memory: bool, loc := #caller_location) -> Allocator_Error {
if array == nil {
return nil
}
@@ -213,12 +228,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
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))
}
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
assert(footer.cap == old_cap)
old_size := 0
@@ -238,7 +248,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
old_data := (^rawptr)(array)^
new_bytes := array.allocator.procedure(
array.allocator.data, .Alloc, new_size, max_align,
array.allocator.data, .Alloc if zero_memory else .Alloc_Non_Zeroed, new_size, max_align,
nil, old_size, loc,
) or_return
new_data := raw_data(new_bytes)
@@ -273,15 +283,26 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
return nil
}
@builtin
append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_soa_elem(array, true, arg, loc)
}
@builtin
non_zero_append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_soa_elem(array, false, arg, loc)
}
_append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil {
return 0, nil
}
if cap(array) <= len(array) + 1 {
cap := 2 * cap(array) + 8
err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
// Same behavior as append_soa_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
cap := 2 * cap(array) + DEFAULT_DYNAMIC_ARRAY_CAPACITY
err = _reserve_soa(array, cap, zero_memory, loc) // do not 'or_return' here as it could be a partial success
}
footer := raw_soa_footer(array)
@@ -290,12 +311,7 @@ append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_locat
ti := type_info_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))
}
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
data := (^rawptr)(array)^
@@ -326,7 +342,17 @@ append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_locat
}
@builtin
append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_soa_elems(array, true, args=args, loc=loc)
}
@builtin
non_zero_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
return _append_soa_elems(array, false, args=args, loc=loc)
}
_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil {
return
}
@@ -337,8 +363,8 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
}
if cap(array) <= len(array)+arg_len {
cap := 2 * cap(array) + max(8, arg_len)
err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
cap := 2 * cap(array) + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
err = _reserve_soa(array, cap, zero_memory, loc) // do not 'or_return' here as it could be a partial success
}
arg_len = min(cap(array)-len(array), arg_len)
@@ -347,7 +373,7 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
ti := type_info_of(typeid_of(T))
ti = type_info_base(ti)
si := &ti.variant.(Type_Info_Struct)
field_count := uintptr(intrinsics.type_struct_field_count(E))
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
data := (^rawptr)(array)^
@@ -389,7 +415,8 @@ append_soa :: proc{
delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
when intrinsics.type_struct_field_count(E) != 0 {
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
when field_count != 0 {
array := array
ptr := (^rawptr)(&array)^
free(ptr, allocator, loc) or_return
@@ -398,7 +425,8 @@ delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc
}
delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error {
when intrinsics.type_struct_field_count(E) != 0 {
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
when field_count != 0 {
array := array
ptr := (^rawptr)(&array)^
footer := raw_soa_footer(&array)
@@ -416,7 +444,8 @@ delete_soa :: proc{
clear_soa_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) {
when intrinsics.type_struct_field_count(E) != 0 {
field_count :: len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E)
when field_count != 0 {
footer := raw_soa_footer(array)
footer.len = 0
}
@@ -438,12 +467,7 @@ into_dynamic_soa :: proc(array: $T/#soa[]$E) -> #soa[dynamic]E {
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))
}
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
array := array
dynamic_data := ([^]rawptr)(&d)[:field_count]
@@ -467,12 +491,7 @@ unordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #cal
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))
}
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
data := uintptr(array)
for i in 0..<field_count {
@@ -500,12 +519,7 @@ ordered_remove_soa :: proc(array: ^$T/#soa[dynamic]$E, index: int, loc := #calle
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))
}
field_count := uintptr(len(E) when intrinsics.type_is_array(E) else intrinsics.type_struct_field_count(E))
data := uintptr(array)
for i in 0..<field_count {
+1 -1
View File
@@ -6,7 +6,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
} else when ODIN_DEFAULT_TO_PANIC_ALLOCATOR {
default_allocator_proc :: panic_allocator_proc
default_allocator :: panic_allocator
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
} else when ODIN_OS != .Orca && (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32) {
default_allocator :: default_wasm_allocator
default_allocator_proc :: wasm_allocator_proc
} else {
@@ -12,7 +12,8 @@ Memory_Block :: struct {
capacity: uint,
}
// NOTE: This is for internal use, prefer `Arena` from `core:mem/virtual` if necessary
// NOTE: This is a growing arena that is only used for the default temp allocator.
// For your own growing arena needs, prefer `Arena` from `core:mem/virtual`.
Arena :: struct {
backing_allocator: Allocator,
curr_block: ^Memory_Block,
+1 -1
View File
@@ -157,7 +157,7 @@ __dynamic_map_get // dynamic map calls
__dynamic_map_set // dynamic map calls
## Dynamic literals ([dymamic]T and map[K]V) (can be disabled with -no-dynamic-literals)
## Dynamic literals ([dynamic]T and map[K]V) (can be disabled with -no-dynamic-literals)
__dynamic_array_reserve
__dynamic_array_append
+30 -11
View File
@@ -6,15 +6,34 @@ package runtime
import "base:intrinsics"
when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
@(link_name="_start", linkage="strong", require, export)
_start :: proc "c" () {
context = default_context()
#force_no_inline _startup_runtime()
intrinsics.__entry_point()
when ODIN_OS == .Orca {
@(linkage="strong", require, export)
oc_on_init :: proc "c" () {
context = default_context()
#force_no_inline _startup_runtime()
intrinsics.__entry_point()
}
@(linkage="strong", require, export)
oc_on_terminate :: proc "c" () {
context = default_context()
#force_no_inline _cleanup_runtime()
}
} else {
@(link_name="_start", linkage="strong", require, export)
_start :: proc "c" () {
context = default_context()
when ODIN_OS == .WASI {
_wasi_setup_args()
}
#force_no_inline _startup_runtime()
intrinsics.__entry_point()
}
@(link_name="_end", linkage="strong", require, export)
_end :: proc "c" () {
context = default_context()
#force_no_inline _cleanup_runtime()
}
}
@(link_name="_end", linkage="strong", require, export)
_end :: proc "c" () {
context = default_context()
#force_no_inline _cleanup_runtime()
}
}
}
+4
View File
@@ -4,6 +4,8 @@ package runtime
bounds_trap :: proc "contextless" () -> ! {
when ODIN_OS == .Windows {
windows_trap_array_bounds()
} else when ODIN_OS == .Orca {
abort_ext("", "", 0, "bounds trap")
} else {
trap()
}
@@ -13,6 +15,8 @@ bounds_trap :: proc "contextless" () -> ! {
type_assertion_trap :: proc "contextless" () -> ! {
when ODIN_OS == .Windows {
windows_trap_type_assertion()
} else when ODIN_OS == .Orca {
abort_ext("", "", 0, "type assertion trap")
} else {
trap()
}
+3 -3
View File
@@ -97,14 +97,14 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
}
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
return _heap_alloc(size, zero_memory)
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
return _heap_resize(ptr, new_size)
}
heap_free :: proc(ptr: rawptr) {
heap_free :: proc "contextless" (ptr: rawptr) {
_heap_free(ptr)
}
+29
View File
@@ -0,0 +1,29 @@
//+build orca
//+private
package runtime
foreign {
@(link_name="malloc") _orca_malloc :: proc "c" (size: int) -> rawptr ---
@(link_name="calloc") _orca_calloc :: proc "c" (num, size: int) -> rawptr ---
@(link_name="free") _orca_free :: proc "c" (ptr: rawptr) ---
@(link_name="realloc") _orca_realloc :: proc "c" (ptr: rawptr, size: int) -> rawptr ---
}
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
if size <= 0 {
return nil
}
if zero_memory {
return _orca_calloc(1, size)
} else {
return _orca_malloc(size)
}
}
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
return _orca_realloc(ptr, new_size)
}
_heap_free :: proc "contextless" (ptr: rawptr) {
_orca_free(ptr)
}
+7 -4
View File
@@ -2,14 +2,17 @@
//+private
package runtime
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
context = default_context()
unimplemented("base:runtime 'heap_alloc' procedure is not supported on this platform")
}
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
context = default_context()
unimplemented("base:runtime 'heap_resize' procedure is not supported on this platform")
}
_heap_free :: proc(ptr: rawptr) {
_heap_free :: proc "contextless" (ptr: rawptr) {
context = default_context()
unimplemented("base:runtime 'heap_free' procedure is not supported on this platform")
}
}
+3 -3
View File
@@ -16,7 +16,7 @@ foreign libc {
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---
}
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
if size <= 0 {
return nil
}
@@ -27,12 +27,12 @@ _heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
}
}
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
// POSIX platforms. Ensure your caller takes this into account.
return _unix_realloc(ptr, new_size)
}
_heap_free :: proc(ptr: rawptr) {
_heap_free :: proc "contextless" (ptr: rawptr) {
_unix_free(ptr)
}
+3 -3
View File
@@ -14,11 +14,11 @@ foreign kernel32 {
HeapFree :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr) -> b32 ---
}
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
_heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
HEAP_ZERO_MEMORY :: 0x00000008
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
}
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
_heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
if new_size == 0 {
_heap_free(ptr)
return nil
@@ -30,7 +30,7 @@ _heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
HEAP_ZERO_MEMORY :: 0x00000008
return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ptr, uint(new_size))
}
_heap_free :: proc(ptr: rawptr) {
_heap_free :: proc "contextless" (ptr: rawptr) {
if ptr == nil {
return
}
+33 -29
View File
@@ -1,3 +1,4 @@
//+vet !cast
package runtime
import "base:intrinsics"
@@ -29,7 +30,7 @@ is_power_of_two_int :: #force_inline proc "contextless" (x: int) -> bool {
return (x & (x-1)) == 0
}
align_forward_int :: #force_inline proc(ptr, align: int) -> int {
align_forward_int :: #force_inline proc "odin" (ptr, align: int) -> int {
assert(is_power_of_two_int(align))
p := ptr
@@ -47,7 +48,7 @@ is_power_of_two_uint :: #force_inline proc "contextless" (x: uint) -> bool {
return (x & (x-1)) == 0
}
align_forward_uint :: #force_inline proc(ptr, align: uint) -> uint {
align_forward_uint :: #force_inline proc "odin" (ptr, align: uint) -> uint {
assert(is_power_of_two_uint(align))
p := ptr
@@ -65,7 +66,7 @@ is_power_of_two_uintptr :: #force_inline proc "contextless" (x: uintptr) -> bool
return (x & (x-1)) == 0
}
align_forward_uintptr :: #force_inline proc(ptr, align: uintptr) -> uintptr {
align_forward_uintptr :: #force_inline proc "odin" (ptr, align: uintptr) -> uintptr {
assert(is_power_of_two_uintptr(align))
p := ptr
@@ -483,7 +484,7 @@ quaternion256_ne :: #force_inline proc "contextless" (a, b: quaternion256) -> bo
string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int) {
// NOTE(bill): Duplicated here to remove dependency on package unicode/utf8
@static accept_sizes := [256]u8{
@(static, rodata) accept_sizes := [256]u8{
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
@@ -504,7 +505,7 @@ string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int
}
Accept_Range :: struct {lo, hi: u8}
@static accept_ranges := [5]Accept_Range{
@(static, rodata) accept_ranges := [5]Accept_Range{
{0x80, 0xbf},
{0xa0, 0xbf},
{0x80, 0x9f},
@@ -642,21 +643,24 @@ abs_quaternion256 :: #force_inline proc "contextless" (x: quaternion256) -> f64
quo_complex32 :: proc "contextless" (n, m: complex32) -> complex32 {
e, f: f16
nr, ni := f32(real(n)), f32(imag(n))
mr, mi := f32(real(m)), f32(imag(m))
if abs(real(m)) >= abs(imag(m)) {
ratio := imag(m) / real(m)
denom := real(m) + ratio*imag(m)
e = (real(n) + imag(n)*ratio) / denom
f = (imag(n) - real(n)*ratio) / denom
e, f: f32
if abs(mr) >= abs(mi) {
ratio := mi / mr
denom := mr + ratio*mi
e = (nr + ni*ratio) / denom
f = (ni - nr*ratio) / denom
} else {
ratio := real(m) / imag(m)
denom := imag(m) + ratio*real(m)
e = (real(n)*ratio + imag(n)) / denom
f = (imag(n)*ratio - real(n)) / denom
ratio := mr / mi
denom := mi + ratio*mr
e = (nr*ratio + ni) / denom
f = (ni*ratio - nr) / denom
}
return complex(e, f)
return complex(f16(e), f16(f))
}
@@ -697,15 +701,15 @@ quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 {
}
mul_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q)
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r)
q0, q1, q2, q3 := f32(real(q)), f32(imag(q)), f32(jmag(q)), f32(kmag(q))
r0, r1, r2, r3 := f32(real(r)), f32(imag(r)), f32(jmag(r)), f32(kmag(r))
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
return quaternion(w=t0, x=t1, y=t2, z=t3)
return quaternion(w=f16(t0), x=f16(t1), y=f16(t2), z=f16(t3))
}
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
@@ -733,8 +737,8 @@ mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
}
quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q)
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r)
q0, q1, q2, q3 := f32(real(q)), f32(imag(q)), f32(jmag(q)), f32(kmag(q))
r0, r1, r2, r3 := f32(real(r)), f32(imag(r)), f32(jmag(r)), f32(kmag(r))
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3)
@@ -743,7 +747,7 @@ quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
return quaternion(w=t0, x=t1, y=t2, z=t3)
return quaternion(w=f16(t0), x=f16(t1), y=f16(t2), z=f16(t3))
}
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
@@ -1012,26 +1016,26 @@ modti3 :: proc "c" (a, b: i128) -> i128 {
bn := (b ~ s_b) - s_b
r: u128 = ---
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r)
return (transmute(i128)r ~ s_a) - s_a
_ = udivmod128(u128(an), u128(bn), &r)
return (i128(r) ~ s_a) - s_a
}
@(link_name="__divmodti4", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
divmodti4 :: proc "c" (a, b: i128, rem: ^i128) -> i128 {
u := udivmod128(transmute(u128)a, transmute(u128)b, cast(^u128)rem)
return transmute(i128)u
u := udivmod128(u128(a), u128(b), (^u128)(rem))
return i128(u)
}
@(link_name="__divti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
divti3 :: proc "c" (a, b: i128) -> i128 {
u := udivmodti4(transmute(u128)a, transmute(u128)b, nil)
return transmute(i128)u
u := udivmodti4(u128(a), u128(b), nil)
return i128(u)
}
@(link_name="__fixdfti", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
fixdfti :: proc(a: u64) -> i128 {
fixdfti :: proc "c" (a: u64) -> i128 {
significandBits :: 52
typeWidth :: (size_of(u64)*8)
exponentBits :: (typeWidth - significandBits - 1)
+43
View File
@@ -0,0 +1,43 @@
//+build orca
//+private
package runtime
import "base:intrinsics"
// Constants allowing to specify the level of logging verbosity.
log_level :: enum u32 {
// Only errors are logged.
ERROR = 0,
// Only warnings and errors are logged.
WARNING = 1,
// All messages are logged.
INFO = 2,
COUNT = 3,
}
@(default_calling_convention="c", link_prefix="oc_")
foreign {
abort_ext :: proc(file: cstring, function: cstring, line: i32, fmt: cstring, #c_vararg args: ..any) -> ! ---
assert_fail :: proc(file: cstring, function: cstring, line: i32, src: cstring, fmt: cstring, #c_vararg args: ..any) -> ! ---
log_ext :: proc(level: log_level, function: cstring, file: cstring, line: i32, fmt: cstring, #c_vararg args: ..any) ---
}
// NOTE: This is all pretty gross, don't look.
// WASM is single threaded so this should be fine.
orca_stderr_buffer: [4096]byte
orca_stderr_buffer_idx: int
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
for b in data {
orca_stderr_buffer[orca_stderr_buffer_idx] = b
orca_stderr_buffer_idx += 1
if b == '\n' || orca_stderr_buffer_idx == len(orca_stderr_buffer)-1 {
log_ext(.ERROR, "", "", 0, cstring(raw_data(orca_stderr_buffer[:orca_stderr_buffer_idx])))
orca_stderr_buffer_idx = 0
}
}
return len(data), 0
}
+47 -3
View File
@@ -2,10 +2,54 @@
//+private
package runtime
import "core:sys/wasm/wasi"
foreign import wasi "wasi_snapshot_preview1"
@(default_calling_convention="contextless")
foreign wasi {
fd_write :: proc(
fd: i32,
iovs: [][]byte,
n: ^uint,
) -> u16 ---
@(private="file")
args_sizes_get :: proc(
num_of_args: ^uint,
size_of_args: ^uint,
) -> u16 ---
@(private="file")
args_get :: proc(
argv: [^]cstring,
argv_buf: [^]byte,
) -> u16 ---
}
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
data_iovec := (wasi.ciovec_t)(data)
n, err := wasi.fd_write(1, {data_iovec})
n: uint
err := fd_write(1, {data}, &n)
return int(n), _OS_Errno(err)
}
_wasi_setup_args :: proc() {
num_of_args, size_of_args: uint
if errno := args_sizes_get(&num_of_args, &size_of_args); errno != 0 {
return
}
err: Allocator_Error
if args__, err = make([]cstring, num_of_args); err != nil {
return
}
args_buf: []byte
if args_buf, err = make([]byte, size_of_args); err != nil {
delete(args__)
return
}
if errno := args_get(raw_data(args__), raw_data(args_buf)); errno != 0 {
delete(args__)
delete(args_buf)
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
RtlMoveMemory(dst, src, len)
return dst
}
} else when ODIN_NO_CRT || (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32) {
} else when ODIN_NO_CRT || (ODIN_OS != .Orca && (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32)) {
// NOTE: on wasm, calls to these procs are generated (by LLVM) with type `i32` instead of `int`.
//
// NOTE: `#any_int` is also needed, because calls that we generate (and package code)
+127
View File
@@ -0,0 +1,127 @@
package runtime
import "base:intrinsics"
@(require_results)
random_generator_read_bytes :: proc(rg: Random_Generator, p: []byte) -> bool {
if rg.procedure != nil {
rg.procedure(rg.data, .Read, p)
return true
}
return false
}
@(require_results)
random_generator_read_ptr :: proc(rg: Random_Generator, p: rawptr, len: uint) -> bool {
if rg.procedure != nil {
rg.procedure(rg.data, .Read, ([^]byte)(p)[:len])
return true
}
return false
}
@(require_results)
random_generator_query_info :: proc(rg: Random_Generator) -> (info: Random_Generator_Query_Info) {
if rg.procedure != nil {
rg.procedure(rg.data, .Query_Info, ([^]byte)(&info)[:size_of(info)])
}
return
}
random_generator_reset_bytes :: proc(rg: Random_Generator, p: []byte) {
if rg.procedure != nil {
rg.procedure(rg.data, .Reset, p)
}
}
random_generator_reset_u64 :: proc(rg: Random_Generator, p: u64) {
if rg.procedure != nil {
p := p
rg.procedure(rg.data, .Reset, ([^]byte)(&p)[:size_of(p)])
}
}
Default_Random_State :: struct {
state: u64,
inc: u64,
}
default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, p: []byte) {
@(require_results)
read_u64 :: proc "contextless" (r: ^Default_Random_State) -> u64 {
old_state := r.state
r.state = old_state * 6364136223846793005 + (r.inc|1)
xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081
rot := (old_state >> 59)
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63))
}
@(thread_local)
global_rand_seed: Default_Random_State
init :: proc "contextless" (r: ^Default_Random_State, seed: u64) {
seed := seed
if seed == 0 {
seed = u64(intrinsics.read_cycle_counter())
}
r.state = 0
r.inc = (seed << 1) | 1
_ = read_u64(r)
r.state += seed
_ = read_u64(r)
}
r: ^Default_Random_State = ---
if data == nil {
r = &global_rand_seed
} else {
r = cast(^Default_Random_State)data
}
switch mode {
case .Read:
if r.state == 0 && r.inc == 0 {
init(r, 0)
}
switch len(p) {
case size_of(u64):
// Fast path for a 64-bit destination.
intrinsics.unaligned_store((^u64)(raw_data(p)), read_u64(r))
case:
// All other cases.
pos := i8(0)
val := u64(0)
for &v in p {
if pos == 0 {
val = read_u64(r)
pos = 7
}
v = byte(val)
val >>= 8
pos -= 1
}
}
case .Reset:
seed: u64
mem_copy_non_overlapping(&seed, raw_data(p), min(size_of(seed), len(p)))
init(r, seed)
case .Query_Info:
if len(p) != size_of(Random_Generator_Query_Info) {
return
}
info := (^Random_Generator_Query_Info)(raw_data(p))
info^ += {.Uniform, .Resettable}
}
}
default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator {
return {
procedure = default_random_generator_proc,
data = state,
}
}
+3 -3
View File
@@ -58,7 +58,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
return u128(n[high] >> _ctz(d[high]))
}
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])))
sr = u32(i32(_clz(d[high])) - i32(_clz(n[high])))
if sr > U64_BITS - 2 {
if rem != nil {
rem^ = a
@@ -107,7 +107,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
r[low] = n[high] >> (sr - U64_BITS)
}
} else {
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])))
sr = u32(i32(_clz(d[high])) - i32(_clz(n[high])))
if sr > U64_BITS - 1 {
if rem != nil {
@@ -143,7 +143,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
r_all = transmute(u128)r
s := i128(b - r_all - 1) >> (U128_BITS - 1)
carry = u32(s & 1)
r_all -= b & transmute(u128)s
r_all -= b & u128(s)
r = transmute([2]u64)r_all
}
+17 -17
View File
@@ -7,20 +7,20 @@ import "base:intrinsics"
Port of emmalloc, modified for use in Odin.
Invariants:
- Per-allocation header overhead is 8 bytes, smallest allocated payload
amount is 8 bytes, and a multiple of 4 bytes.
- Acquired memory blocks are subdivided into disjoint regions that lie
next to each other.
- A region is either in used or free.
Used regions may be adjacent, and a used and unused region
may be adjacent, but not two unused ones - they would be
merged.
- Memory allocation takes constant time, unless the alloc needs to wasm_memory_grow()
or memory is very close to being exhausted.
- Free and used regions are managed inside "root regions", which are slabs
of memory acquired via wasm_memory_grow().
- Memory retrieved using wasm_memory_grow() can not be given back to the OS.
Therefore, frees are internal to the allocator.
- Per-allocation header overhead is 8 bytes, smallest allocated payload
amount is 8 bytes, and a multiple of 4 bytes.
- Acquired memory blocks are subdivided into disjoint regions that lie
next to each other.
- A region is either in used or free.
Used regions may be adjacent, and a used and unused region
may be adjacent, but not two unused ones - they would be
merged.
- Memory allocation takes constant time, unless the alloc needs to wasm_memory_grow()
or memory is very close to being exhausted.
- Free and used regions are managed inside "root regions", which are slabs
of memory acquired via wasm_memory_grow().
- Memory retrieved using wasm_memory_grow() can not be given back to the OS.
Therefore, frees are internal to the allocator.
Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
@@ -495,7 +495,7 @@ claim_more_memory :: proc(a: ^WASM_Allocator, num_bytes: uint) -> bool {
// we can just extend the spill.
spill_end := uintptr(raw_data(a.spill)) + uintptr(len(a.spill))
if spill_end == uintptr(raw_data(allocated)) {
raw_spill := transmute(^Raw_Slice)(&a.spill)
raw_spill := (^Raw_Slice)(&a.spill)
raw_spill.len += len(allocated)
} else {
// Otherwise, we have to "waste" the previous spill.
@@ -679,7 +679,7 @@ allocate_memory :: proc(a: ^WASM_Allocator, alignment: uint, size: uint, loc :=
// but we just had a stale bit set to mark a populated bucket.
// Reset the bit to update latest status so that we do not
// redundantly look at this bucket again.
a.free_region_buckets_used &= ~(BUCKET_BITMASK_T(1) << bucket_index)
a.free_region_buckets_used &~= BUCKET_BITMASK_T(1) << bucket_index
bucket_mask ~= 1
}
@@ -760,7 +760,7 @@ free :: proc(a: ^WASM_Allocator, ptr: rawptr, loc := #caller_location) {
defer unlock(a)
size := region.size
assert(region_is_in_use(region), "double free", loc=loc)
assert(region_is_in_use(region), "double free or corrupt region", loc=loc)
prev_region_size_field := ([^]uint)(region)[-1]
prev_region_size := prev_region_size_field & ~uint(FREE_REGION_FLAG)
-50
View File
@@ -1,50 +0,0 @@
import subprocess
import sys
import json
import datetime
import urllib.parse
import sys
def main():
files_by_date = {}
bucket = sys.argv[1]
files_lines = execute_cli(f"b2 ls --long b2://{bucket}/nightly/").split("\n")
for x in files_lines:
parts = x.split(" ", 1)
if parts[0]:
json_str = execute_cli(f"b2 file info {parts[0]}")
data = json.loads(json_str)
name = remove_prefix(data['fileName'], "nightly/")
url = f"https://f001.backblazeb2.com/file/{bucket}/nightly/{urllib.parse.quote_plus(name)}"
sha1 = data['contentSha1']
size = int(data['size'])
ts = int(data['fileInfo']['src_last_modified_millis'])
date = datetime.datetime.fromtimestamp(ts/1000).strftime('%Y-%m-%d')
if date not in files_by_date.keys():
files_by_date[date] = []
files_by_date[date].append({
'name': name,
'url': url,
'sha1': sha1,
'sizeInBytes': size,
})
now = datetime.datetime.utcnow().isoformat()
print(json.dumps({
'last_updated' : now,
'files': files_by_date
}, sort_keys=True, indent=4))
def remove_prefix(text, prefix):
return text[text.startswith(prefix) and len(prefix):]
def execute_cli(command):
sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
return sb.stdout.read().decode("utf-8");
if __name__ == '__main__':
sys.exit(main())
-33
View File
@@ -1,33 +0,0 @@
import subprocess
import sys
import json
import datetime
import urllib.parse
import sys
def main():
files_by_date = {}
bucket = sys.argv[1]
days_to_keep = int(sys.argv[2])
print(f"Looking for binaries to delete older than {days_to_keep} days")
files_lines = execute_cli(f"b2 ls --long --versions b2://{bucket}/nightly/").split("\n")
for x in files_lines:
parts = [y for y in x.split(' ') if y]
if parts and parts[0]:
date = datetime.datetime.strptime(parts[2], '%Y-%m-%d').replace(hour=0, minute=0, second=0, microsecond=0)
now = datetime.datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
delta = now - date
if delta.days > days_to_keep:
print(f'Deleting {parts[5]}')
execute_cli(f'b2 rm {parts[0]}')
def execute_cli(command):
sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
return sb.stdout.read().decode("utf-8");
if __name__ == '__main__':
sys.exit(main())
+140
View File
@@ -0,0 +1,140 @@
import os
import sys
from zipfile import ZipFile, ZIP_DEFLATED
from b2sdk.v2 import InMemoryAccountInfo, B2Api
from datetime import datetime
import json
UPLOAD_FOLDER = "nightly/"
info = InMemoryAccountInfo()
b2_api = B2Api(info)
application_key_id = os.environ['APPID']
application_key = os.environ['APPKEY']
bucket_name = os.environ['BUCKET']
days_to_keep = os.environ['DAYS_TO_KEEP']
def auth() -> bool:
try:
realm = b2_api.account_info.get_realm()
return True # Already authenticated
except:
pass # Not yet authenticated
err = b2_api.authorize_account("production", application_key_id, application_key)
return err == None
def get_bucket():
if not auth(): sys.exit(1)
return b2_api.get_bucket_by_name(bucket_name)
def remove_prefix(text: str, prefix: str) -> str:
return text[text.startswith(prefix) and len(prefix):]
def create_and_upload_artifact_zip(platform: str, artifact: str) -> int:
now = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
destination_zip_name = "odin-{}-nightly+{}.zip".format(platform, now.strftime("%Y-%m-%d"))
source_zip_name = artifact
if not artifact.endswith(".zip"):
print(f"Creating archive {destination_zip_name} from {artifact} and uploading to {bucket_name}")
source_zip_name = destination_zip_name
with ZipFile(source_zip_name, mode='w', compression=ZIP_DEFLATED, compresslevel=9) as z:
for root, directory, filenames in os.walk(artifact):
for file in filenames:
file_path = os.path.join(root, file)
zip_path = os.path.join("dist", os.path.relpath(file_path, artifact))
z.write(file_path, zip_path)
if not os.path.exists(source_zip_name):
print(f"Error: Newly created ZIP archive {source_zip_name} not found.")
return 1
print("Uploading {} to {}".format(source_zip_name, UPLOAD_FOLDER + destination_zip_name))
bucket = get_bucket()
res = bucket.upload_local_file(
source_zip_name, # Local file to upload
"nightly/" + destination_zip_name, # B2 destination path
)
return 0
def prune_artifacts():
print(f"Looking for binaries to delete older than {days_to_keep} days")
bucket = get_bucket()
for file, _ in bucket.ls(UPLOAD_FOLDER, latest_only=False):
# Timestamp is in milliseconds
date = datetime.fromtimestamp(file.upload_timestamp / 1_000.0).replace(hour=0, minute=0, second=0, microsecond=0)
now = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
delta = now - date
if delta.days > int(days_to_keep):
print("Deleting {}".format(file.file_name))
file.delete()
return 0
def update_nightly_json():
print(f"Updating nightly.json with files {days_to_keep} days or newer")
files_by_date = {}
bucket = get_bucket()
for file, _ in bucket.ls(UPLOAD_FOLDER, latest_only=True):
# Timestamp is in milliseconds
date = datetime.fromtimestamp(file.upload_timestamp / 1_000.0).replace(hour=0, minute=0, second=0, microsecond=0).strftime('%Y-%m-%d')
name = remove_prefix(file.file_name, UPLOAD_FOLDER)
sha1 = file.content_sha1
size = file.size
url = bucket.get_download_url(file.file_name)
if date not in files_by_date.keys():
files_by_date[date] = []
files_by_date[date].append({
'name': name,
'url': url,
'sha1': sha1,
'sizeInBytes': size,
})
now = datetime.utcnow().isoformat()
nightly = json.dumps({
'last_updated' : now,
'files': files_by_date
}, sort_keys=True, indent=4, ensure_ascii=False).encode('utf-8')
res = bucket.upload_bytes(
nightly, # JSON bytes
"nightly.json", # B2 destination path
)
return 0
if __name__ == "__main__":
if len(sys.argv) == 1:
print("Usage: {} <verb> [arguments]".format(sys.argv[0]))
print("\tartifact <platform prefix> <artifact path>\n\t\tCreates and uploads a platform artifact zip.")
print("\tprune\n\t\tDeletes old artifacts from bucket")
print("\tjson\n\t\tUpdate and upload nightly.json")
sys.exit(1)
else:
command = sys.argv[1].lower()
if command == "artifact":
if len(sys.argv) != 4:
print("Usage: {} artifact <platform prefix> <artifact path>".format(sys.argv[0]))
print("Error: Expected artifact command to be given platform prefix and artifact path.\n")
sys.exit(1)
res = create_and_upload_artifact_zip(sys.argv[2], sys.argv[3])
sys.exit(res)
elif command == "prune":
res = prune_artifacts()
sys.exit(res)
elif command == "json":
res = update_nightly_json()
sys.exit(res)
-25
View File
@@ -1,25 +0,0 @@
#!/bin/bash
set -e
bucket=$1
platform=$2
artifact=$3
now=$(date +'%Y-%m-%d')
filename="odin-$platform-nightly+$now.zip"
echo "Creating archive $filename from $artifact and uploading to $bucket"
# 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 file upload "$bucket" "output/$filename" "nightly/$filename"
+2 -2
View File
@@ -29,12 +29,12 @@ MIN_READ_BUFFER_SIZE :: 16
@(private)
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator, loc := #caller_location) {
size := size
size = max(size, MIN_READ_BUFFER_SIZE)
reader_reset(b, rd)
b.buf_allocator = allocator
b.buf = make([]byte, size, allocator)
b.buf = make([]byte, size, allocator, loc)
}
reader_init_with_buf :: proc(b: ^Reader, rd: io.Reader, buf: []byte) {
+35 -35
View File
@@ -27,19 +27,19 @@ Read_Op :: enum i8 {
}
buffer_init :: proc(b: ^Buffer, buf: []byte) {
resize(&b.buf, len(buf))
buffer_init :: proc(b: ^Buffer, buf: []byte, loc := #caller_location) {
resize(&b.buf, len(buf), loc=loc)
copy(b.buf[:], buf)
}
buffer_init_string :: proc(b: ^Buffer, s: string) {
resize(&b.buf, len(s))
buffer_init_string :: proc(b: ^Buffer, s: string, loc := #caller_location) {
resize(&b.buf, len(s), loc=loc)
copy(b.buf[:], s)
}
buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) {
buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator, loc := #caller_location) {
if b.buf == nil {
b.buf = make([dynamic]byte, len, cap, allocator)
b.buf = make([dynamic]byte, len, cap, allocator, loc)
return
}
@@ -96,28 +96,28 @@ buffer_truncate :: proc(b: ^Buffer, n: int) {
}
@(private)
_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) {
_buffer_try_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) -> (int, bool) {
if l := len(b.buf); n <= cap(b.buf)-l {
resize(&b.buf, l+n)
resize(&b.buf, l+n, loc=loc)
return l, true
}
return 0, false
}
@(private)
_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
_buffer_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) -> int {
m := buffer_length(b)
if m == 0 && b.off != 0 {
buffer_reset(b)
}
if i, ok := _buffer_try_grow(b, n); ok {
if i, ok := _buffer_try_grow(b, n, loc=loc); ok {
return i
}
if b.buf == nil && n <= SMALL_BUFFER_SIZE {
// Fixes #2756 by preserving allocator if already set on Buffer via init_buffer_allocator
reserve(&b.buf, SMALL_BUFFER_SIZE)
resize(&b.buf, n)
reserve(&b.buf, SMALL_BUFFER_SIZE, loc=loc)
resize(&b.buf, n, loc=loc)
return 0
}
@@ -127,31 +127,31 @@ _buffer_grow :: proc(b: ^Buffer, n: int) -> int {
} else if c > max(int) - c - n {
panic("bytes.Buffer: too large")
} else {
resize(&b.buf, 2*c + n)
resize(&b.buf, 2*c + n, loc=loc)
copy(b.buf[:], b.buf[b.off:])
}
b.off = 0
resize(&b.buf, m+n)
resize(&b.buf, m+n, loc=loc)
return m
}
buffer_grow :: proc(b: ^Buffer, n: int) {
buffer_grow :: proc(b: ^Buffer, n: int, loc := #caller_location) {
if n < 0 {
panic("bytes.buffer_grow: negative count")
}
m := _buffer_grow(b, n)
resize(&b.buf, m)
m := _buffer_grow(b, n, loc=loc)
resize(&b.buf, m, loc=loc)
}
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int, loc := #caller_location) -> (n: int, err: io.Error) {
b.last_read = .Invalid
if offset < 0 {
err = .Invalid_Offset
return
}
_, ok := _buffer_try_grow(b, offset+len(p))
_, ok := _buffer_try_grow(b, offset+len(p), loc=loc)
if !ok {
_ = _buffer_grow(b, offset+len(p))
_ = _buffer_grow(b, offset+len(p), loc=loc)
}
if len(b.buf) <= offset {
return 0, .Short_Write
@@ -160,47 +160,47 @@ buffer_write_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.
}
buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
buffer_write :: proc(b: ^Buffer, p: []byte, loc := #caller_location) -> (n: int, err: io.Error) {
b.last_read = .Invalid
m, ok := _buffer_try_grow(b, len(p))
m, ok := _buffer_try_grow(b, len(p), loc=loc)
if !ok {
m = _buffer_grow(b, len(p))
m = _buffer_grow(b, len(p), loc=loc)
}
return copy(b.buf[m:], p), nil
}
buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
return buffer_write(b, ([^]byte)(ptr)[:size])
buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int, loc := #caller_location) -> (n: int, err: io.Error) {
return buffer_write(b, ([^]byte)(ptr)[:size], loc=loc)
}
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
buffer_write_string :: proc(b: ^Buffer, s: string, loc := #caller_location) -> (n: int, err: io.Error) {
b.last_read = .Invalid
m, ok := _buffer_try_grow(b, len(s))
m, ok := _buffer_try_grow(b, len(s), loc=loc)
if !ok {
m = _buffer_grow(b, len(s))
m = _buffer_grow(b, len(s), loc=loc)
}
return copy(b.buf[m:], s), nil
}
buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error {
buffer_write_byte :: proc(b: ^Buffer, c: byte, loc := #caller_location) -> io.Error {
b.last_read = .Invalid
m, ok := _buffer_try_grow(b, 1)
m, ok := _buffer_try_grow(b, 1, loc=loc)
if !ok {
m = _buffer_grow(b, 1)
m = _buffer_grow(b, 1, loc=loc)
}
b.buf[m] = c
return nil
}
buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) {
buffer_write_rune :: proc(b: ^Buffer, r: rune, loc := #caller_location) -> (n: int, err: io.Error) {
if r < utf8.RUNE_SELF {
buffer_write_byte(b, byte(r))
buffer_write_byte(b, byte(r), loc=loc)
return 1, nil
}
b.last_read = .Invalid
m, ok := _buffer_try_grow(b, utf8.UTF_MAX)
m, ok := _buffer_try_grow(b, utf8.UTF_MAX, loc=loc)
if !ok {
m = _buffer_grow(b, utf8.UTF_MAX)
m = _buffer_grow(b, utf8.UTF_MAX, loc=loc)
}
res: [4]byte
res, n = utf8.encode_rune(r)
+1 -14
View File
@@ -34,20 +34,7 @@ when ODIN_OS == .Windows {
SIGTERM :: 15
}
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
SIG_ERR :: rawptr(~uintptr(0))
SIG_DFL :: rawptr(uintptr(0))
SIG_IGN :: rawptr(uintptr(1))
SIGABRT :: 6
SIGFPE :: 8
SIGILL :: 4
SIGINT :: 2
SIGSEGV :: 11
SIGTERM :: 15
}
when ODIN_OS == .Darwin {
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Darwin {
SIG_ERR :: rawptr(~uintptr(0))
SIG_DFL :: rawptr(uintptr(0))
SIG_IGN :: rawptr(uintptr(1))
+8 -6
View File
@@ -102,10 +102,12 @@ when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
SEEK_END :: 2
foreign libc {
stderr: ^FILE
stdin: ^FILE
stdout: ^FILE
__sF: [3]FILE
}
stdin: ^FILE = &__sF[0]
stdout: ^FILE = &__sF[1]
stderr: ^FILE = &__sF[2]
}
when ODIN_OS == .FreeBSD {
@@ -127,9 +129,9 @@ when ODIN_OS == .FreeBSD {
SEEK_END :: 2
foreign libc {
stderr: ^FILE
stdin: ^FILE
stdout: ^FILE
@(link_name="__stderrp") stderr: ^FILE
@(link_name="__stdinp") stdin: ^FILE
@(link_name="__stdoutp") stdout: ^FILE
}
}
+3 -3
View File
@@ -34,13 +34,13 @@ COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 2
*/
when size_of(uintptr) == 8 {
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
// which is GZIP and PKZIP's max payload size.
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
// which is GZIP and PKZIP's max payload size.
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
} else {
// For 32-bit platforms, we set the default max buffer size to 512 MiB.
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
}
+4 -7
View File
@@ -98,7 +98,7 @@ decompress_slice_to_output_buffer :: proc(input: []u8, output: []u8, model := DE
validate_model(model) or_return
for inp < inp_end {
val := transmute(i8)input[inp]
val := i8(input[inp])
mark := int(-1)
for val < 0 {
@@ -274,12 +274,9 @@ compress_string_to_buffer :: proc(input: string, output: []u8, model := DEFAULT_
out_ptr := raw_data(output[out:])
switch pack.bytes_packed {
case 4:
intrinsics.unaligned_store(transmute(^u32)out_ptr, code)
case 2:
intrinsics.unaligned_store(transmute(^u16)out_ptr, u16(code))
case 1:
intrinsics.unaligned_store(transmute(^u8)out_ptr, u8(code))
case 4: intrinsics.unaligned_store((^u32)(out_ptr), code)
case 2: intrinsics.unaligned_store((^u16)(out_ptr), u16(code))
case 1: intrinsics.unaligned_store( (^u8)(out_ptr), u8(code))
case:
return out, .Unknown_Compression_Method
}
+1 -1
View File
@@ -87,7 +87,7 @@ init_cmp :: proc(
init_ordered :: proc(
t: ^$T/Tree($Value),
node_allocator := context.allocator,
) where intrinsics.type_is_ordered_numeric(Value) {
) where intrinsics.type_is_ordered(Value) {
init_cmp(t, slice.cmp_proc(Value), node_allocator)
}
+6 -3
View File
@@ -210,8 +210,11 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, set_to: bool = true, allocator
ba.max_index = max(idx, ba.max_index)
if set_to{ ba.bits[leg_index] |= 1 << uint(bit_index) }
else { ba.bits[leg_index] &= ~(1 << uint(bit_index)) }
if set_to {
ba.bits[leg_index] |= 1 << uint(bit_index)
} else {
ba.bits[leg_index] &~= 1 << uint(bit_index)
}
return true
}
@@ -253,7 +256,7 @@ Inputs:
- index: Which bit in the array
*/
unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
b.bits[bit >> INDEX_SHIFT] &= ~(1 << uint(bit & INDEX_MASK))
b.bits[bit >> INDEX_SHIFT] &~= 1 << uint(bit & INDEX_MASK)
}
/*
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
+1 -2
View File
@@ -70,8 +70,7 @@ set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Alloc
if c.count == c.capacity {
e = c.tail
_remove_node(c, e)
}
else {
} else {
c.count += 1
e = new(Node(Key, Value), c.node_allocator) or_return
}
+2 -2
View File
@@ -189,7 +189,7 @@ pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
return
}
// Push multiple elements to the front of the queue
// Push multiple elements to the back of the queue
push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> (ok: bool, err: runtime.Allocator_Error) {
n := uint(builtin.len(elems))
if space(q^) < int(n) {
@@ -241,7 +241,7 @@ clear :: proc(q: ^$Q/Queue($T)) {
}
// Internal growinh procedure
// Internal growing procedure
_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> runtime.Allocator_Error {
new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
n := uint(builtin.len(q.data))
+2 -2
View File
@@ -29,7 +29,7 @@ Tree :: struct($Key: typeid, $Value: typeid) {
_root: ^Node(Key, Value),
_node_allocator: runtime.Allocator,
_cmp_fn: proc(Key, Key) -> Ordering,
_cmp_fn: proc(Key, Key) -> Ordering,
_size: int,
}
@@ -79,7 +79,7 @@ init_cmp :: proc(t: ^$T/Tree($Key, $Value), cmp_fn: proc(a, b: Key) -> Ordering,
// init_ordered initializes a tree containing ordered keys, with
// a comparison function that results in an ascending order sort.
init_ordered :: proc(t: ^$T/Tree($Key, $Value), node_allocator := context.allocator) where intrinsics.type_is_ordered_numeric(Key) {
init_ordered :: proc(t: ^$T/Tree($Key, $Value), node_allocator := context.allocator) where intrinsics.type_is_ordered(Key) {
init_cmp(t, slice.cmp_proc(Key), node_allocator)
}
+4 -4
View File
@@ -119,20 +119,20 @@ consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_l
}
ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
runtime.bounds_check_error_loc(loc, index, a.len)
if index+1 < a.len {
runtime.bounds_check_error_loc(loc, index, a.len)
if index+1 < a.len {
copy(a.data[index:], a.data[index+1:])
}
a.len -= 1
}
unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
runtime.bounds_check_error_loc(loc, index, a.len)
runtime.bounds_check_error_loc(loc, index, a.len)
n := a.len-1
if index != n {
a.data[index] = a.data[n]
}
a.len -= 1
a.len -= 1
}
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
@@ -61,7 +61,7 @@ add_dependency :: proc(sorter: ^$S/Sorter($K), key, dependency: K) -> bool {
}
find.dependents[key] = true
find = &sorter.relations[key]
find = &sorter.relations[key]
if find == nil {
find = map_insert(&sorter.relations, key, make_relations(sorter))
}
+2 -2
View File
@@ -110,7 +110,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
if len(b) != 32 {
intrinsics.trap()
}
b_ := transmute(^[32]byte)(raw_data(b))
b_ := (^[32]byte)(raw_data(b))
// Do the work in a scratch element, so that ge is unchanged on
// failure.
@@ -169,7 +169,7 @@ ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
if len(dst) != 32 {
intrinsics.trap()
}
dst_ := transmute(^[32]byte)(raw_data(dst))
dst_ := (^[32]byte)(raw_data(dst))
// Convert the element to affine (x, y) representation.
x, y, z_inv: field.Tight_Field_Element = ---, ---, ---
@@ -28,7 +28,7 @@ sc_set_bytes :: proc "contextless" (sc: ^Scalar, b: []byte) -> bool {
if len(b) != 32 {
intrinsics.trap()
}
b_ := transmute(^[32]byte)(raw_data(b))
b_ := (^[32]byte)(raw_data(b))
return field.fe_from_bytes(sc, b_)
}
@@ -36,7 +36,7 @@ sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
if len(b) != 32 {
intrinsics.trap()
}
b_ := transmute(^[32]byte)(raw_data(b))
b_ := (^[32]byte)(raw_data(b))
field.fe_from_bytes_rfc8032(sc, b_)
}
@@ -6,13 +6,13 @@ import "core:mem"
fe_relax_cast :: #force_inline proc "contextless" (
arg1: ^Tight_Field_Element,
) -> ^Loose_Field_Element {
return transmute(^Loose_Field_Element)(arg1)
return (^Loose_Field_Element)(arg1)
}
fe_tighten_cast :: #force_inline proc "contextless" (
arg1: ^Loose_Field_Element,
) -> ^Tight_Field_Element {
return transmute(^Tight_Field_Element)(arg1)
return (^Tight_Field_Element)(arg1)
}
fe_clear :: proc "contextless" (
+2 -2
View File
@@ -7,13 +7,13 @@ import "core:mem"
fe_relax_cast :: #force_inline proc "contextless" (
arg1: ^Tight_Field_Element,
) -> ^Loose_Field_Element {
return transmute(^Loose_Field_Element)(arg1)
return (^Loose_Field_Element)(arg1)
}
fe_tighten_cast :: #force_inline proc "contextless" (
arg1: ^Loose_Field_Element,
) -> ^Tight_Field_Element {
return transmute(^Tight_Field_Element)(arg1)
return (^Tight_Field_Element)(arg1)
}
fe_from_bytes :: #force_inline proc "contextless" (
+22
View File
@@ -4,6 +4,7 @@ helper routines.
*/
package crypto
import "base:runtime"
import "core:mem"
// compare_constant_time returns 1 iff a and b are equal, 0 otherwise.
@@ -58,3 +59,24 @@ rand_bytes :: proc (dst: []byte) {
_rand_bytes(dst)
}
random_generator :: proc() -> runtime.Random_Generator {
return {
procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) {
switch mode {
case .Read:
rand_bytes(p)
case .Reset:
// do nothing
case .Query_Info:
if len(p) != size_of(runtime.Random_Generator_Query_Info) {
return
}
info := (^runtime.Random_Generator_Query_Info)(raw_data(p))
info^ += {.Uniform, .Cryptographic, .External_Entropy}
}
},
data = nil,
}
}
+5 -5
View File
@@ -61,7 +61,7 @@ init_256 :: proc(ctx: ^Context, key, domain_sep: []byte) {
update :: proc(ctx: ^Context, data: []byte) {
assert(ctx.is_initialized)
shake.write(transmute(^shake.Context)(ctx), data)
shake.write((^shake.Context)(ctx), data)
}
// final finalizes the Context, writes the tag to dst, and calls reset
@@ -75,7 +75,7 @@ final :: proc(ctx: ^Context, dst: []byte) {
panic("crypto/kmac: invalid KMAC tag_size, too short")
}
_sha3.final_cshake(transmute(^_sha3.Context)(ctx), dst)
_sha3.final_cshake((^_sha3.Context)(ctx), dst)
}
// clone clones the Context other into ctx.
@@ -84,7 +84,7 @@ clone :: proc(ctx, other: ^Context) {
return
}
shake.clone(transmute(^shake.Context)(ctx), transmute(^shake.Context)(other))
shake.clone((^shake.Context)(ctx), (^shake.Context)(other))
}
// reset sanitizes the Context. The Context must be re-initialized to
@@ -94,7 +94,7 @@ reset :: proc(ctx: ^Context) {
return
}
shake.reset(transmute(^shake.Context)(ctx))
shake.reset((^shake.Context)(ctx))
}
@(private)
@@ -107,7 +107,7 @@ _init_kmac :: proc(ctx: ^Context, key, s: []byte, sec_strength: int) {
panic("crypto/kmac: invalid KMAC key, too short")
}
ctx_ := transmute(^_sha3.Context)(ctx)
ctx_ := (^_sha3.Context)(ctx)
_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
_sha3.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
}
+5 -5
View File
@@ -66,12 +66,12 @@ init_512 :: proc(ctx: ^Context) {
@(private)
_init :: proc(ctx: ^Context) {
ctx.dsbyte = _sha3.DS_KECCAK
_sha3.init(transmute(^_sha3.Context)(ctx))
_sha3.init((^_sha3.Context)(ctx))
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
_sha3.update(transmute(^_sha3.Context)(ctx), data)
_sha3.update((^_sha3.Context)(ctx), data)
}
// final finalizes the Context, writes the digest to hash, and calls
@@ -80,16 +80,16 @@ update :: proc(ctx: ^Context, data: []byte) {
// 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)
_sha3.final((^_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))
_sha3.clone((^_sha3.Context)(ctx), (^_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))
_sha3.reset((^_sha3.Context)(ctx))
}
+2 -2
View File
@@ -11,7 +11,7 @@ HAS_RAND_BYTES :: true
_rand_bytes :: proc(dst: []byte) {
err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst))
if err != .Success {
msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err))
panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg))
msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err))
fmt.panicf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg)
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ _rand_bytes :: proc (dst: []byte) {
// All other failures are things that should NEVER happen
// unless the kernel interface changes (ie: the Linux
// developers break userland).
panic(fmt.tprintf("crypto: getrandom failed: %v", errno))
fmt.panicf("crypto: getrandom failed: %v", errno)
}
l -= n_read
dst = dst[n_read:]
+10 -10
View File
@@ -11,16 +11,16 @@ _rand_bytes :: proc(dst: []byte) {
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
if ret != os.ERROR_NONE {
switch ret {
case os.ERROR_INVALID_HANDLE:
// The handle to the first parameter is invalid.
// This should not happen here, since we explicitly pass nil to it
panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm")
case os.ERROR_INVALID_PARAMETER:
// One of the parameters was invalid
panic("crypto: BCryptGenRandom Invalid parameter")
case:
// Unknown error
panic(fmt.tprintf("crypto: BCryptGenRandom failed: %d\n", ret))
case os.ERROR_INVALID_HANDLE:
// The handle to the first parameter is invalid.
// This should not happen here, since we explicitly pass nil to it
panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm")
case os.ERROR_INVALID_PARAMETER:
// One of the parameters was invalid
panic("crypto: BCryptGenRandom Invalid parameter")
case:
// Unknown error
fmt.panicf("crypto: BCryptGenRandom failed: %d\n", ret)
}
}
}
+3 -3
View File
@@ -112,7 +112,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
return false
}
b_ := transmute(^[32]byte)(raw_data(b))
b_ := (^[32]byte)(raw_data(b))
s: field.Tight_Field_Element = ---
defer field.fe_clear(&s)
@@ -297,7 +297,7 @@ ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
// 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))
dst_ := (^[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})
@@ -417,7 +417,7 @@ ge_is_identity :: proc(ge: ^Group_Element) -> int {
@(private)
ge_map :: proc "contextless" (ge: ^Group_Element, b: []byte) {
b_ := transmute(^[32]byte)(raw_data(b))
b_ := (^[32]byte)(raw_data(b))
// The MAP function is defined on 32-byte strings as:
//
@@ -46,7 +46,7 @@ sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
panic("crypto/ristretto255: invalid wide input size")
}
b_ := transmute(^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
b_ := (^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
grp.sc_set_bytes_wide(sc, b_)
}
+5 -5
View File
@@ -68,12 +68,12 @@ init_512 :: proc(ctx: ^Context) {
@(private)
_init :: proc(ctx: ^Context) {
ctx.dsbyte = _sha3.DS_SHA3
_sha3.init(transmute(^_sha3.Context)(ctx))
_sha3.init((^_sha3.Context)(ctx))
}
// update adds more data to the Context.
update :: proc(ctx: ^Context, data: []byte) {
_sha3.update(transmute(^_sha3.Context)(ctx), data)
_sha3.update((^_sha3.Context)(ctx), data)
}
// final finalizes the Context, writes the digest to hash, and calls
@@ -82,16 +82,16 @@ update :: proc(ctx: ^Context, data: []byte) {
// 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)
_sha3.final((^_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))
_sha3.clone((^_sha3.Context)(ctx), (^_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))
_sha3.reset((^_sha3.Context)(ctx))
}
+8 -8
View File
@@ -24,35 +24,35 @@ Context :: distinct _sha3.Context
// init_128 initializes a Context for SHAKE128.
init_128 :: proc(ctx: ^Context) {
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 128)
_sha3.init_cshake((^_sha3.Context)(ctx), nil, nil, 128)
}
// init_256 initializes a Context for SHAKE256.
init_256 :: proc(ctx: ^Context) {
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 256)
_sha3.init_cshake((^_sha3.Context)(ctx), nil, nil, 256)
}
// 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)
_sha3.init_cshake((^_sha3.Context)(ctx), nil, domain_sep, 128)
}
// 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)
_sha3.init_cshake((^_sha3.Context)(ctx), nil, domain_sep, 256)
}
// 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)
_sha3.update((^_sha3.Context)(ctx), data)
}
// 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)
ctx_ := (^_sha3.Context)(ctx)
if !ctx.is_finalized {
_sha3.shake_xof(ctx_)
}
@@ -62,11 +62,11 @@ read :: proc(ctx: ^Context, dst: []byte) {
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
_sha3.clone((^_sha3.Context)(ctx), (^_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))
_sha3.reset((^_sha3.Context)(ctx))
}
+7 -7
View File
@@ -13,19 +13,19 @@ 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)
_sha3.init_cshake((^_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)
_sha3.init_cshake((^_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)
_, _ = _sha3.encode_string((^_sha3.Context)(ctx), data)
}
// final finalizes the Context, writes the digest to hash, and calls
@@ -34,7 +34,7 @@ write_element :: proc(ctx: ^Context, data: []byte) {
// 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)
_sha3.final_cshake((^_sha3.Context)(ctx), hash, finalize_clone)
}
// read reads output from the TupleHashXOF instance. There is no practical
@@ -42,7 +42,7 @@ final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
// 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)
ctx_ := (^_sha3.Context)(ctx)
if !ctx.is_finalized {
_sha3.encode_byte_len(ctx_, 0, false) // right_encode
_sha3.shake_xof(ctx_)
@@ -53,13 +53,13 @@ read :: proc(ctx: ^Context, dst: []byte) {
// clone clones the Context other into ctx.
clone :: proc(ctx, other: ^Context) {
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
_sha3.clone((^_sha3.Context)(ctx), (^_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))
_sha3.reset((^_sha3.Context)(ctx))
}
@(private)
+1 -1
View File
@@ -78,7 +78,7 @@ _Context :: struct {
@(private="package")
_init :: proc(ctx: ^Context) -> (ok: bool) {
defer if !ok do destroy(ctx)
defer if !ok { destroy(ctx) }
ctx.impl.state = backtrace_create_state("odin-debug-trace", 1, nil, ctx)
return ctx.impl.state != nil
+4 -2
View File
@@ -1,6 +1,8 @@
//+build !windows !linux !darwin
package debug_trace
import "base:runtime"
_Context :: struct {
}
@@ -10,9 +12,9 @@ _init :: proc(ctx: ^Context) -> (ok: bool) {
_destroy :: proc(ctx: ^Context) -> bool {
return true
}
_frames :: proc(ctx: ^Context, skip: uint, allocator: runtime.Allocator) -> []Frame {
_frames :: proc(ctx: ^Context, skip: uint, frames_buffer: []Frame) -> []Frame {
return nil
}
_resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (result: runtime.Source_Code_Location) {
_resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (result: Frame_Location) {
return
}
+137
View File
@@ -0,0 +1,137 @@
package ansi
BEL :: "\a" // Bell
BS :: "\b" // Backspace
ESC :: "\e" // Escape
// Fe Escape sequences
CSI :: ESC + "[" // Control Sequence Introducer
OSC :: ESC + "]" // Operating System Command
ST :: ESC + "\\" // String Terminator
// CSI sequences
CUU :: "A" // Cursor Up
CUD :: "B" // Cursor Down
CUF :: "C" // Cursor Forward
CUB :: "D" // Cursor Back
CNL :: "E" // Cursor Next Line
CPL :: "F" // Cursor Previous Line
CHA :: "G" // Cursor Horizontal Absolute
CUP :: "H" // Cursor Position
ED :: "J" // Erase in Display
EL :: "K" // Erase in Line
SU :: "S" // Scroll Up
SD :: "T" // Scroll Down
HVP :: "f" // Horizontal Vertical Position
SGR :: "m" // Select Graphic Rendition
AUX_ON :: "5i" // AUX Port On
AUX_OFF :: "4i" // AUX Port Off
DSR :: "6n" // Device Status Report
// CSI: private sequences
SCP :: "s" // Save Current Cursor Position
RCP :: "u" // Restore Saved Cursor Position
DECAWM_ON :: "?7h" // Auto Wrap Mode (Enabled)
DECAWM_OFF :: "?7l" // Auto Wrap Mode (Disabled)
DECTCEM_SHOW :: "?25h" // Text Cursor Enable Mode (Visible)
DECTCEM_HIDE :: "?25l" // Text Cursor Enable Mode (Invisible)
// SGR sequences
RESET :: "0"
BOLD :: "1"
FAINT :: "2"
ITALIC :: "3" // Not widely supported.
UNDERLINE :: "4"
BLINK_SLOW :: "5"
BLINK_RAPID :: "6" // Not widely supported.
INVERT :: "7" // Also known as reverse video.
HIDE :: "8" // Not widely supported.
STRIKE :: "9"
FONT_PRIMARY :: "10"
FONT_ALT1 :: "11"
FONT_ALT2 :: "12"
FONT_ALT3 :: "13"
FONT_ALT4 :: "14"
FONT_ALT5 :: "15"
FONT_ALT6 :: "16"
FONT_ALT7 :: "17"
FONT_ALT8 :: "18"
FONT_ALT9 :: "19"
FONT_FRAKTUR :: "20" // Rarely supported.
UNDERLINE_DOUBLE :: "21" // May be interpreted as "disable bold."
NO_BOLD_FAINT :: "22"
NO_ITALIC_BLACKLETTER :: "23"
NO_UNDERLINE :: "24"
NO_BLINK :: "25"
PROPORTIONAL_SPACING :: "26"
NO_REVERSE :: "27"
NO_HIDE :: "28"
NO_STRIKE :: "29"
FG_BLACK :: "30"
FG_RED :: "31"
FG_GREEN :: "32"
FG_YELLOW :: "33"
FG_BLUE :: "34"
FG_MAGENTA :: "35"
FG_CYAN :: "36"
FG_WHITE :: "37"
FG_COLOR :: "38"
FG_COLOR_8_BIT :: "38;5" // Followed by ";n" where n is in 0..=255
FG_COLOR_24_BIT :: "38;2" // Followed by ";r;g;b" where r,g,b are in 0..=255
FG_DEFAULT :: "39"
BG_BLACK :: "40"
BG_RED :: "41"
BG_GREEN :: "42"
BG_YELLOW :: "43"
BG_BLUE :: "44"
BG_MAGENTA :: "45"
BG_CYAN :: "46"
BG_WHITE :: "47"
BG_COLOR :: "48"
BG_COLOR_8_BIT :: "48;5" // Followed by ";n" where n is in 0..=255
BG_COLOR_24_BIT :: "48;2" // Followed by ";r;g;b" where r,g,b are in 0..=255
BG_DEFAULT :: "49"
NO_PROPORTIONAL_SPACING :: "50"
FRAMED :: "51"
ENCIRCLED :: "52"
OVERLINED :: "53"
NO_FRAME_ENCIRCLE :: "54"
NO_OVERLINE :: "55"
// SGR: non-standard bright colors
FG_BRIGHT_BLACK :: "90" // Also known as grey.
FG_BRIGHT_RED :: "91"
FG_BRIGHT_GREEN :: "92"
FG_BRIGHT_YELLOW :: "93"
FG_BRIGHT_BLUE :: "94"
FG_BRIGHT_MAGENTA :: "95"
FG_BRIGHT_CYAN :: "96"
FG_BRIGHT_WHITE :: "97"
BG_BRIGHT_BLACK :: "100" // Also known as grey.
BG_BRIGHT_RED :: "101"
BG_BRIGHT_GREEN :: "102"
BG_BRIGHT_YELLOW :: "103"
BG_BRIGHT_BLUE :: "104"
BG_BRIGHT_MAGENTA :: "105"
BG_BRIGHT_CYAN :: "106"
BG_BRIGHT_WHITE :: "107"
// Fp Escape sequences
DECSC :: ESC + "7" // DEC Save Cursor
DECRC :: ESC + "8" // DEC Restore Cursor
// OSC sequences
WINDOW_TITLE :: "2" // Followed by ";<text>" ST.
HYPERLINK :: "8" // Followed by ";[params];<URI>" ST. Closed by OSC HYPERLINK ";;" ST.
CLIPBOARD :: "52" // Followed by ";c;<Base64-encoded string>" ST.
+20
View File
@@ -0,0 +1,20 @@
/*
package ansi implements constant references to many widely-supported ANSI
escape codes, primarily used in terminal emulators for enhanced graphics, such
as colors, text styling, and animated displays.
For example, you can print out a line of cyan text like this:
fmt.println(ansi.CSI + ansi.FG_CYAN + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR)
Multiple SGR (Select Graphic Rendition) codes can be joined by semicolons:
fmt.println(ansi.CSI + ansi.BOLD + ";" + ansi.FG_BLUE + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR)
If your terminal supports 24-bit true color mode, you can also do this:
fmt.println(ansi.CSI + ansi.FG_COLOR_24_BIT + ";0;255;255" + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR)
For more information, see:
1. https://en.wikipedia.org/wiki/ANSI_escape_code
2. https://www.vt100.net/docs/vt102-ug/chapter5.html
3. https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
*/
package ansi
+116 -116
View File
@@ -8,141 +8,141 @@ package encoding_base32
// truncate it from the encoded output.
ENC_TABLE := [32]byte {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7',
}
PADDING :: '='
DEC_TABLE := [?]u8 {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
}
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
out_length := (len(data) + 4) / 5 * 8
out := make([]byte, out_length)
_encode(out, data)
return string(out)
out_length := (len(data) + 4) / 5 * 8
out := make([]byte, out_length)
_encode(out, data)
return string(out)
}
@private
_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
out := out
data := data
out := out
data := data
for len(data) > 0 {
carry: byte
switch len(data) {
case:
out[7] = ENC_TABLE[data[4] & 0x1f]
carry = data[4] >> 5
fallthrough
case 4:
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
carry = data[3] >> 7
fallthrough
case 3:
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
carry = (data[2] >> 4) & 0x1f
fallthrough
case 2:
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
carry = (data[1] >> 6) & 0x1f
fallthrough
case 1:
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
out[0] = ENC_TABLE[data[0] >> 3]
}
for len(data) > 0 {
carry: byte
switch len(data) {
case:
out[7] = ENC_TABLE[data[4] & 0x1f]
carry = data[4] >> 5
fallthrough
case 4:
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
carry = data[3] >> 7
fallthrough
case 3:
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
carry = (data[2] >> 4) & 0x1f
fallthrough
case 2:
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
carry = (data[1] >> 6) & 0x1f
fallthrough
case 1:
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
out[0] = ENC_TABLE[data[0] >> 3]
}
if len(data) < 5 {
out[7] = byte(PADDING)
if len(data) < 4 {
out[6] = byte(PADDING)
out[5] = byte(PADDING)
if len(data) < 3 {
out[4] = byte(PADDING)
if len(data) < 2 {
out[3] = byte(PADDING)
out[2] = byte(PADDING)
}
}
}
break
}
data = data[5:]
out = out[8:]
}
if len(data) < 5 {
out[7] = byte(PADDING)
if len(data) < 4 {
out[6] = byte(PADDING)
out[5] = byte(PADDING)
if len(data) < 3 {
out[4] = byte(PADDING)
if len(data) < 2 {
out[3] = byte(PADDING)
out[2] = byte(PADDING)
}
}
}
break
}
data = data[5:]
out = out[8:]
}
}
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
if len(data) == 0 {
return nil
}
if len(data) == 0 {
return nil
}
outi := 0
data := data
outi := 0
data := data
out := make([]byte, len(data) / 8 * 5, allocator)
end := false
for len(data) > 0 && !end {
dbuf : [8]byte
dlen := 8
out := make([]byte, len(data) / 8 * 5, allocator)
end := false
for len(data) > 0 && !end {
dbuf : [8]byte
dlen := 8
for j := 0; j < 8; {
if len(data) == 0 {
dlen, end = j, true
break
}
input := data[0]
data = data[1:]
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
assert(!(len(data) + j < 8 - 1), "Corrupted input")
for k := 0; k < 8-1-j; k +=1 {
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
}
dlen, end = j, true
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
break
}
dbuf[j] = DEC_TABLE[input]
assert(dbuf[j] != 0xff, "Corrupted input")
j += 1
}
for j := 0; j < 8; {
if len(data) == 0 {
dlen, end = j, true
break
}
input := data[0]
data = data[1:]
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
assert(!(len(data) + j < 8 - 1), "Corrupted input")
for k := 0; k < 8-1-j; k +=1 {
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
}
dlen, end = j, true
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
break
}
dbuf[j] = DEC_TABLE[input]
assert(dbuf[j] != 0xff, "Corrupted input")
j += 1
}
switch dlen {
case 8:
out[outi + 4] = dbuf[6] << 5 | dbuf[7]
fallthrough
case 7:
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
fallthrough
case 5:
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
fallthrough
case 4:
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
fallthrough
case 2:
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
}
outi += 5
}
return out
switch dlen {
case 8:
out[outi + 4] = dbuf[6] << 5 | dbuf[7]
fallthrough
case 7:
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
fallthrough
case 5:
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
fallthrough
case 4:
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
fallthrough
case 2:
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
}
outi += 5
}
return out
}
+2 -2
View File
@@ -320,8 +320,8 @@ to_diagnostic_format :: proc {
// Turns the given CBOR value into a human-readable string.
// See docs on the proc group `diagnose` for more info.
to_diagnostic_format_string :: proc(val: Value, padding := 0, allocator := context.allocator) -> (string, mem.Allocator_Error) #optional_allocator_error {
b := strings.builder_make(allocator)
to_diagnostic_format_string :: proc(val: Value, padding := 0, allocator := context.allocator, loc := #caller_location) -> (string, mem.Allocator_Error) #optional_allocator_error {
b := strings.builder_make(allocator, loc)
w := strings.to_stream(&b)
err := to_diagnostic_format_writer(w, val, padding)
if err == .EOF {
+70 -69
View File
@@ -95,24 +95,25 @@ decode :: decode_from
// Decodes the given string as CBOR.
// See docs on the proc group `decode` for more information.
decode_from_string :: proc(s: string, flags: Decoder_Flags = {}, allocator := context.allocator) -> (v: Value, err: Decode_Error) {
decode_from_string :: proc(s: string, flags: Decoder_Flags = {}, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
r: strings.Reader
strings.reader_init(&r, s)
return decode_from_reader(strings.reader_to_stream(&r), flags, allocator)
return decode_from_reader(strings.reader_to_stream(&r), flags, allocator, loc)
}
// Reads a CBOR value from the given reader.
// See docs on the proc group `decode` for more information.
decode_from_reader :: proc(r: io.Reader, flags: Decoder_Flags = {}, allocator := context.allocator) -> (v: Value, err: Decode_Error) {
decode_from_reader :: proc(r: io.Reader, flags: Decoder_Flags = {}, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
return decode_from_decoder(
Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r },
allocator=allocator,
loc = loc,
)
}
// Reads a CBOR value from the given decoder.
// See docs on the proc group `decode` for more information.
decode_from_decoder :: proc(d: Decoder, allocator := context.allocator) -> (v: Value, err: Decode_Error) {
decode_from_decoder :: proc(d: Decoder, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
context.allocator = allocator
d := d
@@ -121,13 +122,13 @@ decode_from_decoder :: proc(d: Decoder, allocator := context.allocator) -> (v: V
d.max_pre_alloc = DEFAULT_MAX_PRE_ALLOC
}
v, err = _decode_from_decoder(d)
v, err = _decode_from_decoder(d, {}, allocator, loc)
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
if err == .EOF { err = .Unexpected_EOF }
return
}
_decode_from_decoder :: proc(d: Decoder, hdr: Header = Header(0)) -> (v: Value, err: Decode_Error) {
_decode_from_decoder :: proc(d: Decoder, hdr: Header = Header(0), allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
hdr := hdr
r := d.reader
if hdr == Header(0) { hdr = _decode_header(r) or_return }
@@ -161,11 +162,11 @@ _decode_from_decoder :: proc(d: Decoder, hdr: Header = Header(0)) -> (v: Value,
switch maj {
case .Unsigned: return _decode_tiny_u8(add)
case .Negative: return Negative_U8(_decode_tiny_u8(add) or_return), nil
case .Bytes: return _decode_bytes_ptr(d, add)
case .Text: return _decode_text_ptr(d, add)
case .Array: return _decode_array_ptr(d, add)
case .Map: return _decode_map_ptr(d, add)
case .Tag: return _decode_tag_ptr(d, add)
case .Bytes: return _decode_bytes_ptr(d, add, .Bytes, allocator, loc)
case .Text: return _decode_text_ptr(d, add, allocator, loc)
case .Array: return _decode_array_ptr(d, add, allocator, loc)
case .Map: return _decode_map_ptr(d, add, allocator, loc)
case .Tag: return _decode_tag_ptr(d, add, allocator, loc)
case .Other: return _decode_tiny_simple(add)
case: return nil, .Bad_Major
}
@@ -203,27 +204,27 @@ encode :: encode_into
// Encodes the CBOR value into binary CBOR allocated on the given allocator.
// See the docs on the proc group `encode_into` for more info.
encode_into_bytes :: proc(v: Value, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (data: []byte, err: Encode_Error) {
b := strings.builder_make(allocator) or_return
encode_into_bytes :: proc(v: Value, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (data: []byte, err: Encode_Error) {
b := strings.builder_make(allocator, loc) or_return
encode_into_builder(&b, v, flags, temp_allocator) or_return
return b.buf[:], nil
}
// Encodes the CBOR value into binary CBOR written to the given builder.
// See the docs on the proc group `encode_into` for more info.
encode_into_builder :: proc(b: ^strings.Builder, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Encode_Error {
return encode_into_writer(strings.to_stream(b), v, flags, temp_allocator)
encode_into_builder :: proc(b: ^strings.Builder, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Encode_Error {
return encode_into_writer(strings.to_stream(b), v, flags, temp_allocator, loc=loc)
}
// Encodes the CBOR value into binary CBOR written to the given writer.
// See the docs on the proc group `encode_into` for more info.
encode_into_writer :: proc(w: io.Writer, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Encode_Error {
return encode_into_encoder(Encoder{flags, w, temp_allocator}, v)
encode_into_writer :: proc(w: io.Writer, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Encode_Error {
return encode_into_encoder(Encoder{flags, w, temp_allocator}, v, loc=loc)
}
// Encodes the CBOR value into binary CBOR written to the given encoder.
// See the docs on the proc group `encode_into` for more info.
encode_into_encoder :: proc(e: Encoder, v: Value) -> Encode_Error {
encode_into_encoder :: proc(e: Encoder, v: Value, loc := #caller_location) -> Encode_Error {
e := e
if e.temp_allocator.procedure == nil {
@@ -232,7 +233,7 @@ encode_into_encoder :: proc(e: Encoder, v: Value) -> Encode_Error {
if .Self_Described_CBOR in e.flags {
_encode_u64(e, TAG_SELF_DESCRIBED_CBOR, .Tag) or_return
e.flags &~= { .Self_Described_CBOR }
e.flags -= { .Self_Described_CBOR }
}
switch v_spec in v {
@@ -366,21 +367,21 @@ _encode_u64_exact :: proc(w: io.Writer, v: u64, major: Major = .Unsigned) -> (er
return
}
_decode_bytes_ptr :: proc(d: Decoder, add: Add, type: Major = .Bytes) -> (v: ^Bytes, err: Decode_Error) {
v = new(Bytes) or_return
defer if err != nil { free(v) }
_decode_bytes_ptr :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := context.allocator, loc := #caller_location) -> (v: ^Bytes, err: Decode_Error) {
v = new(Bytes, allocator, loc) or_return
defer if err != nil { free(v, allocator, loc) }
v^ = _decode_bytes(d, add, type) or_return
v^ = _decode_bytes(d, add, type, allocator, loc) or_return
return
}
_decode_bytes :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := context.allocator) -> (v: Bytes, err: Decode_Error) {
_decode_bytes :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := context.allocator, loc := #caller_location) -> (v: Bytes, err: Decode_Error) {
context.allocator = allocator
add := add
n, scap := _decode_len_str(d, add) or_return
buf := strings.builder_make(0, scap) or_return
buf := strings.builder_make(0, scap, allocator, loc) or_return
defer if err != nil { strings.builder_destroy(&buf) }
buf_stream := strings.to_stream(&buf)
@@ -422,44 +423,44 @@ _decode_bytes :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := c
_encode_bytes :: proc(e: Encoder, val: Bytes, major: Major = .Bytes) -> (err: Encode_Error) {
assert(len(val) >= 0)
_encode_u64(e, u64(len(val)), major) or_return
_, err = io.write_full(e.writer, val[:])
_, err = io.write_full(e.writer, val[:])
return
}
_decode_text_ptr :: proc(d: Decoder, add: Add) -> (v: ^Text, err: Decode_Error) {
v = new(Text) or_return
_decode_text_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Text, err: Decode_Error) {
v = new(Text, allocator, loc) or_return
defer if err != nil { free(v) }
v^ = _decode_text(d, add) or_return
v^ = _decode_text(d, add, allocator, loc) or_return
return
}
_decode_text :: proc(d: Decoder, add: Add, allocator := context.allocator) -> (v: Text, err: Decode_Error) {
return (Text)(_decode_bytes(d, add, .Text, allocator) or_return), nil
_decode_text :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Text, err: Decode_Error) {
return (Text)(_decode_bytes(d, add, .Text, allocator, loc) or_return), nil
}
_encode_text :: proc(e: Encoder, val: Text) -> Encode_Error {
return _encode_bytes(e, transmute([]byte)val, .Text)
return _encode_bytes(e, transmute([]byte)val, .Text)
}
_decode_array_ptr :: proc(d: Decoder, add: Add) -> (v: ^Array, err: Decode_Error) {
v = new(Array) or_return
_decode_array_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Array, err: Decode_Error) {
v = new(Array, allocator, loc) or_return
defer if err != nil { free(v) }
v^ = _decode_array(d, add) or_return
v^ = _decode_array(d, add, allocator, loc) or_return
return
}
_decode_array :: proc(d: Decoder, add: Add) -> (v: Array, err: Decode_Error) {
_decode_array :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Array, err: Decode_Error) {
n, scap := _decode_len_container(d, add) or_return
array := make([dynamic]Value, 0, scap) or_return
array := make([dynamic]Value, 0, scap, allocator, loc) or_return
defer if err != nil {
for entry in array { destroy(entry) }
delete(array)
for entry in array { destroy(entry, allocator) }
delete(array, loc)
}
for i := 0; n == -1 || i < n; i += 1 {
val, verr := _decode_from_decoder(d)
val, verr := _decode_from_decoder(d, {}, allocator, loc)
if n == -1 && verr == .Break {
break
} else if verr != nil {
@@ -479,45 +480,45 @@ _decode_array :: proc(d: Decoder, add: Add) -> (v: Array, err: Decode_Error) {
_encode_array :: proc(e: Encoder, arr: Array) -> Encode_Error {
assert(len(arr) >= 0)
_encode_u64(e, u64(len(arr)), .Array)
for val in arr {
encode(e, val) or_return
}
return nil
for val in arr {
encode(e, val) or_return
}
return nil
}
_decode_map_ptr :: proc(d: Decoder, add: Add) -> (v: ^Map, err: Decode_Error) {
v = new(Map) or_return
_decode_map_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Map, err: Decode_Error) {
v = new(Map, allocator, loc) or_return
defer if err != nil { free(v) }
v^ = _decode_map(d, add) or_return
v^ = _decode_map(d, add, allocator, loc) or_return
return
}
_decode_map :: proc(d: Decoder, add: Add) -> (v: Map, err: Decode_Error) {
_decode_map :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Map, err: Decode_Error) {
n, scap := _decode_len_container(d, add) or_return
items := make([dynamic]Map_Entry, 0, scap) or_return
items := make([dynamic]Map_Entry, 0, scap, allocator, loc) or_return
defer if err != nil {
for entry in items {
destroy(entry.key)
destroy(entry.value)
}
delete(items)
delete(items, loc)
}
for i := 0; n == -1 || i < n; i += 1 {
key, kerr := _decode_from_decoder(d)
key, kerr := _decode_from_decoder(d, {}, allocator, loc)
if n == -1 && kerr == .Break {
break
} else if kerr != nil {
return nil, kerr
}
value := _decode_from_decoder(d) or_return
value := _decode_from_decoder(d, {}, allocator, loc) or_return
append(&items, Map_Entry{
key = key,
value = value,
}) or_return
}, loc) or_return
}
if .Shrink_Excess in d.flags { shrink(&items) }
@@ -575,23 +576,23 @@ _encode_map :: proc(e: Encoder, m: Map) -> (err: Encode_Error) {
encode(e, entry.entry.value) or_return
}
return nil
return nil
}
_decode_tag_ptr :: proc(d: Decoder, add: Add) -> (v: Value, err: Decode_Error) {
tag := _decode_tag(d, add) or_return
_decode_tag_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
tag := _decode_tag(d, add, allocator, loc) or_return
if t, ok := tag.?; ok {
defer if err != nil { destroy(t.value) }
tp := new(Tag) or_return
tp := new(Tag, allocator, loc) or_return
tp^ = t
return tp, nil
}
// no error, no tag, this was the self described CBOR tag, skip it.
return _decode_from_decoder(d)
return _decode_from_decoder(d, {}, allocator, loc)
}
_decode_tag :: proc(d: Decoder, add: Add) -> (v: Maybe(Tag), err: Decode_Error) {
_decode_tag :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Maybe(Tag), err: Decode_Error) {
num := _decode_uint_as_u64(d.reader, add) or_return
// CBOR can be wrapped in a tag that decoders can use to see/check if the binary data is CBOR.
@@ -602,7 +603,7 @@ _decode_tag :: proc(d: Decoder, add: Add) -> (v: Maybe(Tag), err: Decode_Error)
t := Tag{
number = num,
value = _decode_from_decoder(d) or_return,
value = _decode_from_decoder(d, {}, allocator, loc) or_return,
}
if nested, ok := t.value.(^Tag); ok {
@@ -625,7 +626,7 @@ _decode_uint_as_u64 :: proc(r: io.Reader, add: Add) -> (nr: u64, err: Decode_Err
_encode_tag :: proc(e: Encoder, val: Tag) -> Encode_Error {
_encode_u64(e, val.number, .Tag) or_return
return encode(e, val.value)
return encode(e, val.value)
}
_decode_simple :: proc(r: io.Reader) -> (v: Simple, err: io.Error) {
@@ -738,16 +739,16 @@ _encode_nil :: proc(w: io.Writer) -> io.Error {
// Streaming
encode_stream_begin :: proc(w: io.Writer, major: Major) -> (err: io.Error) {
assert(major >= Major(.Bytes) && major <= Major(.Map), "illegal stream type")
assert(major >= Major(.Bytes) && major <= Major(.Map), "illegal stream type")
header := (u8(major) << 5) | u8(Add.Length_Unknown)
_, err = io.write_full(w, {header})
header := (u8(major) << 5) | u8(Add.Length_Unknown)
_, err = io.write_full(w, {header})
return
}
encode_stream_end :: proc(w: io.Writer) -> io.Error {
header := (u8(Major.Other) << 5) | u8(Add.Break)
_, err := io.write_full(w, {header})
header := (u8(Major.Other) << 5) | u8(Add.Break)
_, err := io.write_full(w, {header})
return err
}
@@ -756,8 +757,8 @@ encode_stream_text :: _encode_text
encode_stream_array_item :: encode
encode_stream_map_entry :: proc(e: Encoder, key: Value, val: Value) -> Encode_Error {
encode(e, key) or_return
return encode(e, val)
encode(e, key) or_return
return encode(e, val)
}
// For `Bytes` and `Text` strings: Decodes the number of items the header says follows.
@@ -883,4 +884,4 @@ _encode_deterministic_f64 :: proc(w: io.Writer, v: f64) -> io.Error {
}
return _encode_f64_exact(w, v)
}
}
+9 -9
View File
@@ -45,8 +45,8 @@ marshal :: marshal_into
// Marshals the given value into a CBOR byte stream (allocated using the given allocator).
// See docs on the `marshal_into` proc group for more info.
marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (bytes: []byte, err: Marshal_Error) {
b, alloc_err := strings.builder_make(allocator)
marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (bytes: []byte, err: Marshal_Error) {
b, alloc_err := strings.builder_make(allocator, loc=loc)
// The builder as a stream also returns .EOF if it ran out of memory so this is consistent.
if alloc_err != nil {
return nil, .EOF
@@ -54,7 +54,7 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
defer if err != nil { strings.builder_destroy(&b) }
if err = marshal_into_builder(&b, v, flags, temp_allocator); err != nil {
if err = marshal_into_builder(&b, v, flags, temp_allocator, loc=loc); err != nil {
return
}
@@ -63,20 +63,20 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
// Marshals the given value into a CBOR byte stream written to the given builder.
// See docs on the `marshal_into` proc group for more info.
marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator)
marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error {
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator, loc=loc)
}
// Marshals the given value into a CBOR byte stream written to the given writer.
// See docs on the `marshal_into` proc group for more info.
marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error {
encoder := Encoder{flags, w, temp_allocator}
return marshal_into_encoder(encoder, v)
return marshal_into_encoder(encoder, v, loc=loc)
}
// Marshals the given value into a CBOR byte stream written to the given encoder.
// See docs on the `marshal_into` proc group for more info.
marshal_into_encoder :: proc(e: Encoder, v: any) -> (err: Marshal_Error) {
marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (err: Marshal_Error) {
e := e
if e.temp_allocator.procedure == nil {
@@ -85,7 +85,7 @@ marshal_into_encoder :: proc(e: Encoder, v: any) -> (err: Marshal_Error) {
if .Self_Described_CBOR in e.flags {
err_conv(_encode_u64(e, TAG_SELF_DESCRIBED_CBOR, .Tag)) or_return
e.flags &~= { .Self_Described_CBOR }
e.flags -= { .Self_Described_CBOR }
}
if v == nil {
-1
View File
@@ -95,7 +95,6 @@ tag_register_number :: proc(impl: Tag_Implementation, nr: Tag_Number, id: string
}
// Controls initialization of default tag implementations.
// JS and WASI default to a panic allocator so we don't want to do it on those.
INITIALIZE_DEFAULT_TAGS :: #config(CBOR_INITIALIZE_DEFAULT_TAGS, !ODIN_DEFAULT_TO_PANIC_ALLOCATOR && !ODIN_DEFAULT_TO_NIL_ALLOCATOR)
@(private, init, disabled=!INITIALIZE_DEFAULT_TAGS)
+59 -57
View File
@@ -31,8 +31,8 @@ unmarshal :: proc {
unmarshal_from_string,
}
unmarshal_from_reader :: proc(r: io.Reader, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) {
err = unmarshal_from_decoder(Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r }, ptr, allocator, temp_allocator)
unmarshal_from_reader :: proc(r: io.Reader, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
err = unmarshal_from_decoder(Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r }, ptr, allocator, temp_allocator, loc)
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
if err == .EOF { err = .Unexpected_EOF }
@@ -40,21 +40,21 @@ unmarshal_from_reader :: proc(r: io.Reader, ptr: ^$T, flags := Decoder_Flags{},
}
// Unmarshals from a string, see docs on the proc group `Unmarshal` for more info.
unmarshal_from_string :: proc(s: string, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) {
unmarshal_from_string :: proc(s: string, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
sr: strings.Reader
r := strings.to_reader(&sr, s)
err = unmarshal_from_reader(r, ptr, flags, allocator, temp_allocator)
err = unmarshal_from_reader(r, ptr, flags, allocator, temp_allocator, loc)
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
if err == .EOF { err = .Unexpected_EOF }
return
}
unmarshal_from_decoder :: proc(d: Decoder, ptr: ^$T, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) {
unmarshal_from_decoder :: proc(d: Decoder, ptr: ^$T, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
d := d
err = _unmarshal_any_ptr(d, ptr, nil, allocator, temp_allocator)
err = _unmarshal_any_ptr(d, ptr, nil, allocator, temp_allocator, loc)
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
if err == .EOF { err = .Unexpected_EOF }
@@ -62,7 +62,7 @@ unmarshal_from_decoder :: proc(d: Decoder, ptr: ^$T, allocator := context.alloca
}
_unmarshal_any_ptr :: proc(d: Decoder, v: any, hdr: Maybe(Header) = nil, allocator := context.allocator, temp_allocator := context.temp_allocator) -> Unmarshal_Error {
_unmarshal_any_ptr :: proc(d: Decoder, v: any, hdr: Maybe(Header) = nil, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> Unmarshal_Error {
context.allocator = allocator
context.temp_allocator = temp_allocator
v := v
@@ -78,10 +78,10 @@ _unmarshal_any_ptr :: proc(d: Decoder, v: any, hdr: Maybe(Header) = nil, allocat
}
data := any{(^rawptr)(v.data)^, ti.variant.(reflect.Type_Info_Pointer).elem.id}
return _unmarshal_value(d, data, hdr.? or_else (_decode_header(d.reader) or_return))
return _unmarshal_value(d, data, hdr.? or_else (_decode_header(d.reader) or_return), allocator, temp_allocator, loc)
}
_unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Error) {
_unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
v := v
ti := reflect.type_info_base(type_info_of(v.id))
r := d.reader
@@ -104,7 +104,7 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Err
// Allow generic unmarshal by doing it into a `Value`.
switch &dst in v {
case Value:
dst = err_conv(_decode_from_decoder(d, hdr)) or_return
dst = err_conv(_decode_from_decoder(d, hdr, allocator, loc)) or_return
return
}
@@ -273,13 +273,13 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Err
// NOTE: Because this is a special type and not to be treated as a general integer,
// We only put the value of it in fields that are explicitly of type `Simple`.
switch &dst in v {
case Simple:
dst = decoded
return
case:
return _unsupported(v, hdr, add)
}
switch &dst in v {
case Simple:
dst = decoded
return
case:
return _unsupported(v, hdr, add)
}
case .Tag:
switch &dst in v {
@@ -308,7 +308,7 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Err
if impl, ok := _tag_implementations_nr[nr]; ok {
return impl->unmarshal(d, nr, v)
} else if nr == TAG_OBJECT_TYPE {
return _unmarshal_union(d, v, ti, hdr)
return _unmarshal_union(d, v, ti, hdr, loc=loc)
} else {
// Discard the tag info and unmarshal as its value.
return _unmarshal_value(d, v, _decode_header(r) or_return)
@@ -316,19 +316,19 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Err
return _unsupported(v, hdr, add)
case .Bytes: return _unmarshal_bytes(d, v, ti, hdr, add)
case .Text: return _unmarshal_string(d, v, ti, hdr, add)
case .Array: return _unmarshal_array(d, v, ti, hdr, add)
case .Map: return _unmarshal_map(d, v, ti, hdr, add)
case .Bytes: return _unmarshal_bytes(d, v, ti, hdr, add, allocator=allocator, loc=loc)
case .Text: return _unmarshal_string(d, v, ti, hdr, add, allocator=allocator, loc=loc)
case .Array: return _unmarshal_array(d, v, ti, hdr, add, allocator=allocator, loc=loc)
case .Map: return _unmarshal_map(d, v, ti, hdr, add, allocator=allocator, loc=loc)
case: return .Bad_Major
}
}
_unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
_unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
#partial switch t in ti.variant {
case reflect.Type_Info_String:
bytes := err_conv(_decode_bytes(d, add)) or_return
bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return
if t.is_cstring {
raw := (^cstring)(v.data)
@@ -347,7 +347,7 @@ _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if elem_base.id != byte { return _unsupported(v, hdr) }
bytes := err_conv(_decode_bytes(d, add)) or_return
bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return
raw := (^mem.Raw_Slice)(v.data)
raw^ = transmute(mem.Raw_Slice)bytes
return
@@ -357,12 +357,12 @@ _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if elem_base.id != byte { return _unsupported(v, hdr) }
bytes := err_conv(_decode_bytes(d, add)) or_return
bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return
raw := (^mem.Raw_Dynamic_Array)(v.data)
raw.data = raw_data(bytes)
raw.len = len(bytes)
raw.cap = len(bytes)
raw.allocator = context.allocator
raw.allocator = allocator
return
case reflect.Type_Info_Array:
@@ -385,10 +385,10 @@ _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return _unsupported(v, hdr)
}
_unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
_unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
#partial switch t in ti.variant {
case reflect.Type_Info_String:
text := err_conv(_decode_text(d, add)) or_return
text := err_conv(_decode_text(d, add, allocator, loc)) or_return
if t.is_cstring {
raw := (^cstring)(v.data)
@@ -403,8 +403,8 @@ _unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Heade
// Enum by its variant name.
case reflect.Type_Info_Enum:
text := err_conv(_decode_text(d, add, allocator=context.temp_allocator)) or_return
defer delete(text, context.temp_allocator)
text := err_conv(_decode_text(d, add, allocator=temp_allocator, loc=loc)) or_return
defer delete(text, temp_allocator, loc)
for name, i in t.names {
if name == text {
@@ -414,8 +414,8 @@ _unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Heade
}
case reflect.Type_Info_Rune:
text := err_conv(_decode_text(d, add, allocator=context.temp_allocator)) or_return
defer delete(text, context.temp_allocator)
text := err_conv(_decode_text(d, add, allocator=temp_allocator, loc=loc)) or_return
defer delete(text, temp_allocator, loc)
r := (^rune)(v.data)
dr, n := utf8.decode_rune(text)
@@ -430,13 +430,15 @@ _unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Heade
return _unsupported(v, hdr)
}
_unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
_unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
assign_array :: proc(
d: Decoder,
da: ^mem.Raw_Dynamic_Array,
elemt: ^reflect.Type_Info,
length: int,
growable := true,
allocator := context.allocator,
loc := #caller_location,
) -> (out_of_space: bool, err: Unmarshal_Error) {
for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 {
elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
@@ -450,13 +452,13 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if !growable { return true, .Out_Of_Memory }
cap := 2 * da.cap
ok := runtime.__dynamic_array_reserve(da, elemt.size, elemt.align, cap)
ok := runtime.__dynamic_array_reserve(da, elemt.size, elemt.align, cap, loc)
// NOTE: Might be lying here, but it is at least an allocator error.
if !ok { return false, .Out_Of_Memory }
}
err = _unmarshal_value(d, elem, hdr)
err = _unmarshal_value(d, elem, hdr, allocator=allocator, loc=loc)
if length == -1 && err == .Break { break }
if err != nil { return }
@@ -469,10 +471,10 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
// Allow generically storing the values array.
switch &dst in v {
case ^Array:
dst = err_conv(_decode_array_ptr(d, add)) or_return
dst = err_conv(_decode_array_ptr(d, add, allocator=allocator, loc=loc)) or_return
return
case Array:
dst = err_conv(_decode_array(d, add)) or_return
dst = err_conv(_decode_array(d, add, allocator=allocator, loc=loc)) or_return
return
}
@@ -480,8 +482,8 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
case reflect.Type_Info_Slice:
length, scap := err_conv(_decode_len_container(d, add)) or_return
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align) or_return
defer if err != nil { mem.free_bytes(data) }
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align, allocator=allocator, loc=loc) or_return
defer if err != nil { mem.free_bytes(data, allocator=allocator, loc=loc) }
da := mem.Raw_Dynamic_Array{raw_data(data), 0, length, context.allocator }
@@ -489,7 +491,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if .Shrink_Excess in d.flags {
// Ignoring an error here, but this is not critical to succeed.
_ = runtime.__dynamic_array_shrink(&da, t.elem.size, t.elem.align, da.len)
_ = runtime.__dynamic_array_shrink(&da, t.elem.size, t.elem.align, da.len, loc=loc)
}
raw := (^mem.Raw_Slice)(v.data)
@@ -500,8 +502,8 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
case reflect.Type_Info_Dynamic_Array:
length, scap := err_conv(_decode_len_container(d, add)) or_return
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align) or_return
defer if err != nil { mem.free_bytes(data) }
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align, loc=loc) or_return
defer if err != nil { mem.free_bytes(data, allocator=allocator, loc=loc) }
raw := (^mem.Raw_Dynamic_Array)(v.data)
raw.data = raw_data(data)
@@ -513,7 +515,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if .Shrink_Excess in d.flags {
// Ignoring an error here, but this is not critical to succeed.
_ = runtime.__dynamic_array_shrink(raw, t.elem.size, t.elem.align, raw.len)
_ = runtime.__dynamic_array_shrink(raw, t.elem.size, t.elem.align, raw.len, loc=loc)
}
return
@@ -525,7 +527,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return _unsupported(v, hdr)
}
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, context.allocator }
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
if out_of_space { return _unsupported(v, hdr) }
@@ -539,7 +541,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return _unsupported(v, hdr)
}
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, context.allocator }
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
if out_of_space { return _unsupported(v, hdr) }
@@ -553,7 +555,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return _unsupported(v, hdr)
}
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 2, context.allocator }
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 2, allocator }
info: ^runtime.Type_Info
switch ti.id {
@@ -575,7 +577,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
return _unsupported(v, hdr)
}
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 4, context.allocator }
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 4, allocator }
info: ^runtime.Type_Info
switch ti.id {
@@ -593,17 +595,17 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
}
}
_unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
_unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
r := d.reader
decode_key :: proc(d: Decoder, v: any, allocator := context.allocator) -> (k: string, err: Unmarshal_Error) {
decode_key :: proc(d: Decoder, v: any, allocator := context.allocator, loc := #caller_location) -> (k: string, err: Unmarshal_Error) {
entry_hdr := _decode_header(d.reader) or_return
entry_maj, entry_add := _header_split(entry_hdr)
#partial switch entry_maj {
case .Text:
k = err_conv(_decode_text(d, entry_add, allocator)) or_return
k = err_conv(_decode_text(d, entry_add, allocator=allocator, loc=loc)) or_return
return
case .Bytes:
bytes := err_conv(_decode_bytes(d, entry_add, allocator=allocator)) or_return
bytes := err_conv(_decode_bytes(d, entry_add, allocator=allocator, loc=loc)) or_return
k = string(bytes)
return
case:
@@ -615,10 +617,10 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
// Allow generically storing the map array.
switch &dst in v {
case ^Map:
dst = err_conv(_decode_map_ptr(d, add)) or_return
dst = err_conv(_decode_map_ptr(d, add, allocator=allocator, loc=loc)) or_return
return
case Map:
dst = err_conv(_decode_map(d, add)) or_return
dst = err_conv(_decode_map(d, add, allocator=allocator, loc=loc)) or_return
return
}
@@ -754,7 +756,7 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
// Unmarshal into a union, based on the `TAG_OBJECT_TYPE` tag of the spec, it denotes a tag which
// contains an array of exactly two elements, the first is a textual representation of the following
// CBOR value's type.
_unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header) -> (err: Unmarshal_Error) {
_unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, loc := #caller_location) -> (err: Unmarshal_Error) {
r := d.reader
#partial switch t in ti.variant {
case reflect.Type_Info_Union:
@@ -792,7 +794,7 @@ _unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
case reflect.Type_Info_Named:
if vti.name == target_name {
reflect.set_union_variant_raw_tag(v, tag)
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return)
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return, loc=loc)
}
case:
@@ -804,7 +806,7 @@ _unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if variant_name == target_name {
reflect.set_union_variant_raw_tag(v, tag)
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return)
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return, loc=loc)
}
}
}
+4 -2
View File
@@ -138,7 +138,9 @@ iterator_next :: proc(r: ^Reader) -> (record: []string, idx: int, err: Error, mo
return record, r.line_count - 1, r.last_iterator_error, r.last_iterator_error == nil
}
// Get last error if we the iterator
// Get last CSV parse error if we ignored it in the iterator loop
//
// for record, row_idx in csv.iterator_next(&r) { ... }
iterator_last_error :: proc(r: Reader) -> (err: Error) {
return r.last_iterator_error
}
@@ -169,7 +171,7 @@ is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
// read_all reads all the remaining records from r.
// Each record is a slice of fields.
// read_all is defined to read until an EOF, and does not treat, and does not treat EOF as an error
// read_all is defined to read until an EOF, and does not treat EOF as an error
@(require_results)
read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Error) {
context.allocator = allocator
+54 -69
View File
@@ -56,38 +56,27 @@ CDATA_END :: "]]>"
COMMENT_START :: "<!--"
COMMENT_END :: "-->"
/*
Default: CDATA and comments are passed through unchanged.
*/
// Default: CDATA and comments are passed through unchanged.
XML_Decode_Option :: enum u8 {
/*
Do not decode & entities. It decodes by default.
If given, overrides `Decode_CDATA`.
*/
// Do not decode & entities. It decodes by default. If given, overrides `Decode_CDATA`.
No_Entity_Decode,
/*
CDATA is unboxed.
*/
// CDATA is unboxed.
Unbox_CDATA,
/*
Unboxed CDATA is decoded as well.
Ignored if `.Unbox_CDATA` is not given.
*/
// Unboxed CDATA is decoded as well. Ignored if `.Unbox_CDATA` is not given.
Decode_CDATA,
/*
Comments are stripped.
*/
// Comments are stripped.
Comment_Strip,
// Normalize whitespace
Normalize_Whitespace,
}
XML_Decode_Options :: bit_set[XML_Decode_Option; u8]
/*
Decode a string that may include SGML/XML/HTML entities.
The caller has to free the result.
*/
// Decode a string that may include SGML/XML/HTML entities.
// The caller has to free the result.
decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := context.allocator) -> (decoded: string, err: Error) {
context.allocator = allocator
@@ -100,14 +89,14 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
t := Tokenizer{src=input}
in_data := false
prev: rune = ' '
loop: for {
advance(&t) or_return
if t.r < 0 { break loop }
/*
Below here we're never inside a CDATA tag.
At most we'll see the start of one, but that doesn't affect the logic.
*/
// Below here we're never inside a CDATA tag. At most we'll see the start of one,
// but that doesn't affect the logic.
switch t.r {
case '<':
/*
@@ -126,9 +115,7 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
in_data = _handle_xml_special(&t, &builder, options) or_return
case ']':
/*
If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag.
*/
// If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag.
if in_data {
if t.read_offset + len(CDATA_END) < len(t.src) {
if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
@@ -143,22 +130,16 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
case:
if in_data && .Decode_CDATA not_in options {
/*
Unboxed, but undecoded.
*/
// Unboxed, but undecoded.
write_rune(&builder, t.r)
continue
}
if t.r == '&' {
if entity, entity_err := _extract_xml_entity(&t); entity_err != .None {
/*
We read to the end of the string without closing the entity.
Pass through as-is.
*/
// We read to the end of the string without closing the entity. Pass through as-is.
write_string(&builder, entity)
} else {
if .No_Entity_Decode not_in options {
if decoded, ok := xml_decode_entity(entity); ok {
write_rune(&builder, decoded)
@@ -166,19 +147,41 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
}
}
/*
Literal passthrough because the decode failed or we want entities not decoded.
*/
// Literal passthrough because the decode failed or we want entities not decoded.
write_string(&builder, "&")
write_string(&builder, entity)
write_string(&builder, ";")
}
} else {
write_rune(&builder, t.r)
// Handle AV Normalization: https://www.w3.org/TR/2006/REC-xml11-20060816/#AVNormalize
if .Normalize_Whitespace in options {
switch t.r {
case ' ', '\r', '\n', '\t':
if prev != ' ' {
write_rune(&builder, ' ')
prev = ' '
}
case:
write_rune(&builder, t.r)
prev = t.r
}
} else {
// https://www.w3.org/TR/2006/REC-xml11-20060816/#sec-line-ends
switch t.r {
case '\n', 0x85, 0x2028:
write_rune(&builder, '\n')
case '\r': // Do nothing until next character
case:
if prev == '\r' { // Turn a single carriage return into a \n
write_rune(&builder, '\n')
}
write_rune(&builder, t.r)
}
prev = t.r
}
}
}
}
return strings.clone(strings.to_string(builder), allocator), err
}
@@ -253,24 +256,18 @@ xml_decode_entity :: proc(entity: string) -> (decoded: rune, ok: bool) {
return rune(val), true
case:
/*
Named entity.
*/
// Named entity.
return named_xml_entity_to_rune(entity)
}
}
/*
Private XML helper to extract `&<stuff>;` entity.
*/
// Private XML helper to extract `&<stuff>;` entity.
@(private="file")
_extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
assert(t != nil && t.r == '&')
/*
All of these would be in the ASCII range.
Even if one is not, it doesn't matter. All characters we need to compare to extract are.
*/
// All of these would be in the ASCII range.
// Even if one is not, it doesn't matter. All characters we need to compare to extract are.
length := len(t.src)
found := false
@@ -292,9 +289,7 @@ _extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
return string(t.src[t.offset : t.read_offset]), .Invalid_Entity_Encoding
}
/*
Private XML helper for CDATA and comments.
*/
// Private XML helper for CDATA and comments.
@(private="file")
_handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: XML_Decode_Options) -> (in_data: bool, err: Error) {
assert(t != nil && t.r == '<')
@@ -304,20 +299,14 @@ _handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: X
t.read_offset += len(CDATA_START) - 1
if .Unbox_CDATA in options && .Decode_CDATA in options {
/*
We're unboxing _and_ decoding CDATA
*/
// We're unboxing _and_ decoding CDATA
return true, .None
}
/*
CDATA is passed through.
*/
// CDATA is passed through.
offset := t.offset
/*
Scan until end of CDATA.
*/
// Scan until end of CDATA.
for {
advance(t) or_return
if t.r < 0 { return true, .CDATA_Not_Terminated }
@@ -341,14 +330,10 @@ _handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: X
} else if string(t.src[t.offset:][:len(COMMENT_START)]) == COMMENT_START {
t.read_offset += len(COMMENT_START)
/*
Comment is passed through by default.
*/
// Comment is passed through by default.
offset := t.offset
/*
Scan until end of Comment.
*/
// Scan until end of Comment.
for {
advance(t) or_return
if t.r < 0 { return true, .Comment_Not_Terminated }
File diff suppressed because it is too large Load Diff
+5 -6
View File
@@ -2,8 +2,8 @@ package encoding_hex
import "core:strings"
encode :: proc(src: []byte, allocator := context.allocator) -> []byte #no_bounds_check {
dst := make([]byte, len(src) * 2, allocator)
encode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> []byte #no_bounds_check {
dst := make([]byte, len(src) * 2, allocator, loc)
for i, j := 0, 0; i < len(src); i += 1 {
v := src[i]
dst[j] = HEXTABLE[v>>4]
@@ -15,12 +15,12 @@ encode :: proc(src: []byte, allocator := context.allocator) -> []byte #no_bounds
}
decode :: proc(src: []byte, allocator := context.allocator) -> (dst: []byte, ok: bool) #no_bounds_check {
decode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> (dst: []byte, ok: bool) #no_bounds_check {
if len(src) % 2 == 1 {
return
}
dst = make([]byte, len(src) / 2, allocator)
dst = make([]byte, len(src) / 2, allocator, loc)
for i, j := 0, 1; j < len(src); j += 2 {
p := src[j-1]
q := src[j]
@@ -69,5 +69,4 @@ hex_digit :: proc(char: byte) -> (u8, bool) {
case 'A' ..= 'F': return char - 'A' + 10, true
case: return 0, false
}
}
}
+16 -15
View File
@@ -160,34 +160,35 @@ CONVENTION_SOFT_TRANSFORM :: "transform"
/* destroy procedures */
meta_destroy :: proc(meta: Meta, allocator := context.allocator) {
meta_destroy :: proc(meta: Meta, allocator := context.allocator, loc := #caller_location) {
if nested, ok := meta.value.([]Meta); ok {
for m in nested {
meta_destroy(m)
meta_destroy(m, loc=loc)
}
delete(nested, allocator)
delete(nested, allocator, loc=loc)
}
}
nodes_destroy :: proc(nodes: []Node, allocator := context.allocator) {
nodes_destroy :: proc(nodes: []Node, allocator := context.allocator, loc := #caller_location) {
for node in nodes {
for meta in node.meta_data {
meta_destroy(meta)
meta_destroy(meta, loc=loc)
}
delete(node.meta_data, allocator)
delete(node.meta_data, allocator, loc=loc)
switch n in node.content {
case Node_Geometry:
delete(n.corner_stack, allocator)
delete(n.edge_stack, allocator)
delete(n.face_stack, allocator)
delete(n.corner_stack, allocator, loc=loc)
delete(n.vertex_stack, allocator, loc=loc)
delete(n.edge_stack, allocator, loc=loc)
delete(n.face_stack, allocator, loc=loc)
case Node_Image:
delete(n.image_stack, allocator)
delete(n.image_stack, allocator, loc=loc)
}
}
delete(nodes, allocator)
delete(nodes, allocator, loc=loc)
}
file_destroy :: proc(file: File) {
nodes_destroy(file.nodes, file.allocator)
delete(file.backing, file.allocator)
}
file_destroy :: proc(file: File, loc := #caller_location) {
nodes_destroy(file.nodes, file.allocator, loc=loc)
delete(file.backing, file.allocator, loc=loc)
}
+20 -22
View File
@@ -11,24 +11,21 @@ Read_Error :: enum {
Unable_To_Read_File,
}
read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) {
read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator, loc := #caller_location) -> (file: File, err: Read_Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename)
data, ok := os.read_entire_file(filename, allocator, loc)
if !ok {
err = .Unable_To_Read_File
delete(data, allocator, loc)
return
}
defer if !ok {
delete(data)
} else {
file.backing = data
}
file, err = read(data, filename, print_error, allocator)
file, err = read(data, filename, print_error, allocator, loc)
file.backing = data
return
}
read :: proc(data: []byte, filename := "<input>", print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) {
read :: proc(data: []byte, filename := "<input>", print_error := false, allocator := context.allocator, loc := #caller_location) -> (file: File, err: Read_Error) {
Reader :: struct {
filename: string,
data: []byte,
@@ -79,8 +76,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
return string(data[:len]), nil
}
read_meta :: proc(r: ^Reader, capacity: u32le) -> (meta_data: []Meta, err: Read_Error) {
meta_data = make([]Meta, int(capacity))
read_meta :: proc(r: ^Reader, capacity: u32le, allocator := context.allocator, loc := #caller_location) -> (meta_data: []Meta, err: Read_Error) {
meta_data = make([]Meta, int(capacity), allocator=allocator)
count := 0
defer meta_data = meta_data[:count]
for &m in meta_data {
@@ -111,10 +108,10 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
return
}
read_layer_stack :: proc(r: ^Reader, capacity: u32le) -> (layers: Layer_Stack, err: Read_Error) {
read_layer_stack :: proc(r: ^Reader, capacity: u32le, allocator := context.allocator, loc := #caller_location) -> (layers: Layer_Stack, err: Read_Error) {
stack_count := read_value(r, u32le) or_return
layer_count := 0
layers = make(Layer_Stack, stack_count)
layers = make(Layer_Stack, stack_count, allocator=allocator, loc=loc)
defer layers = layers[:layer_count]
for &layer in layers {
layer.name = read_name(r) or_return
@@ -170,7 +167,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
node_count := 0
file.header = header^
file.nodes = make([]Node, header.internal_node_count)
file.nodes = make([]Node, header.internal_node_count, allocator=allocator, loc=loc)
file.allocator = allocator
defer if err != nil {
nodes_destroy(file.nodes)
file.nodes = nil
@@ -198,15 +196,15 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
case .Geometry:
g: Node_Geometry
g.vertex_count = read_value(r, u32le) or_return
g.vertex_stack = read_layer_stack(r, g.vertex_count) or_return
g.edge_corner_count = read_value(r, u32le) or_return
g.corner_stack = read_layer_stack(r, g.edge_corner_count) or_return
g.vertex_count = read_value(r, u32le) or_return
g.vertex_stack = read_layer_stack(r, g.vertex_count, loc=loc) or_return
g.edge_corner_count = read_value(r, u32le) or_return
g.corner_stack = read_layer_stack(r, g.edge_corner_count, loc=loc) or_return
if header.version > 2 {
g.edge_stack = read_layer_stack(r, g.edge_corner_count) or_return
g.edge_stack = read_layer_stack(r, g.edge_corner_count, loc=loc) or_return
}
g.face_count = read_value(r, u32le) or_return
g.face_stack = read_layer_stack(r, g.face_count) or_return
g.face_count = read_value(r, u32le) or_return
g.face_stack = read_layer_stack(r, g.face_count, loc=loc) or_return
node.content = g
@@ -233,4 +231,4 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
}
return
}
}
+23 -21
View File
@@ -25,7 +25,7 @@ Marshal_Options :: struct {
spec: Specification,
// Use line breaks & tabs/spaces
pretty: bool,
pretty: bool,
// Use spaces for indentation instead of tabs
use_spaces: bool,
@@ -34,7 +34,7 @@ Marshal_Options :: struct {
spaces: int,
// Output uint as hex in JSON5 & MJSON
write_uint_as_hex: bool,
write_uint_as_hex: bool,
// If spec is MJSON and this is true, then keys will be quoted.
//
@@ -62,8 +62,8 @@ Marshal_Options :: struct {
mjson_skipped_first_braces_end: bool,
}
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
b := strings.builder_make(allocator)
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) {
b := strings.builder_make(allocator, loc)
defer if err != nil {
strings.builder_destroy(&b)
}
@@ -138,7 +138,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
// allow uints to be printed as hex
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
switch i in a {
case u8, u16, u32, u64, u128:
case u8, u16, u32, u64, u128:
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
case:
@@ -239,7 +239,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Array:
opt_write_start(w, opt, '[') or_return
for i in 0..<info.count {
opt_write_iteration(w, opt, i) or_return
opt_write_iteration(w, opt, i == 0) or_return
data := uintptr(v.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
}
@@ -248,7 +248,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Enumerated_Array:
opt_write_start(w, opt, '[') or_return
for i in 0..<info.count {
opt_write_iteration(w, opt, i) or_return
opt_write_iteration(w, opt, i == 0) or_return
data := uintptr(v.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
}
@@ -258,7 +258,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
opt_write_start(w, opt, '[') or_return
array := cast(^mem.Raw_Dynamic_Array)v.data
for i in 0..<array.len {
opt_write_iteration(w, opt, i) or_return
opt_write_iteration(w, opt, i == 0) or_return
data := uintptr(array.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
}
@@ -268,7 +268,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
opt_write_start(w, opt, '[') or_return
slice := cast(^mem.Raw_Slice)v.data
for i in 0..<slice.len {
opt_write_iteration(w, opt, i) or_return
opt_write_iteration(w, opt, i == 0) or_return
data := uintptr(slice.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
}
@@ -290,7 +290,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
for bucket_index in 0..<map_cap {
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
opt_write_iteration(w, opt, i) or_return
opt_write_iteration(w, opt, i == 0) or_return
i += 1
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
@@ -356,7 +356,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
slice.sort_by(sorted[:], proc(i, j: Entry) -> bool { return i.key < j.key })
for s, i in sorted {
opt_write_iteration(w, opt, i) or_return
opt_write_iteration(w, opt, i == 0) or_return
opt_write_key(w, opt, s.key) or_return
marshal_to_writer(w, s.value, opt) or_return
}
@@ -387,17 +387,17 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Pointer,
runtime.Type_Info_Multi_Pointer,
runtime.Type_Info_Procedure:
return (^rawptr)(v.data)^ == nil
return (^rawptr)(v.data)^ == nil
case runtime.Type_Info_Dynamic_Array:
return (^runtime.Raw_Dynamic_Array)(v.data).len == 0
return (^runtime.Raw_Dynamic_Array)(v.data).len == 0
case runtime.Type_Info_Slice:
return (^runtime.Raw_Slice)(v.data).len == 0
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 (^runtime.Raw_Map)(v.data).len == 0
}
return false
}
@@ -405,6 +405,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
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)
first_iteration := true
for name, i in info.names {
omitempty := false
@@ -424,7 +425,8 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
continue
}
opt_write_iteration(w, opt, i) or_return
opt_write_iteration(w, opt, first_iteration) or_return
first_iteration = false
if json_name != "" {
opt_write_key(w, opt, json_name) or_return
} else {
@@ -588,10 +590,10 @@ opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: i
}
// insert comma separation and write indentations
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) {
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, first_iteration: bool) -> (err: io.Error) {
switch opt.spec {
case .JSON, .JSON5:
if iteration > 0 {
case .JSON, .JSON5:
if !first_iteration {
io.write_byte(w, ',') or_return
if opt.pretty {
@@ -601,8 +603,8 @@ opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int)
opt_write_indentation(w, opt) or_return
case .MJSON:
if iteration > 0 {
case .MJSON:
if !first_iteration {
// on pretty no commas necessary
if opt.pretty {
io.write_byte(w, '\n') or_return
+33 -32
View File
@@ -28,27 +28,27 @@ make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, par
}
parse :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> (Value, Error) {
return parse_string(string(data), spec, parse_integers, allocator)
parse :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
return parse_string(string(data), spec, parse_integers, allocator, loc)
}
parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> (Value, Error) {
parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
context.allocator = allocator
p := make_parser_from_string(data, spec, parse_integers, allocator)
switch p.spec {
case .JSON:
return parse_object(&p)
return parse_object(&p, loc)
case .JSON5:
return parse_value(&p)
return parse_value(&p, loc)
case .SJSON:
#partial switch p.curr_token.kind {
case .Ident, .String:
return parse_object_body(&p, .EOF)
return parse_object_body(&p, .EOF, loc)
}
return parse_value(&p)
return parse_value(&p, loc)
}
return parse_object(&p)
return parse_object(&p, loc)
}
token_end_pos :: proc(tok: Token) -> Pos {
@@ -106,7 +106,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
return false
}
parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
err = .None
token := p.curr_token
#partial switch token.kind {
@@ -142,13 +142,13 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
case .String:
advance_token(p)
return unquote_string(token, p.spec, p.allocator)
return unquote_string(token, p.spec, p.allocator, loc)
case .Open_Brace:
return parse_object(p)
return parse_object(p, loc)
case .Open_Bracket:
return parse_array(p)
return parse_array(p, loc)
case:
if p.spec != .JSON {
@@ -176,7 +176,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
return
}
parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
err = .None
expect_token(p, .Open_Bracket) or_return
@@ -184,14 +184,14 @@ parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
array.allocator = p.allocator
defer if err != nil {
for elem in array {
destroy_value(elem)
destroy_value(elem, loc=loc)
}
delete(array)
delete(array, loc)
}
for p.curr_token.kind != .Close_Bracket {
elem := parse_value(p) or_return
append(&array, elem)
elem := parse_value(p, loc) or_return
append(&array, elem, loc)
if parse_comma(p) {
break
@@ -228,38 +228,39 @@ clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_locatio
return
}
parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator) -> (key: string, err: Error) {
parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #caller_location) -> (key: string, err: Error) {
tok := p.curr_token
if p.spec != .JSON {
if allow_token(p, .Ident) {
return clone_string(tok.text, key_allocator)
return clone_string(tok.text, key_allocator, loc)
}
}
if tok_err := expect_token(p, .String); tok_err != nil {
err = .Expected_String_For_Object_Key
return
}
return unquote_string(tok, p.spec, key_allocator)
return unquote_string(tok, p.spec, key_allocator, loc)
}
parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, err: Error) {
obj.allocator = p.allocator
parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_location) -> (obj: Object, err: Error) {
obj = make(Object, allocator=p.allocator, loc=loc)
defer if err != nil {
for key, elem in obj {
delete(key, p.allocator)
destroy_value(elem)
delete(key, p.allocator, loc)
destroy_value(elem, loc=loc)
}
delete(obj)
delete(obj, loc)
}
for p.curr_token.kind != end_token {
key := parse_object_key(p, p.allocator) or_return
key := parse_object_key(p, p.allocator, loc) or_return
parse_colon(p) or_return
elem := parse_value(p) or_return
elem := parse_value(p, loc) or_return
if key in obj {
err = .Duplicate_Object_Key
delete(key, p.allocator)
delete(key, p.allocator, loc)
return
}
@@ -267,7 +268,7 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, er
// inserting empty key/values into the object and for those we do not
// want to allocate anything
if key != "" {
reserve_error := reserve(&obj, len(obj) + 1)
reserve_error := reserve(&obj, len(obj) + 1, loc)
if reserve_error == mem.Allocator_Error.Out_Of_Memory {
return nil, .Out_Of_Memory
}
@@ -281,9 +282,9 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, er
return obj, .None
}
parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
expect_token(p, .Open_Brace) or_return
obj := parse_object_body(p, .Close_Brace) or_return
obj := parse_object_body(p, .Close_Brace, loc) or_return
expect_token(p, .Close_Brace) or_return
return obj, .None
}
@@ -480,4 +481,4 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
}
return string(b[:w]), nil
}
}
+7 -7
View File
@@ -89,22 +89,22 @@ Error :: enum {
destroy_value :: proc(value: Value, allocator := context.allocator) {
destroy_value :: proc(value: Value, allocator := context.allocator, loc := #caller_location) {
context.allocator = allocator
#partial switch v in value {
case Object:
for key, elem in v {
delete(key)
destroy_value(elem)
delete(key, loc=loc)
destroy_value(elem, loc=loc)
}
delete(v)
delete(v, loc=loc)
case Array:
for elem in v {
destroy_value(elem)
destroy_value(elem, loc=loc)
}
delete(v)
delete(v, loc=loc)
case String:
delete(v)
delete(v, loc=loc)
}
}
+28
View File
@@ -0,0 +1,28 @@
BSD 3-Clause License
Copyright (c) 2024, Feoramund
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.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
+67
View File
@@ -0,0 +1,67 @@
package uuid
// A RFC 4122 Universally Unique Identifier
Identifier :: distinct [16]u8
EXPECTED_LENGTH :: 8 + 4 + 4 + 4 + 12 + 4
VERSION_BYTE_INDEX :: 6
VARIANT_BYTE_INDEX :: 8
// The number of 100-nanosecond intervals between 1582-10-15 and 1970-01-01.
HNS_INTERVALS_BETWEEN_GREG_AND_UNIX :: 141427 * 24 * 60 * 60 * 1000 * 1000 * 10
VERSION_7_TIME_MASK :: 0xffffffff_ffff0000_00000000_00000000
VERSION_7_TIME_SHIFT :: 80
VERSION_7_COUNTER_MASK :: 0x00000000_00000fff_00000000_00000000
VERSION_7_COUNTER_SHIFT :: 64
@(private)
NO_CSPRNG_ERROR :: "The context random generator is not cryptographic. See the documentation for an example of how to set one up."
@(private)
BIG_CLOCK_ERROR :: "The clock sequence can only hold 14 bits of data, therefore no number greater than 16,383 (0x3FFF)."
@(private)
VERSION_7_BIG_COUNTER_ERROR :: "This implementation of the version 7 UUID counter can only hold 12 bits of data, therefore no number greater than 4,095 (0xFFF)."
Read_Error :: enum {
None,
Invalid_Length,
Invalid_Hexadecimal,
Invalid_Separator,
}
Variant_Type :: enum {
Unknown,
Reserved_Apollo_NCS, // 0b0xx
RFC_4122, // 0b10x
Reserved_Microsoft_COM, // 0b110
Reserved_Future, // 0b111
}
// Name string is a fully-qualified domain name.
@(rodata)
Namespace_DNS := Identifier {
0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1,
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
}
// Name string is a URL.
@(rodata)
Namespace_URL := Identifier {
0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1,
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
}
// Name string is an ISO OID.
@(rodata)
Namespace_OID := Identifier {
0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1,
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
}
// Name string is an X.500 DN (in DER or a text output format).
@(rodata)
Namespace_X500 := Identifier {
0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1,
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
}
+46
View File
@@ -0,0 +1,46 @@
/*
package uuid implements Universally Unique Identifiers according to the
standard originally outlined in RFC 4122 with additions from RFC 9562.
The UUIDs are textually represented and read in the following string format:
`00000000-0000-v000-V000-000000000000`
`v` is where the version bits reside, and `V` is where the variant bits reside.
The meaning of the other bits is version-dependent.
Outside of string representations, UUIDs are represented in memory by a 128-bit
structure organized as an array of 16 bytes.
Of the UUID versions which may make use of random number generation, a
requirement is placed upon them that the underlying generator be
cryptographically-secure, per RFC 9562's suggestion.
- Version 1 without a node argument.
- Version 4 in all cases.
- Version 6 without either a clock or node argument.
- Version 7 in all cases.
Here's an example of how to set up one:
import "core:crypto"
import "core:encoding/uuid"
main :: proc() {
my_uuid: uuid.Identifier
{
// This scope will have a CSPRNG.
context.random_generator = crypto.random_generator()
my_uuid = uuid.generate_v7()
}
// Back to the default random number generator.
}
For more information on the specifications, see here:
- https://www.rfc-editor.org/rfc/rfc4122.html
- https://www.rfc-editor.org/rfc/rfc9562.html
*/
package uuid
+333
View File
@@ -0,0 +1,333 @@
package uuid
import "base:runtime"
import "core:crypto/hash"
import "core:math/rand"
import "core:time"
/*
Generate a version 1 UUID.
Inputs:
- clock_seq: The clock sequence, a number which must be initialized to a random number once in the lifetime of a system.
- node: An optional 48-bit spatially unique identifier, specified to be the IEEE 802 address of the system.
If one is not provided or available, 48 bits of random state will take its place.
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
Returns:
- result: The generated UUID.
*/
generate_v1 :: proc(clock_seq: u16, node: Maybe([6]u8) = nil, timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
assert(clock_seq <= 0x3FFF, BIG_CLOCK_ERROR)
unix_time_in_hns_intervals := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 100
uuid_timestamp := cast(u64le)(HNS_INTERVALS_BETWEEN_GREG_AND_UNIX + unix_time_in_hns_intervals)
uuid_timestamp_octets := transmute([8]u8)uuid_timestamp
result[0] = uuid_timestamp_octets[0]
result[1] = uuid_timestamp_octets[1]
result[2] = uuid_timestamp_octets[2]
result[3] = uuid_timestamp_octets[3]
result[4] = uuid_timestamp_octets[4]
result[5] = uuid_timestamp_octets[5]
result[6] = uuid_timestamp_octets[6] >> 4
result[7] = uuid_timestamp_octets[6] << 4 | uuid_timestamp_octets[7]
if realized_node, ok := node.?; ok {
mutable_node := realized_node
runtime.mem_copy_non_overlapping(&result[10], &mutable_node[0], 6)
} else {
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
bytes_generated := rand.read(result[10:])
assert(bytes_generated == 6, "RNG failed to generate 6 bytes for UUID v1.")
}
result[VERSION_BYTE_INDEX] |= 0x10
result[VARIANT_BYTE_INDEX] |= 0x80
result[8] |= cast(u8)(clock_seq & 0x3F00 >> 8)
result[9] = cast(u8)clock_seq
return
}
/*
Generate a version 4 UUID.
This UUID will be pseudorandom, save for 6 pre-determined version and variant bits.
Returns:
- result: The generated UUID.
*/
generate_v4 :: proc() -> (result: Identifier) {
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
bytes_generated := rand.read(result[:])
assert(bytes_generated == 16, "RNG failed to generate 16 bytes for UUID v4.")
result[VERSION_BYTE_INDEX] &= 0x0F
result[VERSION_BYTE_INDEX] |= 0x40
result[VARIANT_BYTE_INDEX] &= 0x3F
result[VARIANT_BYTE_INDEX] |= 0x80
return
}
/*
Generate a version 6 UUID.
Inputs:
- clock_seq: The clock sequence from version 1, now made optional.
If unspecified, it will be replaced with random bits.
- node: An optional 48-bit spatially unique identifier, specified to be the IEEE 802 address of the system.
If one is not provided or available, 48 bits of random state will take its place.
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
Returns:
- result: The generated UUID.
*/
generate_v6 :: proc(clock_seq: Maybe(u16) = nil, node: Maybe([6]u8) = nil, timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
unix_time_in_hns_intervals := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 100
uuid_timestamp := cast(u128be)(HNS_INTERVALS_BETWEEN_GREG_AND_UNIX + unix_time_in_hns_intervals)
result = transmute(Identifier)(
uuid_timestamp & 0x0FFFFFFF_FFFFF000 << 68 |
uuid_timestamp & 0x00000000_00000FFF << 64
)
if realized_clock_seq, ok := clock_seq.?; ok {
assert(realized_clock_seq <= 0x3FFF, BIG_CLOCK_ERROR)
result[8] |= cast(u8)(realized_clock_seq & 0x3F00 >> 8)
result[9] = cast(u8)realized_clock_seq
} else {
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
temporary: [2]u8
bytes_generated := rand.read(temporary[:])
assert(bytes_generated == 2, "RNG failed to generate 2 bytes for UUID v1.")
result[8] |= temporary[0] & 0x3F
result[9] = temporary[1]
}
if realized_node, ok := node.?; ok {
mutable_node := realized_node
runtime.mem_copy_non_overlapping(&result[10], &mutable_node[0], 6)
} else {
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
bytes_generated := rand.read(result[10:])
assert(bytes_generated == 6, "RNG failed to generate 6 bytes for UUID v1.")
}
result[VERSION_BYTE_INDEX] |= 0x60
result[VARIANT_BYTE_INDEX] |= 0x80
return
}
/*
Generate a version 7 UUID.
This UUID will be pseudorandom, save for 6 pre-determined version and variant
bits and a 48-bit timestamp.
It is designed with time-based sorting in mind, such as for database usage, as
the highest bits are allocated from the timestamp of when it is created.
Inputs:
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
Returns:
- result: The generated UUID.
*/
generate_v7_basic :: proc(timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
unix_time_in_milliseconds := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 1e6
result = transmute(Identifier)(cast(u128be)unix_time_in_milliseconds << VERSION_7_TIME_SHIFT)
bytes_generated := rand.read(result[6:])
assert(bytes_generated == 10, "RNG failed to generate 10 bytes for UUID v7.")
result[VERSION_BYTE_INDEX] &= 0x0F
result[VERSION_BYTE_INDEX] |= 0x70
result[VARIANT_BYTE_INDEX] &= 0x3F
result[VARIANT_BYTE_INDEX] |= 0x80
return
}
/*
Generate a version 7 UUID that has an incremented counter.
This UUID will be pseudorandom, save for 6 pre-determined version and variant
bits, a 48-bit timestamp, and 12 bits of counter state.
It is designed with time-based sorting in mind, such as for database usage, as
the highest bits are allocated from the timestamp of when it is created.
This procedure is preferable if you are generating hundreds or thousands of
UUIDs as a batch within the span of a millisecond. Do note that the counter
only has 12 bits of state, thus `counter` cannot exceed the number 4,095.
Example:
import "core:uuid"
// Create a batch of UUIDs all at once.
batch: [dynamic]uuid.Identifier
for i: u16 = 0; i < 1000; i += 1 {
my_uuid := uuid.generate_v7_counter(i)
append(&batch, my_uuid)
}
Inputs:
- counter: A 12-bit value which should be incremented each time a UUID is generated in a batch.
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
Returns:
- result: The generated UUID.
*/
generate_v7_with_counter :: proc(counter: u16, timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
assert(counter <= 0x0fff, VERSION_7_BIG_COUNTER_ERROR)
unix_time_in_milliseconds := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 1e6
result = transmute(Identifier)(
cast(u128be)unix_time_in_milliseconds << VERSION_7_TIME_SHIFT |
cast(u128be)counter << VERSION_7_COUNTER_SHIFT
)
bytes_generated := rand.read(result[8:])
assert(bytes_generated == 8, "RNG failed to generate 8 bytes for UUID v7.")
result[VERSION_BYTE_INDEX] &= 0x0F
result[VERSION_BYTE_INDEX] |= 0x70
result[VARIANT_BYTE_INDEX] &= 0x3F
result[VARIANT_BYTE_INDEX] |= 0x80
return
}
generate_v7 :: proc {
generate_v7_basic,
generate_v7_with_counter,
}
/*
Generate a version 8 UUID using a specific hashing algorithm.
This UUID is generated by hashing a name with a namespace.
Note that all version 8 UUIDs are for experimental or vendor-specific use
cases, per the specification. This use case in particular is for offering a
non-legacy alternative to UUID versions 3 and 5.
Inputs:
- namespace: An `Identifier` that is used to represent the underlying namespace.
This can be any one of the `Namespace_*` values provided in this package.
- name: The byte slice which will be hashed with the namespace.
- algorithm: A hashing algorithm from `core:crypto/hash`.
Returns:
- result: The generated UUID.
Example:
import "core:crypto/hash"
import "core:encoding/uuid"
import "core:fmt"
main :: proc() {
my_uuid := uuid.generate_v8_hash(uuid.Namespace_DNS, "www.odin-lang.org", .SHA256)
my_uuid_string := uuid.to_string(my_uuid, context.temp_allocator)
fmt.println(my_uuid_string)
}
Output:
3730f688-4bff-8dce-9cbf-74a3960c5703
*/
generate_v8_hash_bytes :: proc(
namespace: Identifier,
name: []byte,
algorithm: hash.Algorithm,
) -> (
result: Identifier,
) {
// 128 bytes should be enough for the foreseeable future.
digest: [128]byte
assert(hash.DIGEST_SIZES[algorithm] >= 16, "Per RFC 9562, the hashing algorithm used must generate a digest of 128 bits or larger.")
assert(hash.DIGEST_SIZES[algorithm] < len(digest), "Digest size is too small for this algorithm. The buffer must be increased.")
hash_context: hash.Context
hash.init(&hash_context, algorithm)
mutable_namespace := namespace
hash.update(&hash_context, mutable_namespace[:])
hash.update(&hash_context, name[:])
hash.final(&hash_context, digest[:])
runtime.mem_copy_non_overlapping(&result, &digest, 16)
result[VERSION_BYTE_INDEX] &= 0x0F
result[VERSION_BYTE_INDEX] |= 0x80
result[VARIANT_BYTE_INDEX] &= 0x3F
result[VARIANT_BYTE_INDEX] |= 0x80
return
}
/*
Generate a version 8 UUID using a specific hashing algorithm.
This UUID is generated by hashing a name with a namespace.
Note that all version 8 UUIDs are for experimental or vendor-specific use
cases, per the specification. This use case in particular is for offering a
non-legacy alternative to UUID versions 3 and 5.
Inputs:
- namespace: An `Identifier` that is used to represent the underlying namespace.
This can be any one of the `Namespace_*` values provided in this package.
- name: The string which will be hashed with the namespace.
- algorithm: A hashing algorithm from `core:crypto/hash`.
Returns:
- result: The generated UUID.
Example:
import "core:crypto/hash"
import "core:encoding/uuid"
import "core:fmt"
main :: proc() {
my_uuid := uuid.generate_v8_hash(uuid.Namespace_DNS, "www.odin-lang.org", .SHA256)
my_uuid_string := uuid.to_string(my_uuid, context.temp_allocator)
fmt.println(my_uuid_string)
}
Output:
3730f688-4bff-8dce-9cbf-74a3960c5703
*/
generate_v8_hash_string :: proc(
namespace: Identifier,
name: string,
algorithm: hash.Algorithm,
) -> (
result: Identifier,
) {
return generate_v8_hash_bytes(namespace, transmute([]byte)name, algorithm)
}
generate_v8_hash :: proc {
generate_v8_hash_bytes,
generate_v8_hash_string,
}
+146
View File
@@ -0,0 +1,146 @@
/*
package uuid/legacy implements versions 3 and 5 of UUID generation, both of
which are using hashing algorithms (MD5 and SHA1, respectively) that are known
these days to no longer be secure.
*/
package uuid_legacy
import "base:runtime"
import "core:crypto/legacy/md5"
import "core:crypto/legacy/sha1"
import "core:encoding/uuid"
Identifier :: uuid.Identifier
VERSION_BYTE_INDEX :: uuid.VERSION_BYTE_INDEX
VARIANT_BYTE_INDEX :: uuid.VARIANT_BYTE_INDEX
/*
Generate a version 3 UUID.
This UUID is generated with a MD5 hash of a name and a namespace.
Inputs:
- namespace: An `Identifier` that is used to represent the underlying namespace.
This can be any one of the `Namespace_*` values provided in the `uuid` package.
- name: The byte slice which will be hashed with the namespace.
Returns:
- result: The generated UUID.
*/
generate_v3_bytes :: proc(
namespace: Identifier,
name: []byte,
) -> (
result: Identifier,
) {
namespace := namespace
ctx: md5.Context
md5.init(&ctx)
md5.update(&ctx, namespace[:])
md5.update(&ctx, name)
md5.final(&ctx, result[:])
result[VERSION_BYTE_INDEX] &= 0x0F
result[VERSION_BYTE_INDEX] |= 0x30
result[VARIANT_BYTE_INDEX] &= 0x3F
result[VARIANT_BYTE_INDEX] |= 0x80
return
}
/*
Generate a version 3 UUID.
This UUID is generated with a MD5 hash of a name and a namespace.
Inputs:
- namespace: An `Identifier` that is used to represent the underlying namespace.
This can be any one of the `Namespace_*` values provided in the `uuid` package.
- name: The string which will be hashed with the namespace.
Returns:
- result: The generated UUID.
*/
generate_v3_string :: proc(
namespace: Identifier,
name: string,
) -> (
result: Identifier,
) {
return generate_v3_bytes(namespace, transmute([]byte)name)
}
generate_v3 :: proc {
generate_v3_bytes,
generate_v3_string,
}
/*
Generate a version 5 UUID.
This UUID is generated with a SHA1 hash of a name and a namespace.
Inputs:
- namespace: An `Identifier` that is used to represent the underlying namespace.
This can be any one of the `Namespace_*` values provided in the `uuid` package.
- name: The byte slice which will be hashed with the namespace.
Returns:
- result: The generated UUID.
*/
generate_v5_bytes :: proc(
namespace: Identifier,
name: []byte,
) -> (
result: Identifier,
) {
namespace := namespace
digest: [sha1.DIGEST_SIZE]byte
ctx: sha1.Context
sha1.init(&ctx)
sha1.update(&ctx, namespace[:])
sha1.update(&ctx, name)
sha1.final(&ctx, digest[:])
runtime.mem_copy_non_overlapping(&result, &digest, 16)
result[VERSION_BYTE_INDEX] &= 0x0F
result[VERSION_BYTE_INDEX] |= 0x50
result[VARIANT_BYTE_INDEX] &= 0x3F
result[VARIANT_BYTE_INDEX] |= 0x80
return
}
/*
Generate a version 5 UUID.
This UUID is generated with a SHA1 hash of a name and a namespace.
Inputs:
- namespace: An `Identifier` that is used to represent the underlying namespace.
This can be any one of the `Namespace_*` values provided in the `uuid` package.
- name: The string which will be hashed with the namespace.
Returns:
- result: The generated UUID.
*/
generate_v5_string :: proc(
namespace: Identifier,
name: string,
) -> (
result: Identifier,
) {
return generate_v5_bytes(namespace, transmute([]byte)name)
}
generate_v5 :: proc {
generate_v5_bytes,
generate_v5_string,
}
+242
View File
@@ -0,0 +1,242 @@
package uuid
import "base:runtime"
import "core:time"
/*
Convert a string to a UUID.
Inputs:
- str: A string in the 8-4-4-4-12 format.
Returns:
- id: The converted identifier, or `nil` if there is an error.
- error: A description of the error, or `nil` if successful.
*/
read :: proc "contextless" (str: string) -> (id: Identifier, error: Read_Error) #no_bounds_check {
// Only exact-length strings are acceptable.
if len(str) != EXPECTED_LENGTH {
return {}, .Invalid_Length
}
// Check ahead to see if the separators are in the right places.
if str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-' {
return {}, .Invalid_Separator
}
read_nibble :: proc "contextless" (nibble: u8) -> u8 {
switch nibble {
case '0' ..= '9':
return nibble - '0'
case 'A' ..= 'F':
return nibble - 'A' + 10
case 'a' ..= 'f':
return nibble - 'a' + 10
case:
// Return an error value.
return 0xFF
}
}
index := 0
octet_index := 0
CHUNKS :: [5]int{8, 4, 4, 4, 12}
for chunk in CHUNKS {
for i := index; i < index + chunk; i += 2 {
high := read_nibble(str[i])
low := read_nibble(str[i + 1])
if high | low > 0xF {
return {}, .Invalid_Hexadecimal
}
id[octet_index] = low | high << 4
octet_index += 1
}
index += chunk + 1
}
return
}
/*
Get the version of a UUID.
Inputs:
- id: The identifier.
Returns:
- number: The version number.
*/
version :: proc "contextless" (id: Identifier) -> (number: int) #no_bounds_check {
return cast(int)(id[VERSION_BYTE_INDEX] & 0xF0 >> 4)
}
/*
Get the variant of a UUID.
Inputs:
- id: The identifier.
Returns:
- variant: The variant type.
*/
variant :: proc "contextless" (id: Identifier) -> (variant: Variant_Type) #no_bounds_check {
switch {
case id[VARIANT_BYTE_INDEX] & 0x80 == 0:
return .Reserved_Apollo_NCS
case id[VARIANT_BYTE_INDEX] & 0xC0 == 0x80:
return .RFC_4122
case id[VARIANT_BYTE_INDEX] & 0xE0 == 0xC0:
return .Reserved_Microsoft_COM
case id[VARIANT_BYTE_INDEX] & 0xF0 == 0xE0:
return .Reserved_Future
case:
return .Unknown
}
}
/*
Get the clock sequence of a version 1 or version 6 UUID.
Inputs:
- id: The identifier.
Returns:
- clock_seq: The 14-bit clock sequence field.
*/
clock_seq :: proc "contextless" (id: Identifier) -> (clock_seq: u16) {
return cast(u16)id[9] | cast(u16)id[8] & 0x3F << 8
}
/*
Get the node of a version 1 or version 6 UUID.
Inputs:
- id: The identifier.
Returns:
- node: The 48-bit spatially unique identifier.
*/
node :: proc "contextless" (id: Identifier) -> (node: [6]u8) {
mutable_id := id
runtime.mem_copy_non_overlapping(&node, &mutable_id[10], 6)
return
}
/*
Get the raw timestamp of a version 1 UUID.
Inputs:
- id: The identifier.
Returns:
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
*/
raw_time_v1 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
timestamp_octets: [8]u8
timestamp_octets[0] = id[0]
timestamp_octets[1] = id[1]
timestamp_octets[2] = id[2]
timestamp_octets[3] = id[3]
timestamp_octets[4] = id[4]
timestamp_octets[5] = id[5]
timestamp_octets[6] = id[6] << 4 | id[7] >> 4
timestamp_octets[7] = id[7] & 0xF
return cast(u64)transmute(u64le)timestamp_octets
}
/*
Get the timestamp of a version 1 UUID.
Inputs:
- id: The identifier.
Returns:
- timestamp: The timestamp of the UUID.
*/
time_v1 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
return time.from_nanoseconds(cast(i64)(raw_time_v1(id) - HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100)
}
/*
Get the raw timestamp of a version 6 UUID.
Inputs:
- id: The identifier.
Returns:
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
*/
raw_time_v6 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
temporary := transmute(u128be)id
timestamp |= cast(u64)(temporary & 0xFFFFFFFF_FFFF0000_00000000_00000000 >> 68)
timestamp |= cast(u64)(temporary & 0x00000000_00000FFF_00000000_00000000 >> 64)
return timestamp
}
/*
Get the timestamp of a version 6 UUID.
Inputs:
- id: The identifier.
Returns:
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
*/
time_v6 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
return time.from_nanoseconds(cast(i64)(raw_time_v6(id) - HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100)
}
/*
Get the raw timestamp of a version 7 UUID.
Inputs:
- id: The identifier.
Returns:
- timestamp: The timestamp, in milliseconds since the UNIX epoch.
*/
raw_time_v7 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
time_bits := transmute(u128be)id & VERSION_7_TIME_MASK
return cast(u64)(time_bits >> VERSION_7_TIME_SHIFT)
}
/*
Get the timestamp of a version 7 UUID.
Inputs:
- id: The identifier.
Returns:
- timestamp: The timestamp, in milliseconds since the UNIX epoch.
*/
time_v7 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
return time.from_nanoseconds(cast(i64)raw_time_v7(id) * 1e6)
}
/*
Get the 12-bit counter value of a version 7 UUID.
The UUID must have been generated with a counter, otherwise this procedure will
return random bits.
Inputs:
- id: The identifier.
Returns:
- counter: The 12-bit counter value.
*/
counter_v7 :: proc "contextless" (id: Identifier) -> (counter: u16) {
counter_bits := transmute(u128be)id & VERSION_7_COUNTER_MASK
return cast(u16)(counter_bits >> VERSION_7_COUNTER_SHIFT)
}
+89
View File
@@ -0,0 +1,89 @@
package uuid
import "base:runtime"
/*
Stamp a 128-bit integer as being a valid version 8 UUID.
Per the specification, all version 8 UUIDs are either for experimental or
vendor-specific purposes. This procedure allows for converting arbitrary data
into custom UUIDs.
Inputs:
- integer: Any integer type.
Returns:
- result: A valid version 8 UUID.
*/
stamp_v8_int :: proc(#any_int integer: u128) -> (result: Identifier) {
result = transmute(Identifier)cast(u128be)integer
result[VERSION_BYTE_INDEX] &= 0x0F
result[VERSION_BYTE_INDEX] |= 0x80
result[VARIANT_BYTE_INDEX] &= 0x3F
result[VARIANT_BYTE_INDEX] |= 0x80
return
}
/*
Stamp an array of 16 bytes as being a valid version 8 UUID.
Per the specification, all version 8 UUIDs are either for experimental or
vendor-specific purposes. This procedure allows for converting arbitrary data
into custom UUIDs.
Inputs:
- array: An array of 16 bytes.
Returns:
- result: A valid version 8 UUID.
*/
stamp_v8_array :: proc(array: [16]u8) -> (result: Identifier) {
result = Identifier(array)
result[VERSION_BYTE_INDEX] &= 0x0F
result[VERSION_BYTE_INDEX] |= 0x80
result[VARIANT_BYTE_INDEX] &= 0x3F
result[VARIANT_BYTE_INDEX] |= 0x80
return
}
/*
Stamp a slice of bytes as being a valid version 8 UUID.
If the slice is less than 16 bytes long, the data available will be used.
If it is longer than 16 bytes, only the first 16 will be used.
This procedure does not modify the underlying slice.
Per the specification, all version 8 UUIDs are either for experimental or
vendor-specific purposes. This procedure allows for converting arbitrary data
into custom UUIDs.
Inputs:
- slice: A slice of bytes.
Returns:
- result: A valid version 8 UUID.
*/
stamp_v8_slice :: proc(slice: []u8) -> (result: Identifier) {
runtime.mem_copy_non_overlapping(&result, &slice[0], min(16, len(slice)))
result[VERSION_BYTE_INDEX] &= 0x0F
result[VERSION_BYTE_INDEX] |= 0x80
result[VARIANT_BYTE_INDEX] &= 0x3F
result[VARIANT_BYTE_INDEX] |= 0x80
return
}
stamp_v8 :: proc {
stamp_v8_int,
stamp_v8_array,
stamp_v8_slice,
}
+131
View File
@@ -0,0 +1,131 @@
package uuid
import "base:runtime"
import "core:io"
import "core:strconv"
import "core:strings"
/*
Write a UUID in the 8-4-4-4-12 format.
This procedure performs error checking with every byte written.
If you can guarantee beforehand that your stream has enough space to hold the
UUID (32 bytes), then it is better to use `unsafe_write` instead as that will
be faster.
Inputs:
- w: A writable stream.
- id: The identifier to convert.
Returns:
- error: An `io` error, if one occurred, otherwise `nil`.
*/
write :: proc(w: io.Writer, id: Identifier) -> (error: io.Error) #no_bounds_check {
write_octet :: proc (w: io.Writer, octet: u8) -> io.Error #no_bounds_check {
high_nibble := octet >> 4
low_nibble := octet & 0xF
io.write_byte(w, strconv.digits[high_nibble]) or_return
io.write_byte(w, strconv.digits[low_nibble]) or_return
return nil
}
for index in 0 ..< 4 { write_octet(w, id[index]) or_return }
io.write_byte(w, '-') or_return
for index in 4 ..< 6 { write_octet(w, id[index]) or_return }
io.write_byte(w, '-') or_return
for index in 6 ..< 8 { write_octet(w, id[index]) or_return }
io.write_byte(w, '-') or_return
for index in 8 ..< 10 { write_octet(w, id[index]) or_return }
io.write_byte(w, '-') or_return
for index in 10 ..< 16 { write_octet(w, id[index]) or_return }
return nil
}
/*
Write a UUID in the 8-4-4-4-12 format.
This procedure performs no error checking on the underlying stream.
Inputs:
- w: A writable stream.
- id: The identifier to convert.
*/
unsafe_write :: proc(w: io.Writer, id: Identifier) #no_bounds_check {
write_octet :: proc (w: io.Writer, octet: u8) #no_bounds_check {
high_nibble := octet >> 4
low_nibble := octet & 0xF
io.write_byte(w, strconv.digits[high_nibble])
io.write_byte(w, strconv.digits[low_nibble])
}
for index in 0 ..< 4 { write_octet(w, id[index]) }
io.write_byte(w, '-')
for index in 4 ..< 6 { write_octet(w, id[index]) }
io.write_byte(w, '-')
for index in 6 ..< 8 { write_octet(w, id[index]) }
io.write_byte(w, '-')
for index in 8 ..< 10 { write_octet(w, id[index]) }
io.write_byte(w, '-')
for index in 10 ..< 16 { write_octet(w, id[index]) }
}
/*
Convert a UUID to a string in the 8-4-4-4-12 format.
*Allocates Using Provided Allocator*
Inputs:
- id: The identifier to convert.
- allocator: (default: context.allocator)
- loc: The caller location for debugging purposes (default: #caller_location)
Returns:
- str: The allocated and converted string.
- error: An optional allocator error if one occured, `nil` otherwise.
*/
to_string_allocated :: proc(
id: Identifier,
allocator := context.allocator,
loc := #caller_location,
) -> (
str: string,
error: runtime.Allocator_Error,
) #optional_allocator_error {
buf := make([]byte, EXPECTED_LENGTH, allocator, loc) or_return
builder := strings.builder_from_bytes(buf[:])
unsafe_write(strings.to_writer(&builder), id)
return strings.to_string(builder), nil
}
/*
Convert a UUID to a string in the 8-4-4-4-12 format.
Inputs:
- id: The identifier to convert.
- buffer: A byte buffer to store the result. Must be at least 32 bytes large.
- loc: The caller location for debugging purposes (default: #caller_location)
Returns:
- str: The converted string which will be stored in `buffer`.
*/
to_string_buffer :: proc(
id: Identifier,
buffer: []byte,
loc := #caller_location,
) -> (
str: string,
) {
assert(len(buffer) >= EXPECTED_LENGTH, "The buffer provided is not at least 32 bytes large.", loc)
builder := strings.builder_from_bytes(buffer)
unsafe_write(strings.to_writer(&builder), id)
return strings.to_string(builder)
}
to_string :: proc {
to_string_allocated,
to_string_buffer,
}
+5 -5
View File
@@ -33,7 +33,7 @@ print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error
written += fmt.wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident)
if len(doc.doctype.rest) > 0 {
fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
}
}
@@ -42,10 +42,10 @@ print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error
}
if len(doc.elements) > 0 {
fmt.wprintln(writer, " --- ")
print_element(writer, doc, 0)
fmt.wprintln(writer, " --- ")
}
fmt.wprintln(writer, " --- ")
print_element(writer, doc, 0)
fmt.wprintln(writer, " --- ")
}
return written, .None
}
+11 -32
View File
@@ -218,9 +218,7 @@ scan_identifier :: proc(t: ^Tokenizer) -> string {
for is_valid_identifier_rune(t.ch) {
advance_rune(t)
if t.ch == ':' {
/*
A namespaced attr can have at most two parts, `namespace:ident`.
*/
// A namespaced attr can have at most two parts, `namespace:ident`.
if namespaced {
break
}
@@ -268,14 +266,10 @@ scan_comment :: proc(t: ^Tokenizer) -> (comment: string, err: Error) {
return string(t.src[offset : t.offset - 1]), .None
}
/*
Skip CDATA
*/
// Skip CDATA
skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
if t.read_offset + len(CDATA_START) >= len(t.src) {
/*
Can't be the start of a CDATA tag.
*/
// Can't be the start of a CDATA tag.
return .None
}
@@ -290,9 +284,7 @@ skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
return .Premature_EOF
}
/*
Scan until the end of a CDATA tag.
*/
// Scan until the end of a CDATA tag.
if t.read_offset + len(CDATA_END) < len(t.src) {
if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
t.read_offset += len(CDATA_END)
@@ -319,14 +311,10 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close
case '<':
if peek_byte(t) == '!' {
if peek_byte(t, 1) == '[' {
/*
Might be the start of a CDATA tag.
*/
// Might be the start of a CDATA tag.
skip_cdata(t) or_return
} else if peek_byte(t, 1) == '-' && peek_byte(t, 2) == '-' {
/*
Comment start. Eat comment.
*/
// Comment start. Eat comment.
t.read_offset += 3
_ = scan_comment(t) or_return
}
@@ -342,17 +330,13 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close
}
if t.ch == close {
/*
If it's not a CDATA or comment, it's the end of this body.
*/
// If it's not a CDATA or comment, it's the end of this body.
break loop
}
advance_rune(t)
}
/*
Strip trailing whitespace.
*/
// Strip trailing whitespace.
lit := string(t.src[offset : t.offset])
end := len(lit)
@@ -369,11 +353,6 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close
if consume_close {
advance_rune(t)
}
/*
TODO: Handle decoding escape characters and unboxing CDATA.
*/
return lit, err
}
@@ -384,7 +363,7 @@ peek :: proc(t: ^Tokenizer) -> (token: Token) {
return token
}
scan :: proc(t: ^Tokenizer) -> Token {
scan :: proc(t: ^Tokenizer, multiline_string := false) -> Token {
skip_whitespace(t)
offset := t.offset
@@ -418,7 +397,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
case '"', '\'':
kind = .Invalid
lit, err = scan_string(t, t.offset, ch, true, false)
lit, err = scan_string(t, t.offset, ch, true, multiline_string)
if err == .None {
kind = .String
}
@@ -435,4 +414,4 @@ scan :: proc(t: ^Tokenizer) -> Token {
lit = string(t.src[offset : t.offset])
}
return Token{kind, lit, pos}
}
}
+14 -10
View File
@@ -203,9 +203,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
doc.elements = make([dynamic]Element, 1024, 1024, allocator)
// strings.intern_init(&doc.intern, allocator, allocator)
err = .Unexpected_Token
err = .Unexpected_Token
element, parent: Element_ID
open: Token
@@ -259,8 +257,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
case .Slash:
// Empty tag. Close it.
expect(t, .Gt) or_return
parent = doc.elements[element].parent
element = parent
parent = doc.elements[element].parent
element = parent
case:
error(t, t.offset, "Expected close tag, got: %#v\n", end_token)
@@ -276,8 +274,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
error(t, t.offset, "Mismatched Closing Tag. Expected %v, got %v\n", doc.elements[element].ident, ident.text)
return doc, .Mismatched_Closing_Tag
}
parent = doc.elements[element].parent
element = parent
parent = doc.elements[element].parent
element = parent
} else if open.kind == .Exclaim {
// <!
@@ -463,8 +461,8 @@ validate_options :: proc(options: Options) -> (validated: Options, err: Error) {
return validated, .None
}
expect :: proc(t: ^Tokenizer, kind: Token_Kind) -> (tok: Token, err: Error) {
tok = scan(t)
expect :: proc(t: ^Tokenizer, kind: Token_Kind, multiline_string := false) -> (tok: Token, err: Error) {
tok = scan(t, multiline_string=multiline_string)
if tok.kind == kind { return tok, .None }
error(t, t.offset, "Expected \"%v\", got \"%v\".", kind, tok.kind)
@@ -480,7 +478,13 @@ parse_attribute :: proc(doc: ^Document) -> (attr: Attribute, offset: int, err: E
offset = t.offset - len(key.text)
_ = expect(t, .Eq) or_return
value := expect(t, .String) or_return
value := expect(t, .String, multiline_string=true) or_return
normalized, normalize_err := entity.decode_xml(value.text, {.Normalize_Whitespace}, doc.allocator)
if normalize_err == .None {
append(&doc.strings_to_free, normalized)
value.text = normalized
}
attr.key = key.text
attr.val = value.text
+28
View File
@@ -0,0 +1,28 @@
BSD 3-Clause License
Copyright (c) 2024, Feoramund
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.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
+38
View File
@@ -0,0 +1,38 @@
package flags
import "core:time"
// Set to true to compile with support for core named types disabled, as a
// fallback in the event your platform does not support one of the types, or
// you have no need for them and want a smaller binary.
NO_CORE_NAMED_TYPES :: #config(ODIN_CORE_FLAGS_NO_CORE_NAMED_TYPES, false)
// Override support for parsing `time` types.
IMPORTING_TIME :: #config(ODIN_CORE_FLAGS_USE_TIME, time.IS_SUPPORTED)
// Override support for parsing `net` types.
// TODO: Update this when the BSDs are supported.
IMPORTING_NET :: #config(ODIN_CORE_FLAGS_USE_NET, ODIN_OS == .Windows || ODIN_OS == .Linux || ODIN_OS == .Darwin)
TAG_ARGS :: "args"
SUBTAG_NAME :: "name"
SUBTAG_POS :: "pos"
SUBTAG_REQUIRED :: "required"
SUBTAG_HIDDEN :: "hidden"
SUBTAG_VARIADIC :: "variadic"
SUBTAG_FILE :: "file"
SUBTAG_PERMS :: "perms"
SUBTAG_INDISTINCT :: "indistinct"
TAG_USAGE :: "usage"
UNDOCUMENTED_FLAG :: "<This flag has not been documented yet.>"
INTERNAL_VARIADIC_FLAG :: "varg"
RESERVED_HELP_FLAG :: "help"
RESERVED_HELP_FLAG_SHORT :: "h"
// If there are more than this number of flags in total, only the required and
// positional flags will be shown in the one-line usage summary.
ONE_LINE_FLAG_CUTOFF_COUNT :: 16
+181
View File
@@ -0,0 +1,181 @@
/*
package flags implements a command-line argument parser.
It works by using Odin's run-time type information to determine where and how
to store data on a struct provided by the program. Type conversion is handled
automatically and errors are reported with useful messages.
Command-Line Syntax:
Arguments are treated differently depending on how they're formatted.
The format is similar to the Odin binary's way of handling compiler flags.
```
type handling
------------ ------------------------
<positional> depends on struct layout
-<flag> set a bool true
-<flag:option> set flag to option
-<flag=option> set flag to option, alternative syntax
-<map>:<key>=<value> set map[key] to value
```
Struct Tags:
Users of the `core:encoding/json` package may be familiar with using tags to
annotate struct metadata. The same technique is used here to annotate where
arguments should go and which are required.
Under the `args` tag, there are the following subtags:
- `name=S`: set `S` as the flag's name.
- `pos=N`: place positional argument `N` into this flag.
- `hidden`: hide this flag from the usage documentation.
- `required`: cause verification to fail if this argument is not set.
- `variadic`: take all remaining arguments when set, UNIX-style only.
- `file`: for `os.Handle` types, file open mode.
- `perms`: for `os.Handle` types, file open permissions.
- `indistinct`: allow the setting of distinct types by their base type.
`required` may be given a range specifier in the following formats:
```
min
<max
min<max
```
`max` is not inclusive in this range, as noted by the less-than `<` sign, so if
you want to require 3 and only 3 arguments in a dynamic array, you would
specify `required=3<4`.
`variadic` may be given a number (`variadic=N`) above 1 to limit how many extra
arguments it consumes.
`file` determines the file open mode for an `os.Handle`.
It accepts a string of flags that can be mixed together:
- r: read
- w: write
- c: create, create the file if it doesn't exist
- a: append, add any new writes to the end of the file
- t: truncate, erase the file on open
`perms` determines the file open permissions for an `os.Handle`.
The permissions are represented by three numbers in octal format. The first
number is the owner, the second is the group, and the third is other. Read is
represented by 4, write by 2, and execute by 1.
These numbers are added together to get combined permissions. For example, 644
represents read/write for the owner, read for the group, and read for other.
Note that this may only have effect on UNIX-like platforms. By default, `perms`
is set to 444 when only reading and 644 when writing.
`indistinct` tells the parser that it's okay to treat distinct types as their
underlying base type. Normally, the parser will hand those types off to the
custom type setter (more about that later) if one is available, if it doesn't
know how to handle the type.
Usage Tag:
There is also the `usage` tag, which is a plain string to be printed alongside
the flag in the usage output. If `usage` contains a newline, it will be
properly aligned when printed.
All surrounding whitespace is trimmed when formatting with multiple lines.
Supported Flag Data Types:
- all booleans
- all integers
- all floats
- all enums
- all complex numbers
- all quaternions
- all bit_sets
- `string` and `cstring`
- `rune`
- `os.Handle`
- `time.Time`
- `datetime.DateTime`
- `net.Host_Or_Endpoint`,
- additional custom types, see Custom Types below
- `dynamic` arrays with element types of the above
- `map[string]`s or `map[cstring]`s with value types of the above
Validation:
The parser will ensure `required` arguments are set, if no errors occurred
during parsing. This is on by default.
Additionally, you may call `register_flag_checker` to set your own argument
validation procedure that will be called after the default checker.
Strict:
The parser will return on the first error and stop parsing. This is on by
default. Otherwise, all arguments that can be parsed, will be, and only the
last error is returned.
Error Messages:
All error message strings are allocated using the context's `temp_allocator`,
so if you need them to persist, make sure to clone the underlying `message`.
Help:
By default, `-h` and `-help` are reserved flags which raise their own error
type when set, allowing the program to handle the request differently from
other errors.
Custom Types:
You may specify your own type setter for program-specific structs and other
named types. Call `register_type_setter` with an appropriate proc before
calling any of the parsing procs.
A compliant `Custom_Type_Setter` must return three values:
- an error message if one occurred,
- a boolean indicating if the proc handles the type, and
- an `Allocator_Error` if any occurred.
If the setter does not handle the type, simply return without setting any of
the values.
UNIX-style:
This package also supports parsing arguments in a limited flavor of UNIX.
Odin and UNIX style are mutually exclusive, and which one to be used is chosen
at parse time.
```
--flag
--flag=argument
--flag argument
--flag argument repeating-argument
```
`-flag` may also be substituted for `--flag`.
Do note that map flags are not currently supported in this parsing style.
Example:
A complete example is given in the `example` subdirectory.
*/
package flags
+50
View File
@@ -0,0 +1,50 @@
package flags
import "core:os"
Parse_Error_Reason :: enum {
None,
// An extra positional argument was given, and there is no `varg` field.
Extra_Positional,
// The underlying type does not support the string value it is being set to.
Bad_Value,
// No flag was given by the user.
No_Flag,
// No value was given by the user.
No_Value,
// The flag on the struct is missing.
Missing_Flag,
// The type itself isn't supported.
Unsupported_Type,
}
// Raised during parsing, naturally.
Parse_Error :: struct {
reason: Unified_Parse_Error_Reason,
message: string,
}
// Raised during parsing.
// Provides more granular information than what just a string could hold.
Open_File_Error :: struct {
filename: string,
errno: os.Errno,
mode: int,
perms: int,
}
// Raised during parsing.
Help_Request :: distinct bool
// Raised after parsing, during validation.
Validation_Error :: struct {
message: string,
}
Error :: union {
Parse_Error,
Open_File_Error,
Help_Request,
Validation_Error,
}
+9
View File
@@ -0,0 +1,9 @@
//+build freebsd, netbsd, openbsd
package flags
import "base:runtime"
Unified_Parse_Error_Reason :: union #shared_nil {
Parse_Error_Reason,
runtime.Allocator_Error,
}
+11
View File
@@ -0,0 +1,11 @@
//+build !freebsd !netbsd !openbsd
package flags
import "base:runtime"
import "core:net"
Unified_Parse_Error_Reason :: union #shared_nil {
Parse_Error_Reason,
runtime.Allocator_Error,
net.Parse_Endpoint_Error,
}
+132
View File
@@ -0,0 +1,132 @@
package core_flags_example
import "base:runtime"
import "core:flags"
import "core:fmt"
import "core:net"
import "core:os"
import "core:time/datetime"
Fixed_Point1_1 :: struct {
integer: u8,
fractional: u8,
}
Optimization_Level :: enum {
Slow,
Fast,
Warp_Speed,
Ludicrous_Speed,
}
// It's simple but powerful.
my_custom_type_setter :: proc(
data: rawptr,
data_type: typeid,
unparsed_value: string,
args_tag: string,
) -> (
error: string,
handled: bool,
alloc_error: runtime.Allocator_Error,
) {
if data_type == Fixed_Point1_1 {
handled = true
ptr := cast(^Fixed_Point1_1)data
// precision := flags.get_subtag(args_tag, "precision")
if len(unparsed_value) == 3 {
ptr.integer = unparsed_value[0] - '0'
ptr.fractional = unparsed_value[2] - '0'
} else {
error = "Incorrect format. Must be in the form of `i.f`."
}
// Perform sanity checking here in the type parsing phase.
//
// The validation phase is flag-specific.
if !(0 <= ptr.integer && ptr.integer < 10) || !(0 <= ptr.fractional && ptr.fractional < 10) {
error = "Incorrect format. Must be between `0.0` and `9.9`."
}
}
return
}
my_custom_flag_checker :: proc(
model: rawptr,
name: string,
value: any,
args_tag: string,
) -> (error: string) {
if name == "iterations" {
v := value.(int)
if !(1 <= v && v < 5) {
error = "Iterations only supports 1 ..< 5."
}
}
return
}
Distinct_Int :: distinct int
main :: proc() {
Options :: struct {
file: os.Handle `args:"pos=0,required,file=r" usage:"Input file."`,
output: os.Handle `args:"pos=1,file=cw" usage:"Output file."`,
hub: net.Host_Or_Endpoint `usage:"Internet address to contact for updates."`,
schedule: datetime.DateTime `usage:"Launch tasks at this time."`,
opt: Optimization_Level `usage:"Optimization level."`,
todo: [dynamic]string `usage:"Todo items."`,
accuracy: Fixed_Point1_1 `args:"required" usage:"Lenience in FLOP calculations."`,
iterations: int `usage:"Run this many times."`,
// Note how the parser will transform this flag's name into `special-int`.
special_int: Distinct_Int `args:"indistinct" usage:"Able to set distinct types."`,
quat: quaternion256,
bits: bit_set[0..<8],
// Many different requirement styles:
// gadgets: [dynamic]string `args:"required=1" usage:"gadgets"`,
// widgets: [dynamic]string `args:"required=<3" usage:"widgets"`,
// foos: [dynamic]string `args:"required=2<4"`,
// bars: [dynamic]string `args:"required=3<4"`,
// bots: [dynamic]string `args:"required"`,
// (Maps) Only available in Odin style:
// assignments: map[string]u8 `args:"name=assign" usage:"Number of jobs per worker."`,
// (Variadic) Only available in UNIX style:
// bots: [dynamic]string `args:"variadic=2,required"`,
verbose: bool `usage:"Show verbose output."`,
debug: bool `args:"hidden" usage:"print debug info"`,
varg: [dynamic]string `usage:"Any extra arguments go here."`,
}
opt: Options
style : flags.Parsing_Style = .Odin
flags.register_type_setter(my_custom_type_setter)
flags.register_flag_checker(my_custom_flag_checker)
flags.parse_or_exit(&opt, os.args, style)
fmt.printfln("%#v", opt)
if opt.output != 0 {
os.write_string(opt.output, "Hellope!\n")
}
}
+262
View File
@@ -0,0 +1,262 @@
//+private
package flags
import "base:intrinsics"
@require import "base:runtime"
import "core:container/bit_array"
@require import "core:fmt"
@require import "core:mem"
import "core:reflect"
@require import "core:strconv"
@require import "core:strings"
// Push a positional argument onto a data struct, checking for specified
// positionals first before adding it to a fallback field.
@(optimization_mode="size")
push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: string) -> (error: Error) {
if bit_array.get(&parser.filled_pos, parser.filled_pos.max_index) {
// The max index is set, which means we're out of space.
// Add one free bit by setting the index above to false.
bit_array.set(&parser.filled_pos, 1 + parser.filled_pos.max_index, false)
}
pos: int = ---
{
iter := bit_array.make_iterator(&parser.filled_pos)
ok: bool
pos, ok = bit_array.iterate_by_unset(&iter)
// This may be an allocator error.
assert(ok, "Unable to find a free spot in the positional bit_array.")
}
field, index, has_pos_assigned := get_field_by_pos(model, pos)
if !has_pos_assigned {
when intrinsics.type_has_field(T, INTERNAL_VARIADIC_FLAG) {
// Add it to the fallback array.
field = reflect.struct_field_by_name(T, INTERNAL_VARIADIC_FLAG)
} else {
return Parse_Error {
.Extra_Positional,
fmt.tprintf("Got extra positional argument `%s` with nowhere to store it.", arg),
}
}
}
ptr := cast(rawptr)(cast(uintptr)model + field.offset)
args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
field_name := get_field_name(field)
error = parse_and_set_pointer_by_type(ptr, arg, field.type, args_tag)
#partial switch &specific_error in error {
case Parse_Error:
specific_error.message = fmt.tprintf("Unable to set positional #%i (%s) of type %v to `%s`.%s%s",
pos,
field_name,
field.type,
arg,
" " if len(specific_error.message) > 0 else "",
specific_error.message)
case nil:
bit_array.set(&parser.filled_pos, pos)
bit_array.set(&parser.fields_set, index)
}
return
}
register_field :: proc(parser: ^Parser, field: reflect.Struct_Field, index: int) {
if pos, ok := get_field_pos(field); ok {
bit_array.set(&parser.filled_pos, pos)
}
bit_array.set(&parser.fields_set, index)
}
// Set a `-flag` argument, Odin-style.
@(optimization_mode="size")
set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Error) {
// We make a special case for help requests.
switch name {
case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
return Help_Request{}
}
field, index := get_field_by_name(model, name) or_return
#partial switch specific_type_info in field.type.variant {
case runtime.Type_Info_Boolean:
ptr := cast(^bool)(cast(uintptr)model + field.offset)
ptr^ = true
case:
return Parse_Error {
.Bad_Value,
fmt.tprintf("Unable to set `%s` of type %v to true.", name, field.type),
}
}
register_field(parser, field, index)
return
}
// Set a `-flag` argument, UNIX-style.
@(optimization_mode="size")
set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args: int, error: Error) {
// We make a special case for help requests.
switch name {
case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
return 0, Help_Request{}
}
field, index := get_field_by_name(model, name) or_return
#partial switch specific_type_info in field.type.variant {
case runtime.Type_Info_Boolean:
ptr := cast(^bool)(cast(uintptr)model + field.offset)
ptr^ = true
case runtime.Type_Info_Dynamic_Array:
future_args = 1
if tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
if length, is_variadic := get_struct_subtag(tag, SUBTAG_VARIADIC); is_variadic {
// Variadic arrays may specify how many arguments they consume at once.
// Otherwise, they take everything that's left.
if value, value_ok := strconv.parse_u64_of_base(length, 10); value_ok {
future_args = cast(int)value
} else {
future_args = max(int)
}
}
}
case:
// `--flag`, waiting on its value.
future_args = 1
}
register_field(parser, field, index)
return
}
// Set a `-flag:option` argument.
@(optimization_mode="size")
set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error: Error) {
field, index := get_field_by_name(model, name) or_return
if len(option) == 0 {
return Parse_Error {
.No_Value,
fmt.tprintf("Setting `%s` to an empty value is meaningless.", name),
}
}
// Guard against incorrect syntax.
#partial switch specific_type_info in field.type.variant {
case runtime.Type_Info_Map:
return Parse_Error {
.No_Value,
fmt.tprintf("Unable to set `%s` of type %v to `%s`. Are you missing an `=`? The correct format is `map:key=value`.", name, field.type, option),
}
}
ptr := cast(rawptr)(cast(uintptr)model + field.offset)
args_tag := reflect.struct_tag_get(field.tag, TAG_ARGS)
error = parse_and_set_pointer_by_type(ptr, option, field.type, args_tag)
#partial switch &specific_error in error {
case Parse_Error:
specific_error.message = fmt.tprintf("Unable to set `%s` of type %v to `%s`.%s%s",
name,
field.type,
option,
" " if len(specific_error.message) > 0 else "",
specific_error.message)
case nil:
register_field(parser, field, index)
}
return
}
// Set a `-map:key=value` argument.
@(optimization_mode="size")
set_key_value :: proc(model: ^$T, parser: ^Parser, name, key, value: string) -> (error: Error) {
field, index := get_field_by_name(model, name) or_return
#partial switch specific_type_info in field.type.variant {
case runtime.Type_Info_Map:
key := key
key_ptr := cast(rawptr)&key
key_cstr: cstring
if reflect.is_cstring(specific_type_info.key) {
// We clone the key here, because it's liable to be a slice of an
// Odin string, and we need to put a NUL terminator in it.
key_cstr = strings.clone_to_cstring(key)
key_ptr = &key_cstr
}
defer if key_cstr != nil {
delete(key_cstr)
}
raw_map := (^runtime.Raw_Map)(cast(uintptr)model + field.offset)
hash := specific_type_info.map_info.key_hasher(key_ptr, runtime.map_seed(raw_map^))
backing_alloc := false
elem_backing: []byte
value_ptr: rawptr
if raw_map.allocator.procedure == nil {
raw_map.allocator = context.allocator
} else {
value_ptr = runtime.__dynamic_map_get(raw_map,
specific_type_info.map_info,
hash,
key_ptr,
)
}
if value_ptr == nil {
alloc_error: runtime.Allocator_Error = ---
elem_backing, alloc_error = mem.alloc_bytes(specific_type_info.value.size, specific_type_info.value.align)
if elem_backing == nil {
return Parse_Error {
alloc_error,
"Failed to allocate element backing for map value.",
}
}
backing_alloc = true
value_ptr = raw_data(elem_backing)
}
args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
error = parse_and_set_pointer_by_type(value_ptr, value, specific_type_info.value, args_tag)
#partial switch &specific_error in error {
case Parse_Error:
specific_error.message = fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.%s%s",
name,
field.type,
key,
value,
" " if len(specific_error.message) > 0 else "",
specific_error.message)
}
if backing_alloc {
runtime.__dynamic_map_set(raw_map,
specific_type_info.map_info,
hash,
key_ptr,
value_ptr,
)
delete(elem_backing)
}
register_field(parser, field, index)
return
}
return Parse_Error {
.Bad_Value,
fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.", name, field.type, key, value),
}
}
+170
View File
@@ -0,0 +1,170 @@
//+private
package flags
import "core:container/bit_array"
import "core:strconv"
import "core:strings"
// Used to group state together.
Parser :: struct {
// `fields_set` tracks which arguments have been set.
// It uses their struct field index.
fields_set: bit_array.Bit_Array,
// `filled_pos` tracks which arguments have been filled into positional
// spots, much like how `fmt` treats them.
filled_pos: bit_array.Bit_Array,
}
parse_one_odin_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> (error: Error) {
arg := arg
if strings.has_prefix(arg, "-") {
arg = arg[1:]
flag: string
assignment_rune: rune
find_assignment: for r, i in arg {
switch r {
case ':', '=':
assignment_rune = r
flag = arg[:i]
arg = arg[1 + i:]
break find_assignment
case:
continue find_assignment
}
}
if assignment_rune == 0 {
if len(arg) == 0 {
return Parse_Error {
.No_Flag,
"No flag was given.",
}
}
// -flag
set_odin_flag(model, parser, arg) or_return
} else if assignment_rune == ':' {
// -flag:option <OR> -map:key=value
error = set_option(model, parser, flag, arg)
if error != nil {
// -flag:option did not work, so this may be a -map:key=value set.
find_equals: for r, i in arg {
if r == '=' {
key := arg[:i]
arg = arg[1 + i:]
error = set_key_value(model, parser, flag, key, arg)
break find_equals
}
}
}
} else {
// -flag=option, alternative syntax
set_option(model, parser, flag, arg) or_return
}
} else {
// positional
error = push_positional(model, parser, arg)
}
return
}
parse_one_unix_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> (
future_args: int,
current_flag: string,
error: Error,
) {
arg := arg
if strings.has_prefix(arg, "-") {
// -flag
arg = arg[1:]
if strings.has_prefix(arg, "-") {
// Allow `--` to function as `-`.
arg = arg[1:]
if len(arg) == 0 {
// `--`, and only `--`.
// Everything from now on will be treated as an argument.
future_args = max(int)
current_flag = INTERNAL_VARIADIC_FLAG
return
}
}
flag: string
find_assignment: for r, i in arg {
if r == '=' {
// --flag=option
flag = arg[:i]
arg = arg[1 + i:]
error = set_option(model, parser, flag, arg)
return
}
}
// --flag option, potentially
future_args = set_unix_flag(model, parser, arg) or_return
current_flag = arg
} else {
// positional
error = push_positional(model, parser, arg)
}
return
}
// Parse a number of requirements specifier.
//
// Examples:
//
// `min`
// `<max`
// `min<max`
parse_requirements :: proc(str: string) -> (minimum, maximum: int, ok: bool) {
if len(str) == 0 {
return 1, max(int), true
}
if less_than := strings.index_byte(str, '<'); less_than != -1 {
if len(str) == 1 {
return 0, 0, false
}
#no_bounds_check left := str[:less_than]
#no_bounds_check right := str[1 + less_than:]
if left_value, parse_ok := strconv.parse_u64_of_base(left, 10); parse_ok {
minimum = cast(int)left_value
} else if len(left) > 0 {
return 0, 0, false
}
if right_value, parse_ok := strconv.parse_u64_of_base(right, 10); parse_ok {
maximum = cast(int)right_value
} else if len(right) > 0 {
return 0, 0, false
} else {
maximum = max(int)
}
} else {
if value, parse_ok := strconv.parse_u64_of_base(str, 10); parse_ok {
minimum = cast(int)value
maximum = max(int)
} else {
return 0, 0, false
}
}
ok = true
return
}
+529
View File
@@ -0,0 +1,529 @@
//+private
package flags
import "base:intrinsics"
import "base:runtime"
import "core:fmt"
import "core:mem"
import "core:os"
import "core:reflect"
import "core:strconv"
import "core:strings"
@require import "core:time"
@require import "core:time/datetime"
import "core:unicode/utf8"
@(optimization_mode="size")
parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info) -> bool {
bounded_int :: proc(value, min, max: i128) -> (result: i128, ok: bool) {
return value, min <= value && value <= max
}
bounded_uint :: proc(value, max: u128) -> (result: u128, ok: bool) {
return value, value <= max
}
// NOTE(Feoramund): This procedure has been written with the goal in mind
// of generating the least amount of assembly, given that this library is
// likely to be called once and forgotten.
//
// I've rewritten the switch tables below in 3 different ways, and the
// current one generates the least amount of code for me on Linux AMD64.
//
// The other two ways were:
//
// - the original implementation: use of parametric polymorphism which led
// to dozens of functions generated, one for each type.
//
// - a `value, ok` assignment statement with the `or_return` done at the
// end of the switch, instead of inline.
//
// This seems to be the smallest way for now.
#partial switch specific_type_info in type_info.variant {
case runtime.Type_Info_Integer:
if specific_type_info.signed {
value := strconv.parse_i128(str) or_return
switch type_info.id {
case i8: (^i8) (ptr)^ = cast(i8) bounded_int(value, cast(i128)min(i8), cast(i128)max(i8) ) or_return
case i16: (^i16) (ptr)^ = cast(i16) bounded_int(value, cast(i128)min(i16), cast(i128)max(i16) ) or_return
case i32: (^i32) (ptr)^ = cast(i32) bounded_int(value, cast(i128)min(i32), cast(i128)max(i32) ) or_return
case i64: (^i64) (ptr)^ = cast(i64) bounded_int(value, cast(i128)min(i64), cast(i128)max(i64) ) or_return
case i128: (^i128) (ptr)^ = value
case int: (^int) (ptr)^ = cast(int) bounded_int(value, cast(i128)min(int), cast(i128)max(int) ) or_return
case i16le: (^i16le) (ptr)^ = cast(i16le) bounded_int(value, cast(i128)min(i16le), cast(i128)max(i16le) ) or_return
case i32le: (^i32le) (ptr)^ = cast(i32le) bounded_int(value, cast(i128)min(i32le), cast(i128)max(i32le) ) or_return
case i64le: (^i64le) (ptr)^ = cast(i64le) bounded_int(value, cast(i128)min(i64le), cast(i128)max(i64le) ) or_return
case i128le: (^i128le)(ptr)^ = cast(i128le) bounded_int(value, cast(i128)min(i128le), cast(i128)max(i128le)) or_return
case i16be: (^i16be) (ptr)^ = cast(i16be) bounded_int(value, cast(i128)min(i16be), cast(i128)max(i16be) ) or_return
case i32be: (^i32be) (ptr)^ = cast(i32be) bounded_int(value, cast(i128)min(i32be), cast(i128)max(i32be) ) or_return
case i64be: (^i64be) (ptr)^ = cast(i64be) bounded_int(value, cast(i128)min(i64be), cast(i128)max(i64be) ) or_return
case i128be: (^i128be)(ptr)^ = cast(i128be) bounded_int(value, cast(i128)min(i128be), cast(i128)max(i128be)) or_return
}
} else {
value := strconv.parse_u128(str) or_return
switch type_info.id {
case u8: (^u8) (ptr)^ = cast(u8) bounded_uint(value, cast(u128)max(u8) ) or_return
case u16: (^u16) (ptr)^ = cast(u16) bounded_uint(value, cast(u128)max(u16) ) or_return
case u32: (^u32) (ptr)^ = cast(u32) bounded_uint(value, cast(u128)max(u32) ) or_return
case u64: (^u64) (ptr)^ = cast(u64) bounded_uint(value, cast(u128)max(u64) ) or_return
case u128: (^u128) (ptr)^ = value
case uint: (^uint) (ptr)^ = cast(uint) bounded_uint(value, cast(u128)max(uint) ) or_return
case uintptr: (^uintptr)(ptr)^ = cast(uintptr) bounded_uint(value, cast(u128)max(uintptr)) or_return
case u16le: (^u16le) (ptr)^ = cast(u16le) bounded_uint(value, cast(u128)max(u16le) ) or_return
case u32le: (^u32le) (ptr)^ = cast(u32le) bounded_uint(value, cast(u128)max(u32le) ) or_return
case u64le: (^u64le) (ptr)^ = cast(u64le) bounded_uint(value, cast(u128)max(u64le) ) or_return
case u128le: (^u128le) (ptr)^ = cast(u128le) bounded_uint(value, cast(u128)max(u128le) ) or_return
case u16be: (^u16be) (ptr)^ = cast(u16be) bounded_uint(value, cast(u128)max(u16be) ) or_return
case u32be: (^u32be) (ptr)^ = cast(u32be) bounded_uint(value, cast(u128)max(u32be) ) or_return
case u64be: (^u64be) (ptr)^ = cast(u64be) bounded_uint(value, cast(u128)max(u64be) ) or_return
case u128be: (^u128be) (ptr)^ = cast(u128be) bounded_uint(value, cast(u128)max(u128be) ) or_return
}
}
case runtime.Type_Info_Rune:
if utf8.rune_count_in_string(str) != 1 {
return false
}
(^rune)(ptr)^ = utf8.rune_at_pos(str, 0)
case runtime.Type_Info_Float:
value := strconv.parse_f64(str) or_return
switch type_info.id {
case f16: (^f16) (ptr)^ = cast(f16) value
case f32: (^f32) (ptr)^ = cast(f32) value
case f64: (^f64) (ptr)^ = value
case f16le: (^f16le)(ptr)^ = cast(f16le) value
case f32le: (^f32le)(ptr)^ = cast(f32le) value
case f64le: (^f64le)(ptr)^ = cast(f64le) value
case f16be: (^f16be)(ptr)^ = cast(f16be) value
case f32be: (^f32be)(ptr)^ = cast(f32be) value
case f64be: (^f64be)(ptr)^ = cast(f64be) value
}
case runtime.Type_Info_Complex:
value := strconv.parse_complex128(str) or_return
switch type_info.id {
case complex32: (^complex32) (ptr)^ = (complex32)(value)
case complex64: (^complex64) (ptr)^ = (complex64)(value)
case complex128: (^complex128)(ptr)^ = value
}
case runtime.Type_Info_Quaternion:
value := strconv.parse_quaternion256(str) or_return
switch type_info.id {
case quaternion64: (^quaternion64) (ptr)^ = (quaternion64)(value)
case quaternion128: (^quaternion128)(ptr)^ = (quaternion128)(value)
case quaternion256: (^quaternion256)(ptr)^ = value
}
case runtime.Type_Info_String:
if specific_type_info.is_cstring {
cstr_ptr := (^cstring)(ptr)
if cstr_ptr != nil {
// Prevent memory leaks from us setting this value multiple times.
delete(cstr_ptr^)
}
cstr_ptr^ = strings.clone_to_cstring(str)
} else {
(^string)(ptr)^ = str
}
case runtime.Type_Info_Boolean:
value := strconv.parse_bool(str) or_return
switch type_info.id {
case bool: (^bool)(ptr)^ = value
case b8: (^b8) (ptr)^ = b8(value)
case b16: (^b16) (ptr)^ = b16(value)
case b32: (^b32) (ptr)^ = b32(value)
case b64: (^b64) (ptr)^ = b64(value)
}
case runtime.Type_Info_Bit_Set:
// Parse a string of 1's and 0's, from left to right,
// least significant bit to most significant bit.
value: u128
// NOTE: `upper` is inclusive, i.e: `0..=31`
max_bit_index := u128(1 + specific_type_info.upper - specific_type_info.lower)
bit_index := u128(0)
#no_bounds_check for string_index in 0..<uint(len(str)) {
if bit_index == max_bit_index {
// The string's too long for this bit_set.
return false
}
switch str[string_index] {
case '1':
value |= 1 << bit_index
bit_index += 1
case '0':
bit_index += 1
continue
case '_':
continue
case:
return false
}
}
if specific_type_info.underlying != nil {
set_unbounded_integer_by_type(ptr, value, specific_type_info.underlying.id)
} else {
switch 8*type_info.size {
case 8: (^u8) (ptr)^ = cast(u8) value
case 16: (^u16) (ptr)^ = cast(u16) value
case 32: (^u32) (ptr)^ = cast(u32) value
case 64: (^u64) (ptr)^ = cast(u64) value
case 128: (^u128)(ptr)^ = value
}
}
case:
fmt.panicf("Unsupported base data type: %v", specific_type_info)
}
return true
}
// This proc exists to make error handling easier, since everything in the base
// type one above works on booleans. It's a simple parsing error if it's false.
//
// However, here we have to be more careful about how we handle errors,
// especially with files.
//
// We want to provide as informative as an error as we can.
@(optimization_mode="size", disabled=NO_CORE_NAMED_TYPES)
parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type: typeid, arg_tag: string, out_error: ^Error) {
// Core types currently supported:
//
// - os.Handle
// - time.Time
// - datetime.DateTime
// - net.Host_Or_Endpoint
GENERIC_RFC_3339_ERROR :: "Invalid RFC 3339 string. Try this format: `yyyy-mm-ddThh:mm:ssZ`, for example `2024-02-29T16:30:00Z`."
out_error^ = nil
if data_type == os.Handle {
// NOTE: `os` is hopefully available everywhere, even if it might panic on some calls.
wants_read := false
wants_write := false
mode: int
if file, ok := get_struct_subtag(arg_tag, SUBTAG_FILE); ok {
for i in 0..<len(file) {
#no_bounds_check switch file[i] {
case 'r': wants_read = true
case 'w': wants_write = true
case 'c': mode |= os.O_CREATE
case 'a': mode |= os.O_APPEND
case 't': mode |= os.O_TRUNC
}
}
}
// Sane default.
// owner/group/other: r--r--r--
perms: int = 0o444
if wants_read && wants_write {
mode |= os.O_RDWR
perms |= 0o200
} else if wants_write {
mode |= os.O_WRONLY
perms |= 0o200
} else {
mode |= os.O_RDONLY
}
if permstr, ok := get_struct_subtag(arg_tag, SUBTAG_PERMS); ok {
if value, parse_ok := strconv.parse_u64_of_base(permstr, 8); parse_ok {
perms = int(value)
}
}
handle, errno := os.open(str, mode, perms)
if errno != 0 {
// NOTE(Feoramund): os.Errno is system-dependent, and there's
// currently no good way to translate them all into strings.
//
// The upcoming `os2` package will hopefully solve this.
//
// We can at least provide the number for now, so the user can look
// it up.
out_error^ = Open_File_Error {
str,
errno,
mode,
perms,
}
return
}
(^os.Handle)(ptr)^ = handle
return
}
when IMPORTING_TIME {
if data_type == time.Time {
// NOTE: The leap second data is discarded.
res, consumed := time.rfc3339_to_time_utc(str)
if consumed == 0 {
// The RFC 3339 parsing facilities provide no indication as to what
// went wrong, so just treat it as a regular parsing error.
out_error^ = Parse_Error {
.Bad_Value,
GENERIC_RFC_3339_ERROR,
}
return
}
(^time.Time)(ptr)^ = res
return
} else if data_type == datetime.DateTime {
// NOTE: The UTC offset and leap second data are discarded.
res, _, _, consumed := time.rfc3339_to_components(str)
if consumed == 0 {
out_error^ = Parse_Error {
.Bad_Value,
GENERIC_RFC_3339_ERROR,
}
return
}
(^datetime.DateTime)(ptr)^ = res
return
}
}
when IMPORTING_NET {
if try_net_parse_workaround(data_type, str, ptr, out_error) {
return
}
}
out_error ^= Parse_Error {
// The caller will add more details.
.Unsupported_Type,
"",
}
}
@(optimization_mode="size")
set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) {
switch data_type {
case i8: (^i8) (ptr)^ = cast(i8) value
case i16: (^i16) (ptr)^ = cast(i16) value
case i32: (^i32) (ptr)^ = cast(i32) value
case i64: (^i64) (ptr)^ = cast(i64) value
case i128: (^i128) (ptr)^ = cast(i128) value
case int: (^int) (ptr)^ = cast(int) value
case i16le: (^i16le) (ptr)^ = cast(i16le) value
case i32le: (^i32le) (ptr)^ = cast(i32le) value
case i64le: (^i64le) (ptr)^ = cast(i64le) value
case i128le: (^i128le) (ptr)^ = cast(i128le) value
case i16be: (^i16be) (ptr)^ = cast(i16be) value
case i32be: (^i32be) (ptr)^ = cast(i32be) value
case i64be: (^i64be) (ptr)^ = cast(i64be) value
case i128be: (^i128be) (ptr)^ = cast(i128be) value
case u8: (^u8) (ptr)^ = cast(u8) value
case u16: (^u16) (ptr)^ = cast(u16) value
case u32: (^u32) (ptr)^ = cast(u32) value
case u64: (^u64) (ptr)^ = cast(u64) value
case u128: (^u128) (ptr)^ = cast(u128) value
case uint: (^uint) (ptr)^ = cast(uint) value
case uintptr: (^uintptr)(ptr)^ = cast(uintptr) value
case u16le: (^u16le) (ptr)^ = cast(u16le) value
case u32le: (^u32le) (ptr)^ = cast(u32le) value
case u64le: (^u64le) (ptr)^ = cast(u64le) value
case u128le: (^u128le) (ptr)^ = cast(u128le) value
case u16be: (^u16be) (ptr)^ = cast(u16be) value
case u32be: (^u32be) (ptr)^ = cast(u32be) value
case u64be: (^u64be) (ptr)^ = cast(u64be) value
case u128be: (^u128be) (ptr)^ = cast(u128be) value
case rune: (^rune) (ptr)^ = cast(rune) value
case:
fmt.panicf("Unsupported integer backing type: %v", data_type)
}
}
@(optimization_mode="size")
parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info, arg_tag: string) -> (error: Error) {
#partial switch specific_type_info in type_info.variant {
case runtime.Type_Info_Named:
if global_custom_type_setter != nil {
// The program gets to go first.
error_message, handled, alloc_error := global_custom_type_setter(ptr, type_info.id, str, arg_tag)
if alloc_error != nil {
// There was an allocation error. Bail out.
return Parse_Error {
alloc_error,
"Custom type setter encountered allocation error.",
}
}
if handled {
// The program handled the type.
if len(error_message) != 0 {
// However, there was an error. Pass it along.
error = Parse_Error {
.Bad_Value,
error_message,
}
}
return
}
}
// Might be a named enum. Need to check here first, since we handle all enums.
if enum_type_info, is_enum := specific_type_info.base.variant.(runtime.Type_Info_Enum); is_enum {
if value, ok := reflect.enum_from_name_any(type_info.id, str); ok {
set_unbounded_integer_by_type(ptr, value, enum_type_info.base.id)
} else {
return Parse_Error {
.Bad_Value,
fmt.tprintf("Invalid value name. Valid names are: %s", enum_type_info.names),
}
}
} else {
parse_and_set_pointer_by_named_type(ptr, str, type_info.id, arg_tag, &error)
if error != nil {
// So far, it's none of the types that we recognize.
// Check to see if we can set it by base type, if allowed.
if _, is_indistinct := get_struct_subtag(arg_tag, SUBTAG_INDISTINCT); is_indistinct {
return parse_and_set_pointer_by_type(ptr, str, specific_type_info.base, arg_tag)
}
}
}
case runtime.Type_Info_Dynamic_Array:
ptr := cast(^runtime.Raw_Dynamic_Array)ptr
// Try to convert the value first.
elem_backing, alloc_error := mem.alloc_bytes(specific_type_info.elem.size, specific_type_info.elem.align)
if alloc_error != nil {
return Parse_Error {
alloc_error,
"Failed to allocate element backing for dynamic array.",
}
}
defer delete(elem_backing)
parse_and_set_pointer_by_type(raw_data(elem_backing), str, specific_type_info.elem, arg_tag) or_return
if !runtime.__dynamic_array_resize(ptr, specific_type_info.elem.size, specific_type_info.elem.align, ptr.len + 1) {
// NOTE: This is purely an assumption that it's OOM.
// Regardless, the resize failed.
return Parse_Error {
runtime.Allocator_Error.Out_Of_Memory,
"Failed to resize dynamic array.",
}
}
subptr := rawptr(
uintptr(ptr.data) +
uintptr((ptr.len - 1) * specific_type_info.elem.size))
mem.copy(subptr, raw_data(elem_backing), len(elem_backing))
case runtime.Type_Info_Enum:
// This is a nameless enum.
// The code here is virtually the same as above for named enums.
if value, ok := reflect.enum_from_name_any(type_info.id, str); ok {
set_unbounded_integer_by_type(ptr, value, specific_type_info.base.id)
} else {
return Parse_Error {
.Bad_Value,
fmt.tprintf("Invalid value name. Valid names are: %s", specific_type_info.names),
}
}
case:
if !parse_and_set_pointer_by_base_type(ptr, str, type_info) {
return Parse_Error {
// The caller will add more details.
.Bad_Value,
"",
}
}
}
return
}
get_struct_subtag :: get_subtag
get_field_name :: proc(field: reflect.Struct_Field) -> string {
if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
if name_subtag, name_ok := get_struct_subtag(args_tag, SUBTAG_NAME); name_ok {
return name_subtag
}
}
name, _ := strings.replace_all(field.name, "_", "-", context.temp_allocator)
return name
}
get_field_pos :: proc(field: reflect.Struct_Field) -> (int, bool) {
if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
if pos_subtag, pos_ok := get_struct_subtag(args_tag, SUBTAG_POS); pos_ok {
if value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10); parse_ok {
return int(value), true
}
}
}
return 0, false
}
// Get a struct field by its field name or `name` subtag.
get_field_by_name :: proc(model: ^$T, name: string) -> (result: reflect.Struct_Field, index: int, error: Error) {
for field, i in reflect.struct_fields_zipped(T) {
if get_field_name(field) == name {
return field, i, nil
}
}
error = Parse_Error {
.Missing_Flag,
fmt.tprintf("Unable to find any flag named `%s`.", name),
}
return
}
// Get a struct field by its `pos` subtag.
get_field_by_pos :: proc(model: ^$T, pos: int) -> (result: reflect.Struct_Field, index: int, ok: bool) {
for field, i in reflect.struct_fields_zipped(T) {
args_tag := reflect.struct_tag_lookup(field.tag, TAG_ARGS) or_continue
pos_subtag := get_struct_subtag(args_tag, SUBTAG_POS) or_continue
value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10)
if parse_ok && cast(int)value == pos {
return field, i, true
}
}
return
}
+31
View File
@@ -0,0 +1,31 @@
//+private
//+build !freebsd !netbsd !openbsd
package flags
import "core:net"
// This proc exists purely as a workaround for import restrictions.
// Returns true if caller should return early.
try_net_parse_workaround :: #force_inline proc (
data_type: typeid,
str: string,
ptr: rawptr,
out_error: ^Error,
) -> bool {
if data_type == net.Host_Or_Endpoint {
addr, net_error := net.parse_hostname_or_endpoint(str)
if net_error != nil {
// We pass along `net.Error` here.
out_error^ = Parse_Error {
net_error,
"Invalid Host/Endpoint.",
}
return true
}
(cast(^net.Host_Or_Endpoint)ptr)^ = addr
return true
}
return false
}
+244
View File
@@ -0,0 +1,244 @@
//+private
package flags
@require import "base:runtime"
@require import "core:container/bit_array"
@require import "core:fmt"
@require import "core:mem"
@require import "core:os"
@require import "core:reflect"
@require import "core:strconv"
@require import "core:strings"
// This proc is used to assert that `T` meets the expectations of the library.
@(optimization_mode="size", disabled=ODIN_DISABLE_ASSERT)
validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_location) {
positionals_assigned_so_far: bit_array.Bit_Array
defer bit_array.destroy(&positionals_assigned_so_far)
check_fields: for field in reflect.struct_fields_zipped(T) {
if style == .Unix {
#partial switch specific_type_info in field.type.variant {
case runtime.Type_Info_Map:
fmt.panicf("%T.%s is a map type, and these are not supported in UNIX-style parsing mode.",
model_type, field.name, loc = loc)
}
}
name_is_safe := true
defer {
fmt.assertf(name_is_safe, "%T.%s is using a reserved name.",
model_type, field.name, loc = loc)
}
switch field.name {
case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
name_is_safe = false
}
args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
if !ok {
// If it has no args tag, then we've checked all we need to.
// Most of this proc is validating that the subtags are sane.
continue
}
if name, has_name := get_struct_subtag(args_tag, SUBTAG_NAME); has_name {
fmt.assertf(len(name) > 0, "%T.%s has a zero-length `%s`.",
model_type, field.name, SUBTAG_NAME, loc = loc)
fmt.assertf(strings.index(name, " ") == -1, "%T.%s has a `%s` with spaces in it.",
model_type, field.name, SUBTAG_NAME, loc = loc)
switch name {
case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
name_is_safe = false
continue check_fields
case:
name_is_safe = true
}
}
if pos_str, has_pos := get_struct_subtag(args_tag, SUBTAG_POS); has_pos {
#partial switch specific_type_info in field.type.variant {
case runtime.Type_Info_Map:
fmt.panicf("%T.%s has `%s` defined, and this does not make sense on a map type.",
model_type, field.name, SUBTAG_POS, loc = loc)
}
pos_value, pos_ok := strconv.parse_u64_of_base(pos_str, 10)
fmt.assertf(pos_ok, "%T.%s has `%s` defined as %q but cannot be parsed a base-10 integer >= 0.",
model_type, field.name, SUBTAG_POS, pos_str, loc = loc)
fmt.assertf(!bit_array.get(&positionals_assigned_so_far, pos_value), "%T.%s has `%s` set to #%i, but that position has already been assigned to another flag.",
model_type, field.name, SUBTAG_POS, pos_value, loc = loc)
bit_array.set(&positionals_assigned_so_far, pos_value)
}
required_min, required_max: int
if requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED); is_required {
fmt.assertf(!reflect.is_boolean(field.type), "%T.%s is a required boolean. This is disallowed.",
model_type, field.name, loc = loc)
fmt.assertf(field.name != INTERNAL_VARIADIC_FLAG, "%T.%s is defined as required. This is disallowed.",
model_type, field.name, loc = loc)
if len(requirement) > 0 {
if required_min, required_max, ok = parse_requirements(requirement); ok {
#partial switch specific_type_info in field.type.variant {
case runtime.Type_Info_Dynamic_Array:
fmt.assertf(required_min != required_max, "%T.%s has `%s` defined as %q, but the minimum and maximum are the same. Increase the maximum by 1 for an exact number of arguments: (%i<%i)",
model_type,
field.name,
SUBTAG_REQUIRED,
requirement,
required_min,
1 + required_max,
loc = loc)
fmt.assertf(required_min < required_max, "%T.%s has `%s` defined as %q, but the minimum and maximum are swapped.",
model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
case:
fmt.panicf("%T.%s has `%s` defined as %q, but ranges are only supported on dynamic arrays.",
model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
}
} else {
fmt.panicf("%T.%s has `%s` defined as %q, but it cannot be parsed as a valid range.",
model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
}
}
}
if length, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic {
if value, parse_ok := strconv.parse_u64_of_base(length, 10); parse_ok {
fmt.assertf(value > 0,
"%T.%s has `%s` set to %i. It must be greater than zero.",
model_type, field.name, value, SUBTAG_VARIADIC, loc = loc)
fmt.assertf(value != 1,
"%T.%s has `%s` set to 1. This has no effect.",
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
}
#partial switch specific_type_info in field.type.variant {
case runtime.Type_Info_Dynamic_Array:
fmt.assertf(style != .Odin,
"%T.%s has `%s` defined, but this only makes sense in UNIX-style parsing mode.",
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
case:
fmt.panicf("%T.%s has `%s` defined, but this only makes sense on dynamic arrays.",
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
}
}
allowed_to_define_file_perms: bool = ---
#partial switch specific_type_info in field.type.variant {
case runtime.Type_Info_Map:
allowed_to_define_file_perms = specific_type_info.value.id == os.Handle
case runtime.Type_Info_Dynamic_Array:
allowed_to_define_file_perms = specific_type_info.elem.id == os.Handle
case:
allowed_to_define_file_perms = field.type.id == os.Handle
}
if _, has_file := get_struct_subtag(args_tag, SUBTAG_FILE); has_file {
fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `os.Handle` type.",
model_type, field.name, SUBTAG_FILE, loc = loc)
}
if _, has_perms := get_struct_subtag(args_tag, SUBTAG_PERMS); has_perms {
fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `os.Handle` type.",
model_type, field.name, SUBTAG_PERMS, loc = loc)
}
#partial switch specific_type_info in field.type.variant {
case runtime.Type_Info_Map:
fmt.assertf(reflect.is_string(specific_type_info.key), "%T.%s is defined as a map[%T]. Only string types are currently supported as map keys.",
model_type,
field.name,
specific_type_info.key)
}
}
}
// Validate that all the required arguments are set and that the set arguments
// are up to the program's expectations.
@(optimization_mode="size")
validate_arguments :: proc(model: ^$T, parser: ^Parser) -> Error {
check_fields: for field, index in reflect.struct_fields_zipped(T) {
was_set := bit_array.get(&parser.fields_set, index)
field_name := get_field_name(field)
args_tag := reflect.struct_tag_get(field.tag, TAG_ARGS)
requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED)
required_min, required_max: int
has_requirements: bool
if is_required {
required_min, required_max, has_requirements = parse_requirements(requirement)
}
if has_requirements && required_min == 0 {
// Allow `0<n` or `<n` to bypass the required condition.
is_required = false
}
if _, is_array := field.type.variant.(runtime.Type_Info_Dynamic_Array); is_array && has_requirements {
// If it's an array, make sure it meets the required number of arguments.
ptr := cast(^runtime.Raw_Dynamic_Array)(cast(uintptr)model + field.offset)
if required_min == required_max - 1 && ptr.len != required_min {
return Validation_Error {
fmt.tprintf("The flag `%s` had %i option%s set, but it requires exactly %i.",
field_name,
ptr.len,
"" if ptr.len == 1 else "s",
required_min),
}
} else if required_min > ptr.len || ptr.len >= required_max {
if required_max == max(int) {
return Validation_Error {
fmt.tprintf("The flag `%s` had %i option%s set, but it requires at least %i.",
field_name,
ptr.len,
"" if ptr.len == 1 else "s",
required_min),
}
} else {
return Validation_Error {
fmt.tprintf("The flag `%s` had %i option%s set, but it requires at least %i and at most %i.",
field_name,
ptr.len,
"" if ptr.len == 1 else "s",
required_min,
required_max - 1),
}
}
}
} else if !was_set {
if is_required {
return Validation_Error {
fmt.tprintf("The required flag `%s` was not set.", field_name),
}
}
// Not set, not required; moving on.
continue
}
// All default checks have passed. The program gets a look at it now.
if global_custom_flag_checker != nil {
ptr := cast(rawptr)(cast(uintptr)model + field.offset)
error := global_custom_flag_checker(model,
field.name,
mem.make_any(ptr, field.type.id),
args_tag)
if len(error) > 0 {
// The program reported an error message.
return Validation_Error { error }
}
}
}
return nil
}
+101
View File
@@ -0,0 +1,101 @@
package flags
@require import "core:container/bit_array"
@require import "core:fmt"
Parsing_Style :: enum {
// Odin-style: `-flag`, `-flag:option`, `-map:key=value`
Odin,
// UNIX-style: `-flag` or `--flag`, `--flag=argument`, `--flag argument repeating-argument`
Unix,
}
/*
Parse a slice of command-line arguments into an annotated struct.
*Allocates Using Provided Allocator*
By default, this proc will only allocate memory outside of its lifetime if it
has to append to a dynamic array, set a map value, or set a cstring.
The program is expected to free any allocations on `model` as a result of parsing.
Inputs:
- model: A pointer to an annotated struct with flag definitions.
- args: A slice of strings, usually `os.args[1:]`.
- style: The argument parsing style.
- validate_args: If `true`, will ensure that all required arguments are set if no errors occurred.
- strict: If `true`, will return on first error. Otherwise, parsing continues.
- allocator: (default: context.allocator)
- loc: The caller location for debugging purposes (default: #caller_location)
Returns:
- error: A union of errors; parsing, file open, a help request, or validation.
*/
@(optimization_mode="size")
parse :: proc(
model: ^$T,
args: []string,
style: Parsing_Style = .Odin,
validate_args: bool = true,
strict: bool = true,
allocator := context.allocator,
loc := #caller_location,
) -> (error: Error) {
context.allocator = allocator
validate_structure(model^, style, loc)
parser: Parser
defer {
bit_array.destroy(&parser.filled_pos)
bit_array.destroy(&parser.fields_set)
}
switch style {
case .Odin:
for arg in args {
error = parse_one_odin_arg(model, &parser, arg)
if strict && error != nil {
return
}
}
case .Unix:
// Support for `-flag argument (repeating-argument ...)`
future_args: int
current_flag: string
for i := 0; i < len(args); i += 1 {
#no_bounds_check arg := args[i]
future_args, current_flag, error = parse_one_unix_arg(model, &parser, arg)
if strict && error != nil {
return
}
for starting_future_args := future_args; future_args > 0; future_args -= 1 {
i += 1
if i == len(args) {
if future_args == starting_future_args {
return Parse_Error {
.No_Value,
fmt.tprintf("Expected a value for `%s` but none was given.", current_flag),
}
}
break
}
#no_bounds_check arg = args[i]
error = set_option(model, &parser, current_flag, arg)
if strict && error != nil {
return
}
}
}
}
if error == nil && validate_args {
return validate_arguments(model, &parser)
}
return
}
+43
View File
@@ -0,0 +1,43 @@
package flags
import "base:runtime"
/*
Handle setting custom data types.
Inputs:
- data: A raw pointer to the field where the data will go.
- data_type: Type information on the underlying field.
- unparsed_value: The unparsed string that the flag is being set to.
- args_tag: The `args` tag from the struct's field.
Returns:
- error: An error message, or an empty string if no error occurred.
- handled: A boolean indicating if the setter handles this type.
- alloc_error: If an allocation error occurred, return it here.
*/
Custom_Type_Setter :: #type proc(
data: rawptr,
data_type: typeid,
unparsed_value: string,
args_tag: string,
) -> (
error: string,
handled: bool,
alloc_error: runtime.Allocator_Error,
)
@(private)
global_custom_type_setter: Custom_Type_Setter
/*
Set the global custom type setter.
Note that only one can be active at a time.
Inputs:
- setter: The type setter. Pass `nil` to disable any previously set setter.
*/
register_type_setter :: proc(setter: Custom_Type_Setter) {
global_custom_type_setter = setter
}

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