Compare commits

...

382 Commits

Author SHA1 Message Date
gingerBill b2cf0755f2 Add vendor to nightly.yml 2021-09-01 13:08:26 +01:00
gingerBill d399d2256b Change to [^][N] to just [^] 2021-08-31 20:39:32 +01:00
Jeroen van Rijn 27fd702692 Merge pull request #1111 from Kelimion/libtommath
Slim down LibTomMath compile.
2021-08-29 16:01:42 +02:00
gingerBill 5f29288254 Remove Syscall type 2021-08-29 15:00:35 +01:00
gingerBill 7c108dbf48 Update usage of syscall to use the intrinsics 2021-08-29 14:56:47 +01:00
Jeroen van Rijn 5c7cb393dc Slim down LibTomMath compile. 2021-08-29 15:54:56 +02:00
gingerBill 54b37573c9 Add intrinsics.syscall (for Linux and Darwin only) 2021-08-29 13:17:06 +01:00
gingerBill c3a64c2a59 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-29 11:45:16 +01:00
gingerBill a5c31bbee0 Add map_insert which returns the pointer to inserted value 2021-08-29 11:45:11 +01:00
Jeroen van Rijn d6bd56da2c Merge pull request #1109 from Kelimion/makefile
Fix `Makefile`.
2021-08-28 22:41:06 +02:00
Jeroen van Rijn 17d31bfad6 Fix Makefile. 2021-08-28 22:40:38 +02:00
Jeroen van Rijn 89ffd40d70 Merge pull request #1108 from Kelimion/bigint
big: Add two more asymptotically optimal multiplication methods.
2021-08-28 18:19:55 +02:00
Jeroen van Rijn 737b4fde1c big: Add _private_int_mul_balance. 2021-08-28 18:17:57 +02:00
Jeroen van Rijn 2cfd6b7024 big: Add _private_int_mul_high. 2021-08-28 14:59:13 +02:00
gingerBill 713cd728ba Merge pull request #1107 from odin-lang/mv/libc-errors
Fix libc errors in Windows and add some tests.
2021-08-28 12:51:22 +01:00
vassvik f9bea5b791 Updated comment for Windows version of setjmp. 2021-08-28 13:41:14 +02:00
Jeroen van Rijn 586641d77f Merge pull request #1106 from Kelimion/bigint
big: Add `int_is_square` and Montgomery Reduction.
2021-08-28 13:36:36 +02:00
vassvik 8ca4286624 Add core:c/libc tests 2021-08-28 13:32:32 +02:00
vassvik 165118c641 Fix runtime crash for setjmp in Windows related to an hidden second argument not normally accessible needing to be set to 0. 2021-08-28 13:32:13 +02:00
Jeroen van Rijn 852643e6ba Add tests for `internal_int_is_square'. 2021-08-28 13:27:46 +02:00
vassvik 102d080a31 Fix core:c/libc Windows compilation errors by linking to the right libraries.
Fix some name typos and missing types in Windows.
Add explicit cast on MB_CUR_MAX
2021-08-28 13:27:41 +02:00
Jeroen van Rijn ec4cae4f04 big: Add int_is_square. 2021-08-27 16:41:16 +02:00
Jeroen van Rijn 4153898c55 big: Add Montgomery Reduction. 2021-08-27 16:41:16 +02:00
Jeroen van Rijn 33df335ec9 big: Add internal_int_montgomery_calc_normalization. 2021-08-27 16:41:16 +02:00
Jeroen van Rijn 893cc013b5 big: Add Montgomery reduction. 2021-08-27 16:41:16 +02:00
gingerBill b88e945268 ERROR_BLOCK() any usages of "Did you mean?" like behaviour whilst iterating across a scope entry map 2021-08-27 12:14:51 +01:00
gingerBill bf56e3ea8d Improve strings.index_any and strings.last_index_any 2021-08-27 12:07:57 +01:00
gingerBill 284acc37f9 Update SDL scancodes to have the same C enums equivalent global values 2021-08-27 11:38:29 +01:00
gingerBill 582559f7ac Correct did you mean logic and make thread-safe-er 2021-08-27 11:18:38 +01:00
gingerBill 53556d9bd2 Disable local mutex for the time being. 2021-08-26 23:16:57 +01:00
gingerBill da79124e5d Use local mutex for each AstFile.arena 2021-08-26 23:10:15 +01:00
gingerBill 2f34f1283a Make thread_join be more correct 2021-08-26 22:42:56 +01:00
gingerBill f973d271cf Add mutex around condition_broadcast 2021-08-26 22:28:09 +01:00
gingerBill 4625b25287 Wrap linux specific code for internal_thread_proc 2021-08-26 22:26:51 +01:00
gingerBill 8d8b3fd071 Add missing header 2021-08-26 22:24:26 +01:00
gingerBill a852c17614 Don't permit any signal delivery to threads on Linux 2021-08-26 22:20:10 +01:00
gingerBill b33bf3f704 Correct race condition and incorrect usage of condition_signal outside of a mutex lock 2021-08-26 22:17:51 +01:00
gingerBill 726788a483 Treat Type_Tuple closer to a Type_Struct in lb_type 2021-08-26 22:01:02 +01:00
gingerBill cdd3560702 Merge pull request #1103 from odin-lang/new-thread-pool
Improved Thread Pool implementation for the Compiler
2021-08-26 21:44:02 +01:00
gingerBill 6d49df1d87 Don't use the thread pool if worker count is 0 2021-08-26 21:40:54 +01:00
gingerBill ac191bd31f Simplify logic for -thread-count:1 2021-08-26 21:30:23 +01:00
gingerBill ad3a3547d6 Unify thread pool logic across the rest of the compiler, using a global thread pool 2021-08-26 21:22:30 +01:00
gingerBill aba14c43ac Fix typo 2021-08-26 17:57:29 +01:00
gingerBill 25c3fd48f0 Improved ThreadPool implementation 2021-08-26 17:56:28 +01:00
gingerBill e45aa68c14 Remove unneeded +1 for outstanding_task_count 2021-08-26 16:10:32 +01:00
gingerBill 6dfab34aca Merge pull request #1101 from odin-lang/compiler-allocator-improvements
Compiler Allocator Improvements
2021-08-26 16:06:37 +01:00
gingerBill d3d805ffb3 Fix typo 2021-08-26 15:58:34 +01:00
gingerBill 05b9724c85 Correct platform_virtual_memory_init on Unix 2021-08-26 15:55:09 +01:00
gingerBill 5053f0179c Implement virtual memory code for *nix and make generic 2021-08-26 15:53:08 +01:00
gingerBill 3e4d615983 Minor fixes 2021-08-26 15:41:32 +01:00
gingerBill aa8777ee47 Change the implementation of Arena to use virtual memory, and remove the old gbArena code 2021-08-26 15:38:34 +01:00
gingerBill 98dd59e412 Fix return value 2021-08-25 22:28:15 +01:00
gingerBill 7b2f6aaa1c Add [^] to GameControllerGetSensorData 2021-08-25 16:10:39 +01:00
gingerBill ad943f0189 Use [^] on PeepEvents 2021-08-25 15:50:35 +01:00
gingerBill 224496dca7 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-25 15:21:14 +01:00
gingerBill 1ef59417ef Add gl_set_proc_address to both sdl2 and glfw as utility loaders for OpenGL 2021-08-25 15:21:06 +01:00
Jeroen van Rijn 0d5a160409 Add PortMidi to vendor:README.txt. 2021-08-25 13:54:39 +02:00
gingerBill 82facb387c Add vendor:portmidi 2021-08-25 12:46:18 +01:00
gingerBill da7a0df7a1 Simplify logic for parse_binary_expr 2021-08-25 11:43:02 +01:00
gingerBill fcbd94b924 Update enums 2021-08-24 20:05:01 +01:00
gingerBill 5bb3912001 Update licences for GLFW 2021-08-24 20:04:35 +01:00
gingerBill f4248b159d Add vendor:glfw 2021-08-24 20:00:24 +01:00
gingerBill 2e4edcc7e9 Merge pull request #1099 from Kelimion/vendor
vendor: Add `README.md` and clarify licensing.
2021-08-24 18:34:47 +01:00
gingerBill 0d3272d914 Remove ICD from generation 2021-08-24 18:33:35 +01:00
Jeroen van Rijn 50f3e77b43 vendor: Add README.md and clarify licensing. 2021-08-24 19:33:10 +02:00
gingerBill 964d91b855 Add vendor:vulkan 2021-08-24 17:08:03 +01:00
gingerBill bdac3ee120 Add gamecontrollerdb.txt to vendor:sdl
https://github.com/gabomdq/SDL_GameControllerDB
2021-08-24 14:04:27 +01:00
gingerBill b81c670597 Remove debug message 2021-08-24 11:08:41 +01:00
gingerBill 4489df2871 Make sync_t distinct 2021-08-24 00:22:11 +01:00
gingerBill 766c17a6a3 Change [^][1]T usage to [^]T 2021-08-24 00:20:51 +01:00
gingerBill 018f8a82d6 Correct indentation 2021-08-24 00:19:47 +01:00
gingerBill d5a0c004b6 More [^][N]T usage for UniformNuiv 2021-08-24 00:16:39 +01:00
gingerBill 0b6e45c9a2 More [^][N]T usage 2021-08-24 00:15:34 +01:00
gingerBill 5c41f64829 Improve signatures for [^][N*M]T like matrix parameters 2021-08-24 00:14:09 +01:00
gingerBill ad56cf0038 Change some usages of [^]T to ^[N]T where appropriate 2021-08-24 00:06:49 +01:00
gingerBill 58b5e92c2f Update more uses of [^]T types 2021-08-23 23:57:16 +01:00
gingerBill 0a0752db7c Correct int to i32 usage 2021-08-23 23:46:36 +01:00
gingerBill 49fbdd6188 Add more indirect command structs to the procedure calls 2021-08-23 23:45:00 +01:00
gingerBill 1048553e78 More corrections; Add DrawArraysIndirectCommand and DrawElementsIndirectCommand data structures 2021-08-23 23:02:19 +01:00
gingerBill 63282290db Remove dead procedure 2021-08-23 21:42:11 +01:00
gingerBill 7a1498e7dc Remove space prefix 2021-08-23 21:40:17 +01:00
gingerBill 0d3cbb8883 Add vendor:OpenGL
Based off a heavily modified version of: https://github.com/vassvik/odin-gl
2021-08-23 21:33:39 +01:00
gingerBill ce7698c20e Correct core library usage of the new mem.new behaviour 2021-08-23 19:29:01 +01:00
gingerBill 382ca20916 Correct procedure checking flag handling, and correct the (bodge) handle of unchecked procedure bodies 2021-08-23 19:24:53 +01:00
gingerBill fe2ad54f60 Fix bug in check_is_terminating 2021-08-23 17:42:54 +01:00
gingerBill b014879159 Add extra message to assert 2021-08-23 16:45:52 +01:00
gingerBill 81623861c0 Correct mem.clone_slice 2021-08-23 14:33:54 +01:00
gingerBill bd86993035 Remove the old inline and no_inline tokens 2021-08-23 12:37:42 +01:00
gingerBill 4ccf135892 Unify new/make the internal logic between runtime and mem 2021-08-23 12:35:29 +01:00
gingerBill cba0bd30f5 Add suggestions when trying to take the address the a value from a for/switch statement 2021-08-23 11:50:02 +01:00
gingerBill 276d4b8f0d Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-23 11:31:09 +01:00
gingerBill 7bdbaca938 Make SDL_image.Init return InitFlags 2021-08-23 11:31:00 +01:00
Jeroen van Rijn 7f34080b69 Merge pull request #1097 from nakst/master
Thread pool: create threads in thread_pool_wait
2021-08-23 11:17:54 +02:00
nakst 9397555c91 Thread pool: create threads in thread_pool_wait 2021-08-23 10:11:24 +01:00
gingerBill 7a00ef1879 Merge pull request #1096 from nakst/master
thread_pool.cpp: fix with 1 thread; gb.h: remove buggy /proc/cpuinfo code
2021-08-23 09:32:41 +01:00
nakst 35204e3cc5 thread_pool.cpp: fix with 1 thread; gb.h: remove buggy /proc/cpuinfo code 2021-08-23 09:18:18 +01:00
gingerBill daced956e3 Make ThreadPool use std::atomic and heap_allocator() 2021-08-22 23:01:01 +01:00
gingerBill 5a2d582a09 Merge pull request #1095 from nakst/master
Rewrite thread_pool.cpp
2021-08-22 22:55:31 +01:00
nakst b878be6f79 Thread pool fix on Win32 2021-08-22 21:24:56 +01:00
nakst 1f25f60a68 Rewrite thread_pool.cpp 2021-08-22 21:13:41 +01:00
gingerBill 2e921c88fb Add debug information for [^]T 2021-08-22 19:02:52 +01:00
gingerBill abaf8c127d Correct build.bat 2021-08-22 17:12:19 +01:00
gingerBill 0c5fa2cdd5 Fix build.bat 2021-08-22 17:09:40 +01:00
gingerBill 36cb1f868b Embed the SDL2 libraries into vendor:sdl2 2021-08-22 17:05:06 +01:00
gingerBill 037cc679c4 Keep -vet happy 2021-08-22 16:58:12 +01:00
gingerBill db6fad7396 Fix indexing code gen for multi-pointers 2021-08-22 16:55:57 +01:00
gingerBill 07bfb55658 Fix code gen for compare against nil for multi pointers 2021-08-22 16:53:26 +01:00
gingerBill 56078ee099 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-22 16:49:48 +01:00
gingerBill ae4a378294 Merge pull request #1094 from graphitemaster/master
Project all of libc
2021-08-22 16:06:15 +01:00
Dale Weiler 791d7f764b cleanup 2021-08-22 10:05:27 -04:00
Dale Weiler b39a4f3e3b Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-22 09:49:20 -04:00
Dale Weiler 389b50f735 libc projection 2021-08-22 09:48:34 -04:00
gingerBill 2f6e566a32 Remove the deprecated fmt.print*_err procedures 2021-08-22 12:57:08 +01:00
gingerBill d5bad374d9 Remove deprecated procedure slice_ptr_to_bytes 2021-08-22 12:55:57 +01:00
gingerBill 445ed9be2b Use multi-pointers when appropriate 2021-08-22 12:54:04 +01:00
gingerBill 8694a0f68a Update signature_parameter_similar_enough for multi pointers 2021-08-22 12:31:03 +01:00
gingerBill d3fee9d761 Merge pull request #1093 from odin-lang/multi-pointers
Multi Pointers `[^]T`
2021-08-22 11:50:47 +01:00
gingerBill 36a6805b7c Update doc format for multi-pointers 2021-08-22 11:46:26 +01:00
gingerBill 19bf12aa09 Update odin/ast for multi pointers 2021-08-22 11:46:12 +01:00
gingerBill 0decdaed1a Merge branch 'master' into multi-pointers 2021-08-22 11:28:44 +01:00
gingerBill 93b5befe45 Improve error handling for missing semicolon separators in a for loop 2021-08-22 11:27:24 +01:00
gingerBill d72f4a8a79 Correct Multi Pointer Type handling in expressions 2021-08-22 00:59:42 +01:00
gingerBill 91247a8fe1 Fix multi pointers for parapoly 2021-08-21 23:16:30 +01:00
gingerBill 18a0fa02c5 Add multi pointers to core 2021-08-21 23:16:14 +01:00
gingerBill 932f330a51 Add comparisons to LLVM backend for multi pointers 2021-08-21 23:12:15 +01:00
gingerBill 6a77fc4cdd Add multi-pointer types [^]T 2021-08-21 23:10:21 +01:00
gingerBill 01a888fced Update import path 2021-08-21 14:09:48 +01:00
gingerBill f0437a4242 Enforce core:builtin and core:intrinsics for imports 2021-08-21 13:44:16 +01:00
gingerBill 32bdad322a Improve parsing for or_return in core:odin/parser 2021-08-21 13:43:38 +01:00
gingerBill bf130087e7 Change to strings.clone_from_cstring_bounded 2021-08-21 13:43:03 +01:00
gingerBill 38e038a1ab Add strings.clone_from and strings.clone_from_nul_terminated 2021-08-21 13:42:06 +01:00
gingerBill 6504607adf Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-21 13:32:12 +01:00
gingerBill c8378fce95 Add library collection vendor 2021-08-21 13:32:07 +01:00
Jeroen van Rijn 1aeaec8d5c Merge pull request #1091 from Kelimion/win11
os: Add Windows 11 detection.
2021-08-21 14:12:26 +02:00
gingerBill 2aaf927beb Improve error message for Cannot assign value if they have the same name but are from different packages 2021-08-21 13:09:23 +01:00
Jeroen van Rijn bb86b0f526 os: Add Windows 11 detection. 2021-08-21 14:08:22 +02:00
gingerBill 2f5edebefa Rename mem.reinterpret to mem.reinterpret_copy 2021-08-20 10:19:30 +01:00
gingerBill b5cdb331b0 Add mem.reinterpret 2021-08-20 10:18:34 +01:00
gingerBill fa4f3aa7ad Correct atomic usage 2021-08-19 17:51:19 +01:00
gingerBill a90fe7211c Make global_entity_id atomic 2021-08-19 17:44:26 +01:00
gingerBill ac6cc5191a Make ThreadPool.is_running atomic 2021-08-19 17:43:15 +01:00
gingerBill 38841dd46e Fix race condition from add_entity_use due to Entity.identifier 2021-08-19 17:38:18 +01:00
gingerBill e722af7f61 Remove unneeded disabled warnings from build.bat 2021-08-19 15:43:51 +01:00
gingerBill df372dbd5b Migrate and remove more from gb.h 2021-08-19 15:38:21 +01:00
gingerBill 5c4d95d539 Move more of gb.h's Synchronization code into common.cpp 2021-08-19 15:19:36 +01:00
gingerBill 9ae4de2ab8 Remove unused code from gb.h (which means it is heavily modified now) 2021-08-19 15:09:39 +01:00
gingerBill 7845769d4b Remove unused code 2021-08-19 15:03:10 +01:00
gingerBill 33239324b8 Improve the C++ to be more correct for clang on Windows, still requiring the same disabled warnings as on *nix 2021-08-19 14:44:53 +01:00
gingerBill 82a74ebfa9 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-19 11:40:32 +01:00
gingerBill 35026000bb Fix deadlock caused by typo 2021-08-19 11:40:26 +01:00
Jeroen van Rijn 55cf3d26bf Merge pull request #1090 from Kelimion/bigint
big: Update license to BSD-3.
2021-08-19 12:13:39 +02:00
Jeroen van Rijn 23d29be4d8 big: Update license to BSD-3. 2021-08-19 12:12:59 +02:00
Jeroen van Rijn 1ad0743a52 big: Nicely align test suite results. 2021-08-19 12:12:59 +02:00
gingerBill 54af47a138 Remove useless code 2021-08-18 23:38:06 +01:00
gingerBill b84ee3ab8f Fix odin test not executing any tests 2021-08-18 23:33:27 +01:00
gingerBill e023b96737 Remove json import from demo 2021-08-18 22:33:18 +01:00
gingerBill 3fde4616e0 Correct mutex usage for path_to_fullpath; make ThreadPool use BlockingMutex 2021-08-18 21:21:30 +01:00
Mikkel Hjortshøj 4812006eb8 Update ci.yml 2021-08-18 22:09:44 +02:00
gingerBill 740995df3d Remove attribute parameter for pthread_mutex_init 2021-08-18 21:07:06 +01:00
gingerBill aa5c3da414 Make BlockingMutex non-recursive on *nix systems 2021-08-18 20:57:03 +01:00
gingerBill d419d81841 Remove unused code 2021-08-18 20:48:26 +01:00
gingerBill 08942714a2 Make Arena allocate virtual memory directory; make it use a BlockingMutex 2021-08-18 20:46:10 +01:00
gingerBill 3c443babb2 Extra sanity check around static 2021-08-18 20:32:54 +01:00
gingerBill 326e5cd046 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-18 20:31:39 +01:00
gingerBill 79e98b71d3 Remove dead code, and add an extra mutex 2021-08-18 20:31:34 +01:00
Jeroen van Rijn 96605f700b Merge pull request #1089 from Kelimion/bigint
big: Enable Toom again.
2021-08-18 15:36:45 +02:00
Jeroen van Rijn 38b5e01343 Merge branch 'master' into bigint 2021-08-18 15:35:49 +02:00
Jeroen van Rijn 06cde91ba3 big: Enable Toom again. 2021-08-18 15:35:01 +02:00
Jeroen van Rijn 3891d6a483 Merge pull request #1088 from Kelimion/bigint
big: Fix signed multiplication for some inputs.
2021-08-18 15:32:49 +02:00
Jeroen van Rijn 5fd7a5f32a big: Fix signed multiplication for some inputs. 2021-08-18 15:24:43 +02:00
gingerBill a01c946c20 Add mutex to Scope lookups and insertions 2021-08-18 11:17:14 +01:00
gingerBill 84b0da44db Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-17 19:54:17 +01:00
gingerBill cdb3a5205c Add mutex for add_type_and_value 2021-08-17 19:54:09 +01:00
Jeroen van Rijn eae98feb7a Merge pull request #1086 from Kelimion/bigint
big: Temporarily disable Toom.
2021-08-16 22:19:10 +02:00
Jeroen van Rijn 3af078e941 Merge branch 'master' into bigint 2021-08-16 22:16:03 +02:00
Jeroen van Rijn 48c1f0ab59 big: Disable Toom for a moment. 2021-08-16 22:13:54 +02:00
Jeroen van Rijn 19386814b3 Merge pull request #1085 from Kelimion/bigint
Add `_private_int_mul_toom` + `_private_int_mul_karatsuba`.
2021-08-16 21:29:40 +02:00
Jeroen van Rijn 706e58c1c7 big: Add _private_int_mul_toom`. 2021-08-16 21:17:21 +02:00
gingerBill 9ab94650c8 Allow + in import paths 2021-08-16 18:21:58 +01:00
gingerBill fce86ff3d5 Correct struct tag bug 2021-08-16 18:17:26 +01:00
gingerBill 0051cd12e2 Make flags atomic for Entity and Type 2021-08-16 16:30:49 +01:00
gingerBill df159dbae7 Add some missing files to sync2 for linux and darwin 2021-08-16 15:48:54 +01:00
gingerBill 94d298755a Fix race condition when adding a dependency 2021-08-16 15:33:26 +01:00
Jeroen van Rijn 8b49bbb0fc big: Add _private_mul_karatsuba. 2021-08-16 16:10:10 +02:00
gingerBill 5f072591ba Merge pull request #1082 from odin-lang/or_else-or_return-operator
`or_else` and `or_return` operators
2021-08-16 12:03:43 +01:00
gingerBill e3fef2dade Improve parsing for or_return; allow #force_inline foo() or_return; 2021-08-16 11:58:50 +01:00
gingerBill 4c306a6f99 Correct or_return logic for debug printing and expression is not used checking 2021-08-16 11:08:37 +01:00
gingerBill 0996cc82a7 Keep -vet happy 2021-08-15 23:17:12 +01:00
gingerBill f293d7c997 Update package encoding/json for or_return 2021-08-15 23:11:15 +01:00
gingerBill b2097604d5 Add clone_ast for or_else and or_return 2021-08-15 23:10:52 +01:00
gingerBill 4e1c9b71f4 Update core:odin/* for or_else and or_return 2021-08-15 19:05:55 +01:00
gingerBill 3e2788afdc Add extra example to or_return_operator 2021-08-15 18:51:20 +01:00
gingerBill 1a7f508dd9 Improve comment 2021-08-15 18:44:50 +01:00
gingerBill dc8cfcf92a Fix typos and improve clarity of or_return_operator 2021-08-15 18:36:34 +01:00
gingerBill ac08d37ca0 Add or_return_operator to examples/demo 2021-08-15 18:29:49 +01:00
gingerBill 4035fec784 Add more uses of or_return 2021-08-15 18:13:56 +01:00
gingerBill b071a07c86 Replace uses of err != nil with or_return where appropriate 2021-08-15 17:56:24 +01:00
gingerBill c27b8a71fd Replace err != nil with or_return where appropriate 2021-08-15 17:52:10 +01:00
gingerBill b8661e0ae0 Update semi-colon insertion rules for or_return 2021-08-15 17:16:37 +01:00
gingerBill 21cbac755e Make or_else and or_return operators (binary and suffix respectively) 2021-08-15 17:14:35 +01:00
gingerBill a3a20f09e2 or_return built-in procedure 2021-08-15 16:55:30 +01:00
gingerBill d62f189d72 Remove some dead code 2021-08-15 15:09:47 +01:00
gingerBill 84713b58e0 Improve error handling for field list prefixes 2021-08-15 13:29:44 +01:00
gingerBill 294c8426e6 Minor clean up of parse_field_prefixes 2021-08-15 13:18:06 +01:00
gingerBill 7bbc9a4634 Add #any_int directive to replace auto_cast uses on parameters. 2021-08-15 12:56:59 +01:00
gingerBill 1cd3b693ae Update LICENSE 2021-08-15 12:12:48 +01:00
gingerBill d1b9b06614 Add core:math/big to examples/all 2021-08-15 11:14:51 +01:00
gingerBill 19aefa6a40 Add assignment statement to #no_bounds_check etc rules. Correct os_linux.odin usage. 2021-08-15 11:14:34 +01:00
gingerBill effecf8595 Fix typo 2021-08-15 11:13:59 +01:00
gingerBill d5e3f72a0b Define where #bounds_check/#no_bounds_check can be applied 2021-08-15 11:09:41 +01:00
gingerBill 9fb486b2ad Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-15 11:08:35 +01:00
gingerBill d70fa4329c Move #no_bounds_check to before return 2021-08-15 11:08:28 +01:00
Jeroen van Rijn 3f29a0d6dd Merge pull request #1078 from Kelimion/bigint
Add recursive division.
2021-08-14 15:58:27 +02:00
Jeroen van Rijn 0db86a0638 big: Add workaround for DLL globals bug. 2021-08-14 13:51:17 +02:00
Jeroen van Rijn dc02566a84 big: Add _private_int_div_recursive. 2021-08-13 23:45:00 +02:00
gingerBill 0e84e06756 Fix lower and upper values for a bit_set[Enum] type. 2021-08-13 14:17:27 +01:00
gingerBill e6b2df4b2b Add extra error message check to lb_big_int_to_llvm 2021-08-13 14:16:53 +01:00
Jeroen van Rijn 37be8d4091 big: Add internal_invmod. 2021-08-13 14:55:53 +02:00
gingerBill a3930cb470 Improve fmt._user_formatters logic 2021-08-13 12:44:55 +01:00
gingerBill ad402726f1 Fix #1026 2021-08-13 12:21:14 +01:00
gingerBill 8ff9f2e44f Fix #1077 2021-08-13 11:49:52 +01:00
gingerBill 799a56bbcb Fix column in tokenizer (due to removed line) 2021-08-13 10:50:05 +01:00
gingerBill fbbd43a6d8 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-13 10:45:35 +01:00
gingerBill 367bf0c7ae Fix #1076 2021-08-13 10:45:29 +01:00
Jeroen van Rijn f72a0de074 big: Add inverse mod. 2021-08-13 01:41:33 +02:00
Jeroen van Rijn 07baae04c9 Merge pull request #1075 from Kelimion/bigint
Add `core:math/big`, an arbitrary precision library.
2021-08-11 21:43:54 +02:00
Jeroen van Rijn eb22a49b02 big: Add int_from_bytes_*. 2021-08-11 20:59:54 +02:00
Jeroen van Rijn ee24f2dd37 big: Improve int_to_bytes_*. 2021-08-11 20:59:54 +02:00
Jeroen van Rijn 12f9b6db63 big: Add int_to_bytes_{big, little} + Python compatible variants. 2021-08-11 20:59:54 +02:00
Jeroen van Rijn 851780b8f4 big: Add arguments and usage to test.py. 2021-08-11 20:59:54 +02:00
Jeroen van Rijn 5f34ff9f9f big: Add _private_int_sqr_toom. 2021-08-11 20:59:54 +02:00
Jeroen van Rijn 2b274fefbb big: Add _private_int_sqr_karatsuba. 2021-08-11 20:59:54 +02:00
Jeroen van Rijn 6c681b258c big: Add _private_int_sqr_comba. 2021-08-11 20:59:54 +02:00
Jeroen van Rijn 1f91a2fe65 big: Finish refactor. 2021-08-11 20:59:54 +02:00
Jeroen van Rijn 19ff27788c big: Refactoring. 2021-08-11 20:59:54 +02:00
Jeroen van Rijn 1ebb0bd9d6 big: More refactoring. 2021-08-11 20:59:54 +02:00
Jeroen van Rijn d505a05d36 big: More refactoring. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 07dca737f0 big: More refactoring. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 6d34a8344a big: Refactor helpers. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 40b7b9ecdf big: Refactor exponents and such. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 53bf66ce1e big: Prettify internal_cmp_digit. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn fd95f50c56 big: Split up int_is_* comparison tests. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 777e17d80f big: Improve tunables. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn d4a03acbc3 big: Split up int_mod_bits (res = val % (1 << bits)) 2021-08-11 20:59:53 +02:00
Jeroen van Rijn c3db24f834 big: Split up gcd + lcm. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 62dcccd7ef big: Move division internals. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn e288a563e1 big: Move _mul private functions. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 6298226238 big: Switch choose over to internal implementations. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 9321616c80 big: Split more into public and internal. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 9890e7cfeb big: Improved zero_unused helper. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 4be48973ad big: Squashed shl1 bug when a larger dest was reused for a smaller result. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn f8442e0524 big: Split up mul into internal and public parts. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 9858989b1c big: Split up add and sub into public and internal parts. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 511057ca36 big: Improve timing code. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 35d8976de4 bit: Optimized int_bitfield_extract. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 463003e86a bit: Improved bitfield extraction. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 85a2a8815e big: Some more work on constants. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 47397a6a48 Add faster divison. 2021-08-11 20:59:53 +02:00
Jeroen van Rijn 2323ca1622 big: Add MATH_BIG_FORCE_64/32_BIT flags. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn fc0a92f8ac big: Add constants. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 97d80d03f9 big: Error.None -> nil 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 627872db97 big: Timed factorial. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn a27612ec6a Add _mul_comba path. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 491e4ecc74 big: Add binary split factorial. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn cd0ce7b76e big: Add choose. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 320387c4ee big: Add gcd_lcm fast path in wrapper. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 6424a5a8dd big: Refactored gcm and lcm to use a common function. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 06f5a6c785 big: Special case gcd(0,0) + lcm(0,0). 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 8b1d8c8453 big: Add lcm and its test. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 0028cb0258 big: Test gcd. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn b15ee059ad big: Add gcd. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 50feeaa285 big: Add test for factorial. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn e80ac18324 big: Add factorial, have tests use hex strings. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn db0196abc7 big: Test root_n. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 149c7b88df big: Fix sqrt, div, add with certain inputs. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 7afd1b15a8 big: test_pow for larger ints. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn f12672727d big: Add test_pow and some more switches. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 2179cc2bc7 big: Improved test driver. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 961adfedd9 big: Test negative inputs as well. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 385b9c9922 big: Add tests for log. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 922df6a438 big: Add more exhaustive tests. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn c1a001c331 big: Add randomized testing. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 13fab36639 big: Fix mul. 2021-08-11 20:59:52 +02:00
Jeroen van Rijn 708389a7ee big: Improve test driver. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn fb6c9af1ae big: Improve tests. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 85aa4dd670 big: Start test suite. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 74258a170a big: fix itoa base PoT other than 16. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 2fbff25a18 big: Improve int_bitfield_extract. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 9c150381bf big: Add rand. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 531c4936dd big: Add root_n. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 2aae1016ab big: Add sqrt. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 9c2468ecf7 big: Add atoi. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 0a431eef19 big: Add another way to estimate radix size. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 9646d1f2b8 big: Add submod, mulmod, sqrmod. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 5f7aeb3045 big: Add mod and addmod. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 1ebaa9fb3b big: itoa now works for arbitrary radixes. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn c2255c6c19 big: Add div. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 2884fa5506 big: add div by 3. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 31c94bd7f8 big: Finish log, fix sqr. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 5f63e3952e big: Correct pow bugs from the original. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn d953e40fb3 big: Add pow. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn c3a4d7dda2 big: Fast square method. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn b4a29844e9 big: Add multiplication. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 0254057f1b big: Add swap. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn f34ba44bf8 big: Add shl, shr and shrmod. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn d4d863c4db big: Add mod_power_of_two. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 78c0877994 big: Add get(a, type) and get_float. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 1d0b37c1d8 big: add shl1, shr1. 2021-08-11 20:59:51 +02:00
Jeroen van Rijn 7648f2e655 big: Finish big ZII refactor. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn d9efa6c8b5 big: More ZII refactoring. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn 2e372b33a3 big: More ZII refactoring. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn 687c211a58 big: ZII. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn 4eadd0867d big: Continuing to refactor. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn 9dba17cf87 bigint: refactor to big.Int instead of bigint.Int. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn baef0c291d bigint: Added some more helpers. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn cccd290834 bigint: Add is_power_of_two helper. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn 5af85aed3d bigint: itoa support for arbitrary precision if is_power_of_two(radix) 2021-08-11 20:59:50 +02:00
Jeroen van Rijn e600e5947b bigint: remove unnecessary boundary checks. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn d7ae611f76 bigint: itoa now writes backwards directly, no need to reverse after. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn 04a83eb9f7 bigint: pass size to itoa_raw. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn 341e8a3c99 bigint: itoa works for numbers <= 120 bits. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn e3d8ac559d bigint: Fast paths for radix code. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn 767948ab46 bigint: log_n for bases that fit within one DIGIT or are a power of two. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn dbcd8da733 bigint: Working on itoa and logn. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn 905d5459a9 bigint: Add count_bits and more prep. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn dfd5a993a2 bigint: Prepare for multiplication. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn daceaa65f5 bigint: Add substractin with immediate. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn c2c07f07db Add single DIGIT addition. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn c5cbd3260a bigint: Add prototypes for immediate add+sub. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn d57e1be89f bigint: Improve add. 2021-08-11 20:59:50 +02:00
Jeroen van Rijn 18dda6ff9d Start of core:math/bigint
We have:
- `init` to create a new `Int`
- `init(from_integer)` to create a new `Int` and set it to `from_integer`.
- `set(Int, from_integer)` to set an `Int` to `from_integer`
- `add(dest, a, b)` to add `a` and `b` into `dest`.
- `sub(dest, a, b)` to subtract `b` from `a` and put the result in `dest`.

And a few helper functions, like:
- `is_zero`, `is_negative`, ...
- `grow`, `shrink`, `clear`, `zero`
2021-08-11 20:59:50 +02:00
gingerBill 7afc367275 Update examples/all/all_main.odin 2021-08-11 19:11:00 +01:00
gingerBill c465171b45 Fix #1061 2021-08-09 21:41:19 +01:00
gingerBill 02f22a0b3f Correct DllMain behaviour 2021-08-09 21:23:24 +01:00
gingerBill 193fd0eecb Correct and improve type inference for swizzling expressions 2021-08-09 20:13:58 +01:00
gingerBill 01f431b01f Unify semantics of the built-in swizzle procedure with the selector expression semantics e.g. .xyz 2021-08-09 19:37:58 +01:00
gingerBill aebfa4b28e Allow len and cap to return a uint if a type hint is uint to aid people wanting to use unsigned integers 2021-08-09 17:39:38 +01:00
gingerBill a3abe991a4 Add package core:encoding/hxa 2021-08-09 16:26:51 +01:00
gingerBill e793f92e67 Improve parsing handling for the { return } cases 2021-08-09 13:01:47 +01:00
gingerBill 042f376626 Minor code changes to Map/StringMap 2021-08-09 13:01:26 +01:00
gingerBill d99ed692ba Add utility procedures: io.read_ptr; io.write_ptr; io.read_ptr_at; io.write_ptr_at 2021-08-09 12:33:21 +01:00
gingerBill 4d00c2b800 Allocator_Error.Mode_Not_Implemented; Minor improvement to map runtime procedures 2021-08-08 14:29:45 +01:00
gingerBill a5605e94b1 Simplify Map and StringMap in the compiler to reuse the hashes' array data if possible. 2021-08-08 13:56:40 +01:00
gingerBill 9cfe20cfb4 Correct error message for add_import_dependency_node 2021-08-08 13:13:31 +01:00
gingerBill db3501f61b [Breaking] Change the layout json.Value to be a union rather than a struct of a json.Pos and the union 2021-08-08 12:59:35 +01:00
gingerBill 48538aa792 Remove package core:encoding/cel 2021-08-08 12:50:38 +01:00
gingerBill 5fd64f48ee Minor procedure rename 2021-08-08 12:48:44 +01:00
gingerBill a3b7126875 Simplify init_tokenizer_with_data 2021-08-08 12:47:45 +01:00
gingerBill 5756c8a7c6 Merge branch 'master' of https://github.com/odin-lang/Odin 2021-08-08 12:45:33 +01:00
gingerBill cdd0061869 Prefer ..= over .. 2021-08-08 12:45:27 +01:00
Mikkel Hjortshøj e6adfd8054 Update create_nightly_json.py 2021-08-08 11:29:56 +02:00
Jeroen van Rijn 6d59223efd Merge pull request #1072 from Kelimion/cel_fix_ise
CEL: Fix by changing `using enum` to ISE.
2021-08-08 02:21:04 +02:00
Jeroen van Rijn ebd034fff9 CEL: Fix ISE. 2021-08-08 02:19:32 +02:00
gingerBill 000bda8419 Reduce superfluous error messages for return statements expecting not-1 return values 2021-08-07 16:29:00 +01:00
gingerBill 423b842347 Fix typo 2021-08-07 15:07:29 +01:00
gingerBill 16eeae36d7 Inline heap_allocator resize logic on *nix platforms 2021-08-07 15:05:46 +01:00
gingerBill 5453e92bcb Minor test on array_set_capacity 2021-08-07 14:44:48 +01:00
gingerBill c16c9535d9 Reorder ci.yml from run check version to version check run 2021-08-07 14:40:54 +01:00
gingerBill 662c7b1e71 Minor clean-up 2021-08-07 14:39:26 +01:00
gingerBill 571170fd30 Improve and simplify the memory layout of MPMCQueue 2021-08-07 14:25:48 +01:00
gingerBill 911c428dac Remove dead code in queue.cpp; clean up initialization code 2021-08-07 13:54:08 +01:00
gingerBill 40822be595 Reorganize llvm_backend.cpp into separate files for easier maintenance 2021-08-07 12:01:48 +01:00
gingerBill f5e51a29b5 Fix #1070 2021-08-07 11:23:40 +01:00
gingerBill beaad719ad Reallow using on enum declarations temporarily but with a warning. 2021-08-05 17:51:45 +01:00
gingerBill 0d257c61cd Disallow using on an enum declaration. 2021-08-05 17:46:42 +01:00
gingerBill dd8fa1d690 Prefer ..= over .. 2021-08-04 00:36:10 +01:00
gingerBill af6df7d7c9 Improve error cases in core:odin/parser 2021-08-04 00:23:31 +01:00
gingerBill 57a17a708b Improve core:odin/parser 2021-08-04 00:10:41 +01:00
gingerBill 1f79082921 Remove dead comments 2021-08-04 00:10:05 +01:00
gingerBill afff9478c8 Make core:odin/tokenizer be consistent with the compiler's version 2021-08-03 23:27:26 +01:00
gingerBill b352b42afc Remove old comment 2021-08-03 16:31:50 +01:00
gingerBill c3e4509d17 Improve error message when using a comma rather than a semicolon in a bit_set 2021-08-03 15:46:14 +01:00
gingerBill 664be28941 Fix typo 2021-08-03 15:45:32 +01:00
gingerBill d7e970ac32 Add extra debugf message for generate missing procedure 2021-08-03 14:36:15 +01:00
gingerBill 810dcfc602 Remove debug gb_printf_err and replace with debugf 2021-08-03 14:30:32 +01:00
gingerBill 14645b147f Reorganize missing_procedures_to_check generation code 2021-08-03 14:17:39 +01:00
gingerBill b036cc9013 Add missing_procedures_to_check to lbModule 2021-08-03 14:13:38 +01:00
gingerBill 3a8ac92995 Add sanity check in lb_create_procedure 2021-08-03 13:49:01 +01:00
gingerBill 67bedcba4b Reorganize stages in checker 2021-08-03 13:41:28 +01:00
gingerBill 4987ef89f1 Reorganize some of the checker stages 2021-08-03 13:40:47 +01:00
gingerBill 545b345eea Remove optimizations in tokenizer and default to older code (same logic) (optimize later) 2021-08-03 13:33:01 +01:00
gingerBill 3e961af5f1 Sort files by name in packages 2021-08-02 22:55:22 +01:00
291 changed files with 73248 additions and 22773 deletions
+24 -15
View File
@@ -10,12 +10,15 @@ jobs:
run: sudo apt-get install llvm-11 clang-11 llvm
- name: build odin
run: make release
- name: Odin run
run: ./odin run examples/demo/demo.odin
- name: Odin check
run: ./odin check examples/demo/demo.odin -vet
- name: Odin version
run: ./odin version
timeout-minutes: 1
- name: Odin check
run: ./odin check examples/demo/demo.odin -vet
timeout-minutes: 10
- name: Odin run
run: ./odin run examples/demo/demo.odin
timeout-minutes: 10
build_macOS:
runs-on: macos-latest
steps:
@@ -28,12 +31,15 @@ jobs:
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
- name: build odin
run: make release
- name: Odin run
run: ./odin run examples/demo/demo.odin
- name: Odin check
run: ./odin check examples/demo/demo.odin -vet
- name: Odin version
run: ./odin version
timeout-minutes: 1
- name: Odin check
run: ./odin check examples/demo/demo.odin -vet
timeout-minutes: 10
- name: Odin run
run: ./odin run examples/demo/demo.odin
timeout-minutes: 10
build_windows:
runs-on: windows-latest
steps:
@@ -43,17 +49,20 @@ jobs:
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
./build.bat 1
- name: Odin run
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo/demo.odin
- name: Odin version
run: ./odin version
timeout-minutes: 1
- name: Odin check
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/demo/demo.odin -vet
- name: Odin version
run: ./odin version
timeout-minutes: 10
- name: Odin run
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo/demo.odin
timeout-minutes: 10
+3
View File
@@ -28,6 +28,7 @@ jobs:
cp LLVM-C.dll dist
cp -r shared dist
cp -r core dist
cp -r vendor dist
cp -r bin dist
cp -r examples dist
- name: Upload artifact
@@ -51,6 +52,7 @@ jobs:
cp odin dist
cp -r shared dist
cp -r core dist
cp -r vendor dist
cp -r examples dist
- name: Upload artifact
uses: actions/upload-artifact@v1
@@ -77,6 +79,7 @@ jobs:
cp odin dist
cp -r shared dist
cp -r core dist
cp -r vendor dist
cp -r examples dist
- name: Upload artifact
uses: actions/upload-artifact@v1
+9 -5
View File
@@ -3,12 +3,16 @@ Copyright (c) 2016-2021 Ginger Bill. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+22 -25
View File
@@ -1,5 +1,5 @@
GIT_SHA=$(shell git rev-parse --short HEAD)
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined -Wno-unused-value
DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value
LDFLAGS=-pthread -ldl -lm -lstdc++
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
@@ -8,31 +8,31 @@ CC=clang
OS=$(shell uname)
ifeq ($(OS), Darwin)
LLVM_CONFIG=llvm-config
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
LLVM_CONFIG=llvm-config
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) -lLLVM-C
LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) -lLLVM-C
endif
ifeq ($(OS), Linux)
LLVM_CONFIG=llvm-config-11
ifneq ($(shell which llvm-config-11 2>/dev/null),)
LLVM_CONFIG=llvm-config-11
else
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
endif
LLVM_CONFIG=llvm-config-11
ifneq ($(shell which llvm-config-11 2>/dev/null),)
LLVM_CONFIG=llvm-config-11
else
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
endif
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
endif
all: debug demo
@@ -51,6 +51,3 @@ release_native:
nightly:
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
+3 -5
View File
@@ -46,10 +46,8 @@ if %release_mode% EQU 0 ( rem Debug
set compiler_warnings= ^
-W4 -WX ^
-wd4100 -wd4101 -wd4127 -wd4146 -wd4189 ^
-wd4201 -wd4204 ^
-wd4456 -wd4457 -wd4480 ^
-wd4512
-wd4100 -wd4101 -wd4127 -wd4146 ^
-wd4456 -wd4457
set compiler_includes=
set libs= ^
@@ -73,7 +71,7 @@ del *.ilk > NUL 2> NUL
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
if %errorlevel% neq 0 goto end_of_build
if %release_mode% EQU 0 odin run examples/demo/demo.odin
if %release_mode% EQU 0 odin run examples/demo
del *.obj > NUL 2> NUL
+1 -1
View File
@@ -18,7 +18,7 @@ def main():
name = remove_prefix(data['fileName'], "nightly/")
url = f"https://f001.backblazeb2.com/file/{bucket}/nightly/{urllib.parse.quote_plus(name)}"
sha1 = data['contentSha1']
size = int(data['contentLength'])
size = int(data['size'])
ts = int(data['fileInfo']['src_last_modified_millis'])
date = datetime.datetime.fromtimestamp(ts/1000).strftime('%Y-%m-%d')
+1 -1
View File
@@ -4,7 +4,7 @@ import "core:bytes"
import "core:io"
import "core:mem"
import "core:unicode/utf8"
import "intrinsics"
import "core:intrinsics"
// Extra errors returns by scanning procedures
Scanner_Extra_Error :: enum i32 {
+1 -3
View File
@@ -183,9 +183,7 @@ writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
for {
if writer_available(b) == 0 {
if ferr := writer_flush(b); ferr != nil {
return n, ferr;
}
writer_flush(b) or_return;
}
if b.max_consecutive_empty_writes <= 0 {
b.max_consecutive_empty_writes = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
+73
View File
@@ -0,0 +1,73 @@
# C support
The following is a mostly-complete projection of the C11 standard library as defined by the C11 specification: N1570, or ISO/IEC 9899:2011. Only the macros, types, and functions as required by the standard are projected. Extensions to C, such as POSIX are not handled by these bindings, this is otherwise portable to any implementation which can support a hosted C runtime.
## Support matrix
| Header | Status |
|:------------------|:---------------------------------------------------|
| `<assert.h>` | Not applicable, use Odin's `#assert` |
| `<complex.h>` | Mostly projected, see [limitations](#Limitations) |
| `<ctype.h>` | Fully projected |
| `<errno.h>` | Fully projected |
| `<fenv.h>` | Not projected |
| `<float.h>` | Not projected |
| `<inttypes.h>` | Fully projected |
| `<iso646.h>` | Not applicable, use Odin's operators |
| `<limits.h>` | Not projected |
| `<locale.h>` | Not projected |
| `<math.h>` | Mostly projected, see [limitations](#Limitations) |
| `<setjmp.h>` | Fully projected |
| `<signal.h>` | Fully projected |
| `<stdalign.h>` | Not applicable, use Odin's `#align` |
| `<stdarg.h>` | Mostly projected, see [limitations](#Limitations) |
| `<stdatomic.h>` | Fully projected |
| `<stdbool.h>` | Not applicable, use Odin's `b32` |
| `<stddef.h>` | Mostly projected, see [limitations](#Limitations) |
| `<stdint.h>` | Fully projected |
| `<stdio.h>` | Fully projected |
| `<stdlib.h>` | Fully projected |
| `<stdnoreturn.h>` | Not applicable, use Odin's divergent return `!` |
| `<string.h>` | Fully projected |
| `<tgmath.h>` | Mostly projected, see [limitations](#Limitations) |
| `<threads.h>` | Fully projected |
| `<time.h>` | Fully projected |
| `<uchar.h>` | Fully projected |
| `<wchar.h>` | Fully projected |
| `<wctype.h>` | Fully projected |
## Limitations
Not all C standard library functionality can be fully projected due to language differences. These limitations are listed here.
### `long double`
As Odin lacks a means to interact with `long double` in it's foreign interface, this projection effort does not bind or define anything requiring `long double` which is permitted by the C standard.
### `<complex.h>`
The special values `_Complex_I`, `_Imaginary_I` and the appropriate definition of `I` cannot be realized with the same type in Odin as it would be in C. The literal `1i` is tempting to use for these definitions but the semantics differ from C and would be confusing to use.
### `<math.h>`
The classification functions, e.g: `fpclassify` are required by C to be implemented as macros, meaning no implementation would expose functions in their library we could bind. Instead, we provide native Odin implementations with functionally equivalent semantics and behavior as the C ones. Unfortunately, since classification returns unspecified constant values this may be an ABI break where the value of those constants enter and exit native C code.
### `<stdarg.h>`
While Odin can interact with variable argument C functions through the use of the `#c_vararg` attribute within a foreign block, it's not actually possible to create procedures in Odin with bodies that have the same ABI as that of variable argument C functions, as a result `va_arg` is not projected.
### `<stddef.h>`
`offsetof` is not realizable in Odin, however you can use `offset_of` instead.
### `<tgmath.h>`
C has some strange promotion and type-coercion behavior for `<tgmath.h>` which isn't correctly handled by this projection, specifically involving the use of complex arithmetic and kernels. We do mostly support type-generic math through the use of Odin's explicit procedure overloading, however the semantic behavior of that doesn't match C and so literal expressions of complex type in C may not call the same underlying math kernel functions as they do in Odin through this projection.
## Caveats
In addition to limitations, there are some minor caveats you should be aware when using this projection.
* `errno()` is a function which returns `^int` rather than a macro.
* `MB_CUR_MAX()` is a function which return `size_t` rather than a macro.
* Currently only works on Windows (MSVCRT) and Linux (GLIBC or MUSL)
## License
Every file within this directory is made available under Odin's BSD-2 license
with the following copyright.
```
Copyright 2021 Dale Weiler <weilercdale@gmail.com>.
```
+82
View File
@@ -0,0 +1,82 @@
package libc
// 7.3 Complex arithmetic
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
@(default_calling_convention="c")
foreign libc {
// 7.3.5 Trigonometric functions
cacos :: proc(z: complex_double) -> complex_double ---;
cacosf :: proc(z: complex_float) -> complex_float ---;
casin :: proc(z: complex_double) -> complex_double ---;
casinf :: proc(z: complex_float) -> complex_float ---;
catan :: proc(z: complex_double) -> complex_double ---;
catanf :: proc(z: complex_float) -> complex_float ---;
ccos :: proc(z: complex_double) -> complex_double ---;
ccosf :: proc(z: complex_float) -> complex_float ---;
csin :: proc(z: complex_double) -> complex_double ---;
csinf :: proc(z: complex_float) -> complex_float ---;
ctan :: proc(z: complex_double) -> complex_double ---;
ctanf :: proc(z: complex_float) -> complex_float ---;
// 7.3.6 Hyperbolic functions
cacosh :: proc(z: complex_double) -> complex_double ---;
cacoshf :: proc(z: complex_float) -> complex_float ---;
casinh :: proc(z: complex_double) -> complex_double ---;
casinhf :: proc(z: complex_float) -> complex_float ---;
catanh :: proc(z: complex_double) -> complex_double ---;
catanhf :: proc(z: complex_float) -> complex_float ---;
ccosh :: proc(z: complex_double) -> complex_double ---;
ccoshf :: proc(z: complex_float) -> complex_float ---;
csinh :: proc(z: complex_double) -> complex_double ---;
csinhf :: proc(z: complex_float) -> complex_float ---;
ctanh :: proc(z: complex_double) -> complex_double ---;
ctanhf :: proc(z: complex_float) -> complex_float ---;
// 7.3.7 Exponential and logarithmic functions
cexp :: proc(z: complex_double) -> complex_double ---;
cexpf :: proc(z: complex_float) -> complex_float ---;
clog :: proc(z: complex_double) -> complex_double ---;
clogf :: proc(z: complex_float) -> complex_float ---;
// 7.3.8 Power and absolute-value functions
cabs :: proc(z: complex_double) -> complex_double ---;
cabsf :: proc(z: complex_float) -> complex_float ---;
cpow :: proc(z: complex_double) -> complex_double ---;
cpowf :: proc(z: complex_float) -> complex_float ---;
csqrt :: proc(z: complex_double) -> complex_double ---;
csqrtf :: proc(z: complex_float) -> complex_float ---;
// 7.3.9 Manipulation functions
carg :: proc(z: complex_double) -> double ---;
cargf :: proc(z: complex_float) -> float ---;
cimag :: proc(z: complex_double) -> double ---;
cimagf :: proc(z: complex_float) -> float ---;
conj :: proc(z: complex_double) -> complex_double ---;
conjf :: proc(z: complex_float) -> complex_float ---;
cproj :: proc(z: complex_double) -> complex_double ---;
cprojf :: proc(z: complex_float) -> complex_float ---;
creal :: proc(z: complex_double) -> double ---;
crealf :: proc(z: complex_float) -> float ---;
}
import builtin "core:builtin"
complex_float :: distinct builtin.complex64;
complex_double :: distinct builtin.complex128;
// Cannot implement _Complex_I or _Imaginary_I in Odin, thus
// complex and imaginary cannot be implement either.
CMPLX :: #force_inline proc(x, y: double) -> complex_double {
return builtin.complex(x, y);
}
CMPLXF :: #force_inline proc(x, y: float) -> complex_float {
return builtin.complex(x, y);
}
+30
View File
@@ -0,0 +1,30 @@
package libc
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
// 7.4 Character handling
@(default_calling_convention="c")
foreign libc {
// 7.4.1 Character classification functions
isalnum :: proc(c: int) -> int ---;
isalpha :: proc(c: int) -> int ---;
isblank :: proc(c: int) -> int ---;
iscntrl :: proc(c: int) -> int ---;
isdigit :: proc(c: int) -> int ---;
isgraph :: proc(c: int) -> int ---;
islower :: proc(c: int) -> int ---;
isprint :: proc(c: int) -> int ---;
ispunct :: proc(c: int) -> int ---;
isspace :: proc(c: int) -> int ---;
isupper :: proc(c: int) -> int ---;
isxdigit :: proc(c: int) -> int ---;
// 7.4.2 Character case mapping functions
tolower :: proc(c: int) -> int ---;
toupper :: proc(c: int) -> int ---;
}
+47
View File
@@ -0,0 +1,47 @@
package libc
// 7.5 Errors
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
// C11 standard only requires the definition of:
// EDOM,
// EILSEQ
// ERANGE
when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
@(private="file")
@(default_calling_convention="c")
foreign libc {
@(link_name="__libc_errno_location")
_get_errno :: proc() -> ^int ---;
}
EDOM :: 33;
EILSEQ :: 84;
ERANGE :: 34;
}
when ODIN_OS == "windows" {
@(private="file")
@(default_calling_convention="c")
foreign libc {
@(link_name="_errno")
_get_errno :: proc() -> ^int ---;
}
EDOM :: 33;
EILSEQ :: 42;
ERANGE :: 34;
}
// Odin has no way to make an identifier "errno" behave as a function call to
// read the value, or to produce an lvalue such that you can assign a different
// error value to errno. To work around this, just expose it as a function like
// it actually is.
errno :: #force_inline proc() -> ^int {
return _get_errno();
}
+400
View File
@@ -0,0 +1,400 @@
package libc
// 7.12 Mathematics
import "core:intrinsics"
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
// To support C's tgmath behavior we use Odin's explicit procedure overloading,
// but we cannot use the same names as exported by libc so use @(link_name)
// and keep them as private symbols of name "libc_"
@(private="file")
@(default_calling_convention="c")
foreign libc {
// 7.12.4 Trigonometric functions
@(link_name="acos") libc_acos :: proc(x: double) -> double ---;
@(link_name="acosf") libc_acosf :: proc(x: float) -> float ---;
@(link_name="asin") libc_asin :: proc(x: double) -> double ---;
@(link_name="asinf") libc_asinf :: proc(x: float) -> float ---;
@(link_name="atan") libc_atan :: proc(x: double) -> double ---;
@(link_name="atanf") libc_atanf :: proc(x: float) -> float ---;
@(link_name="atan2") libc_atan2 :: proc(y: double, x: double) -> double ---;
@(link_name="atan2f") libc_atan2f :: proc(y: float, x: float) -> float ---;
@(link_name="cos") libc_cos :: proc(x: double) -> double ---;
@(link_name="cosf") libc_cosf :: proc(x: float) -> float ---;
@(link_name="sin") libc_sin :: proc(x: double) -> double ---;
@(link_name="sinf") libc_sinf :: proc(x: float) -> float ---;
@(link_name="tan") libc_tan :: proc(x: double) -> double ---;
@(link_name="tanf") libc_tanf :: proc(x: float) -> float ---;
// 7.12.5 Hyperbolic functions
@(link_name="acosh") libc_acosh :: proc(x: double) -> double ---;
@(link_name="acoshf") libc_acoshf :: proc(x: float) -> float ---;
@(link_name="asinh") libc_asinh :: proc(x: double) -> double ---;
@(link_name="asinhf") libc_asinhf :: proc(x: float) -> float ---;
@(link_name="atanh") libc_atanh :: proc(x: double) -> double ---;
@(link_name="atanhf") libc_atanhf :: proc(x: float) -> float ---;
@(link_name="cosh") libc_cosh :: proc(x: double) -> double ---;
@(link_name="coshf") libc_coshf :: proc(x: float) -> float ---;
@(link_name="sinh") libc_sinh :: proc(x: double) -> double ---;
@(link_name="sinhf") libc_sinhf :: proc(x: float) -> float ---;
@(link_name="tanh") libc_tanh :: proc(x: double) -> double ---;
@(link_name="tanhf") libc_tanhf :: proc(x: float) -> float ---;
// 7.12.6 Exponential and logarithmic functions
@(link_name="exp") libc_exp :: proc(x: double) -> double ---;
@(link_name="expf") libc_expf :: proc(x: float) -> float ---;
@(link_name="exp2") libc_exp2 :: proc(x: double) -> double ---;
@(link_name="exp2f") libc_exp2f :: proc(x: float) -> float ---;
@(link_name="expm1") libc_expm1 :: proc(x: double) -> double ---;
@(link_name="expm1f") libc_expm1f :: proc(x: float) -> float ---;
@(link_name="frexp") libc_frexp :: proc(value: double, exp: ^int) -> double ---;
@(link_name="frexpf") libc_frexpf :: proc(value: float, exp: ^int) -> float ---;
@(link_name="ilogb") libc_ilogb :: proc(x: double) -> int ---;
@(link_name="ilogbf") libc_ilogbf :: proc(x: float) -> int ---;
@(link_name="ldexp") libc_ldexp :: proc(x: double, exp: int) -> double ---;
@(link_name="ldexpf") libc_ldexpf :: proc(x: float, exp: int) -> float ---;
@(link_name="log") libc_log :: proc(x: double) -> double ---;
@(link_name="logf") libc_logf :: proc(x: float) -> float ---;
@(link_name="log10") libc_log10 :: proc(x: double) -> double ---;
@(link_name="log10f") libc_log10f :: proc(x: float) -> float ---;
@(link_name="log1p") libc_log1p :: proc(x: double) -> double ---;
@(link_name="log1pf") libc_log1pf :: proc(x: float) -> float ---;
@(link_name="log2") libc_log2 :: proc(x: double) -> double ---;
@(link_name="log2f") libc_log2f :: proc(x: float) -> float ---;
@(link_name="logb") libc_logb :: proc(x: double) -> double ---;
@(link_name="logbf") libc_logbf :: proc(x: float) -> float ---;
@(link_name="modf") libc_modf :: proc(value: double, iptr: ^double) -> double ---;
@(link_name="modff") libc_modff :: proc(value: float, iptr: ^float) -> float ---;
@(link_name="scalbn") libc_scalbn :: proc(x: double, n: int) -> double ---;
@(link_name="scalbnf") libc_scalbnf :: proc(x: float, n: int) -> float ---;
@(link_name="scalbln") libc_scalbln :: proc(x: double, n: long) -> double ---;
@(link_name="scalblnf") libc_scalblnf :: proc(x: float, n: long) -> float ---;
// 7.12.7 Power and absolute-value functions
@(link_name="cbrt") libc_cbrt :: proc(x: double) -> double ---;
@(link_name="cbrtf") libc_cbrtf :: proc(x: float) -> float ---;
@(link_name="fabs") libc_fabs :: proc(x: double) -> double ---;
@(link_name="fabsf") libc_fabsf :: proc(x: float) -> float ---;
@(link_name="hypot") libc_hypot :: proc(x: double, y: double) -> double ---;
@(link_name="hypotf") libc_hypotf :: proc(x: float, y: float) -> float ---;
@(link_name="pow") libc_pow :: proc(x: double, y: double) -> double ---;
@(link_name="powf") libc_powf :: proc(x: float, y: float) -> float ---;
@(link_name="sqrt") libc_sqrt :: proc(x: double) -> double ---;
@(link_name="sqrtf") libc_sqrtf :: proc(x: float) -> float ---;
// 7.12.8 Error and gamma functions
@(link_name="erf") libc_erf :: proc(x: double) -> double ---;
@(link_name="erff") libc_erff :: proc(x: float) -> float ---;
@(link_name="erfc") libc_erfc :: proc(x: double) -> double ---;
@(link_name="erfcf") libc_erfcf :: proc(x: float) -> float ---;
@(link_name="lgamma") libc_lgamma :: proc(x: double) -> double ---;
@(link_name="lgammaf") libc_lgammaf :: proc(x: float) -> float ---;
@(link_name="tgamma") libc_tgamma :: proc(x: double) -> double ---;
@(link_name="tgammaf") libc_tgammaf :: proc(x: float) -> float ---;
// 7.12.9 Nearest integer functions
@(link_name="ceil") libc_ceil :: proc(x: double) -> double ---;
@(link_name="ceilf") libc_ceilf :: proc(x: float) -> float ---;
@(link_name="floor") libc_floor :: proc(x: double) -> double ---;
@(link_name="floorf") libc_floorf :: proc(x: float) -> float ---;
@(link_name="nearbyint") libc_nearbyint :: proc(x: double) -> double ---;
@(link_name="nearbyintf") libc_nearbyintf :: proc(x: float) -> float ---;
@(link_name="rint") libc_rint :: proc(x: double) -> double ---;
@(link_name="rintf") libc_rintf :: proc(x: float) -> float ---;
@(link_name="lrint") libc_lrint :: proc(x: double) -> long ---;
@(link_name="lrintf") libc_lrintf :: proc(x: float) -> long ---;
@(link_name="llrint") libc_llrint :: proc(x: double) -> longlong ---;
@(link_name="llrintf") libc_llrintf :: proc(x: float) -> longlong ---;
@(link_name="round") libc_round :: proc(x: double) -> double ---;
@(link_name="roundf") libc_roundf :: proc(x: float) -> float ---;
@(link_name="lround") libc_lround :: proc(x: double) -> long ---;
@(link_name="lroundf") libc_lroundf :: proc(x: float) -> long ---;
@(link_name="llround") libc_llround :: proc(x: double) -> longlong ---;
@(link_name="llroundf") libc_llroundf :: proc(x: float) -> longlong ---;
@(link_name="trunc") libc_trunc :: proc(x: double) -> double ---;
@(link_name="truncf") libc_truncf :: proc(x: float) -> float ---;
// 7.12.10 Remainder functions
@(link_name="fmod") libc_fmod :: proc(x: double, y: double) -> double ---;
@(link_name="fmodf") libc_fmodf :: proc(x: float, y: float) -> float ---;
@(link_name="remainder") libc_remainder :: proc(x: double, y: double) -> double ---;
@(link_name="remainderf") libc_remainderf :: proc(x: float, y: float) -> float ---;
@(link_name="remquo") libc_remquo :: proc(x: double, y: double, quo: ^int) -> double ---;
@(link_name="remquof") libc_remquof :: proc(x: float, y: float, quo: ^int) -> float ---;
// 7.12.11 Manipulation functions
@(link_name="copysign") libc_copysign :: proc(x: double, y: double) -> double ---;
@(link_name="copysignf") libc_copysignf :: proc(x: float, y: float) -> float ---;
@(link_name="nan") libc_nan :: proc(tagp: cstring) -> double ---;
@(link_name="nanf") libc_nanf :: proc(tagp: cstring) -> float ---;
@(link_name="nextafter") libc_nextafter :: proc(x: double, y: double) -> double ---;
@(link_name="nextafterf") libc_nextafterf :: proc(x: float, y: float) -> float ---;
// 7.12.12 Maximum, minimum, and positive difference functions
@(link_name="fdim") libc_fdim :: proc(x: double, y: double) -> double ---;
@(link_name="fdimf") libc_fdimf :: proc(x: float, y: float) -> float ---;
@(link_name="fmax") libc_fmax :: proc(x: double, y: double) -> double ---;
@(link_name="fmaxf") libc_fmaxf :: proc(x: float, y: float) -> float ---;
@(link_name="fmin") libc_fmin :: proc(x: double, y: double) -> double ---;
@(link_name="fminf") libc_fminf :: proc(x: float, y: float) -> float ---;
@(link_name="fma") libc_fma :: proc(x, y, z: double) -> double ---;
@(link_name="fmaf") libc_fmaf :: proc(x, y, z: float) -> float ---;
}
@(private="file")
_nan_bit_pattern := ~u64(0);
// On amd64 Windows and Linux, float_t and double_t are respectively both
// their usual types. On x86 it's not possible to define these types correctly
// since they would be long double which Odin does have support for.
float_t :: float;
double_t :: double;
NAN := transmute(double)(_nan_bit_pattern);
INFINITY :: 1e5000;
HUGE_VALF :: INFINITY;
HUGE_VAL :: double(INFINITY);
MATH_ERRNO :: 1;
MATH_ERREXCEPT :: 2;
math_errhandling :: 2; // Windows, Linux, macOS all use this mode.
FP_ILOGBNAN :: -1 - int((~uint(0)) >> 1);
FP_ILOGB0 :: FP_ILOGBNAN;
// Number classification constants. These do not have to match libc since we
// implement our own classification functions as libc requires they be macros,
// which means libc does not export standard functions for them.
FP_NAN :: 0;
FP_INFINITE :: 1;
FP_ZERO :: 2;
FP_NORMAL :: 3;
FP_SUBNORMAL :: 4;
@(private)
_fpclassify :: #force_inline proc(x: double) -> int {
u := transmute(uint64_t)x;
e := u >> 52 & 0x7ff;
if e == 0 do return FP_SUBNORMAL if (u << 1) != 0 else FP_ZERO;
if e == 0x7ff do return FP_NAN if (u << 12) != 0 else FP_INFINITE;
return FP_NORMAL;
}
@(private)
_fpclassifyf :: #force_inline proc(x: float) -> int {
u := transmute(uint32_t)x;
e := u >> 23 & 0xff;
if e == 0 do return FP_SUBNORMAL if (u << 1) != 0 else FP_ZERO;
if e == 0xff do return FP_NAN if (u << 9) != 0 else FP_INFINITE;
return FP_NORMAL;
}
@(private)
_signbit :: #force_inline proc(x: double) -> int {
return int(transmute(uint64_t)x >> 63);
}
@(private)
_signbitf :: #force_inline proc(x: float) -> int {
return int(transmute(uint32_t)x >> 31);
}
isfinite :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
return fpclassify(x) == FP_INFINITE;
}
isinf :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
return fpclassify(x) > FP_INFINITE;
}
isnan :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
return fpclassify(x) == FP_NAN;
}
isnormal :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
return fpclassify(x) == FP_NORMAL;
}
// These are special in that they avoid float exceptions. They cannot just be
// implemented as the relational comparisons, as that would produce an invalid
// "sticky" state that propagates and affects maths results. These need
// to be implemented natively in Odin assuming isunordered to prevent that.
isgreater :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
return !isunordered(x, y) && x > y;
}
isgreaterequal :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
return !isunordered(x, y) && x >= y;
}
isless :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
return !isunordered(x, y) && x < y;
}
islessequal :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
return !isunordered(x, y) && x <= y;
}
islessgreater :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
return !isunordered(x, y) && x <= y;
}
isunordered :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
if isnan(x) {
// Force evaluation of y to propagate exceptions for ordering semantics.
// To ensure correct semantics of IEEE 754 this cannot be compiled away.
sink: T;
intrinsics.volatile_store(&sink, intrinsics.volatile_load(&y));
return true;
}
return isnan(y);
}
fpclassify :: proc{_fpclassify, _fpclassifyf};
signbit :: proc{_signbit, _signbitf};
// Emulate tgmath.h behavior with explicit procedure overloading here.
acos :: proc{libc_acos, libc_acosf, cacos, cacosf};
asin :: proc{libc_asin, libc_asinf, casin, casinf};
atan :: proc{libc_atan, libc_atanf, catan, catanf};
atan2 :: proc{libc_atan2, libc_atan2f};
cos :: proc{libc_cos, libc_cosf, ccos, ccosf};
sin :: proc{libc_sin, libc_sinf, csin, csinf};
tan :: proc{libc_tan, libc_tanf, ctan, ctanf};
acosh :: proc{libc_acosh, libc_acoshf, cacosh, cacoshf};
asinh :: proc{libc_asinh, libc_asinhf, casinh, casinhf};
atanh :: proc{libc_atanh, libc_atanhf, catanh, catanhf};
cosh :: proc{libc_cosh, libc_coshf, ccosh, ccoshf};
sinh :: proc{libc_sinh, libc_sinhf, csinh, csinhf};
tanh :: proc{libc_tanh, libc_tanhf, ctanh, ctanhf};
exp :: proc{libc_exp, libc_expf, cexp, cexpf};
exp2 :: proc{libc_exp2, libc_exp2f};
expm1 :: proc{libc_expm1, libc_expm1f};
frexp :: proc{libc_frexp, libc_frexpf};
ilogb :: proc{libc_ilogb, libc_ilogbf};
ldexp :: proc{libc_ldexp, libc_ldexpf};
log :: proc{libc_log, libc_logf, clog, clogf};
log10 :: proc{libc_log10, libc_log10f};
log1p :: proc{libc_log1p, libc_log1pf};
log2 :: proc{libc_log2, libc_log2f};
logb :: proc{libc_logb, libc_logbf};
modf :: proc{libc_modf, libc_modff};
scalbn :: proc{libc_scalbn, libc_scalbnf};
scalbln :: proc{libc_scalbln, libc_scalblnf};
cbrt :: proc{libc_cbrt, libc_cbrtf};
fabs :: proc{libc_fabs, libc_fabsf, cabs, cabsf};
hypot :: proc{libc_hypot, libc_hypotf};
pow :: proc{libc_pow, libc_powf, cpow, cpowf};
sqrt :: proc{libc_sqrt, libc_sqrtf, csqrt, csqrtf};
erf :: proc{libc_erf, libc_erff};
erfc :: proc{libc_erfc, libc_erfcf};
lgamma :: proc{libc_lgamma, libc_lgammaf};
tgamma :: proc{libc_tgamma, libc_tgammaf};
ceil :: proc{libc_ceil, libc_ceilf};
floor :: proc{libc_floor, libc_floorf};
nearbyint :: proc{libc_nearbyint, libc_nearbyintf};
rint :: proc{libc_rint, libc_rintf};
lrint :: proc{libc_lrint, libc_lrintf};
llrint :: proc{libc_llrint, libc_llrintf};
round :: proc{libc_round, libc_roundf};
lround :: proc{libc_lround, libc_lroundf};
llround :: proc{libc_llround, libc_llroundf};
trunc :: proc{libc_trunc, libc_truncf};
fmod :: proc{libc_fmod, libc_fmodf};
remainder :: proc{libc_remainder, libc_remainderf};
remquo :: proc{libc_remquo, libc_remquof};
copysign :: proc{libc_copysign, libc_copysignf};
nextafter :: proc{libc_nextafter, libc_nextafterf};
fdim :: proc{libc_fdim, libc_fdimf};
fmax :: proc{libc_fmax, libc_fmaxf};
fmin :: proc{libc_fmin, libc_fminf};
fma :: proc{libc_fma, libc_fmaf};
// But retain the 'f' suffix-variant functions as well so they can be used,
// a trick is used here where we use explicit procedrual overloading of one
// procedure. This is done because the foreign block is marked @(private) and
// aliasing functions does not remove privateness from the entity.
acosf :: proc{libc_acosf};
asinf :: proc{libc_asinf};
atanf :: proc{libc_atanf};
atan2f :: proc{libc_atan2f};
cosf :: proc{libc_cosf};
sinf :: proc{libc_sinf};
tanf :: proc{libc_tanf};
acoshf :: proc{libc_acoshf};
asinhf :: proc{libc_asinhf};
atanhf :: proc{libc_atanhf};
coshf :: proc{libc_coshf};
sinhf :: proc{libc_sinhf};
tanhf :: proc{libc_tanhf};
expf :: proc{libc_expf};
exp2f :: proc{libc_exp2f};
expm1f :: proc{libc_expm1f};
frexpf :: proc{libc_frexpf};
ilogbf :: proc{libc_ilogbf};
ldexpf :: proc{libc_ldexpf};
logf :: proc{libc_logf};
log10f :: proc{libc_log10f};
log1pf :: proc{libc_log1pf};
log2f :: proc{libc_log2f};
logbf :: proc{libc_logbf};
modff :: proc{libc_modff};
scalbnf :: proc{libc_scalbnf};
scalblnf :: proc{libc_scalblnf};
cbrtf :: proc{libc_cbrtf};
fabsf :: proc{libc_fabsf};
hypotf :: proc{libc_hypotf};
powf :: proc{libc_powf};
sqrtf :: proc{libc_sqrtf};
erff :: proc{libc_erff};
erfcf :: proc{libc_erfcf};
lgammaf :: proc{libc_lgammaf};
tgammaf :: proc{libc_tgammaf};
ceilf :: proc{libc_ceilf};
floorf :: proc{libc_floorf};
nearbyintf :: proc{libc_nearbyintf};
rintf :: proc{libc_rintf};
lrintf :: proc{libc_lrintf};
llrintf :: proc{libc_llrintf};
roundf :: proc{libc_roundf};
lroundf :: proc{libc_lroundf};
llroundf :: proc{libc_llroundf};
truncf :: proc{libc_truncf};
fmodf :: proc{libc_fmodf};
remainderf :: proc{libc_remainderf};
remquof :: proc{libc_remquof};
copysignf :: proc{libc_copysignf};
nextafterf :: proc{libc_nextafterf};
fdimf :: proc{libc_fdimf};
fmaxf :: proc{libc_fmaxf};
fminf :: proc{libc_fminf};
fmaf :: proc{libc_fmaf};
// These two functions are special and not made type generic in tgmath.h since
// they only differ by their return type.
nan :: proc{libc_nan};
nanf :: proc{libc_nanf};
+65
View File
@@ -0,0 +1,65 @@
package libc
// 7.13 Nonlocal jumps
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
when ODIN_OS == "windows" {
@(default_calling_convention="c")
foreign libc {
// 7.13.1 Save calling environment
//
// NOTE(dweiler): C11 requires setjmp be a macro, which means it won't
// necessarily export a symbol named setjmp but rather _setjmp in the case
// of musl, glibc, BSD libc, and msvcrt.
//
/// NOTE(dweiler): UCRT has two implementations of longjmp. One that performs
// stack unwinding and one that doesn't. The choice of which to use depends on a
// flag which is set inside the jmp_buf structure given to setjmp. The default
// behavior is to unwind the stack. Within Odin, we cannot use the stack
// unwinding version as the unwinding information isn't present. To opt-in to
// the regular non-unwinding version we need a way to set this flag. Since the
// location of the flag within the struct is not defined or part of the ABI and
// can change between versions of UCRT, we must rely on setjmp to set it. It
// turns out that setjmp receives this flag in the RDX register on Win64, this
// just so happens to coincide with the second argument of a function in the
// Win64 ABI. By giving our setjmp a second argument with the value of zero,
// the RDX register will contain zero and correctly set the flag to disable
// stack unwinding.
@(link_name="_setjmp")
setjmp :: proc(env: ^jmp_buf, hack: rawptr = nil) -> int ---;
}
} else {
@(default_calling_convention="c")
foreign libc {
// 7.13.1 Save calling environment
//
// NOTE(dweiler): C11 requires setjmp be a macro, which means it won't
// necessarily export a symbol named setjmp but rather _setjmp in the case
// of musl, glibc, BSD libc, and msvcrt.
@(link_name="_setjmp")
setjmp :: proc(env: ^jmp_buf) -> int ---;
}
}
@(default_calling_convention="c")
foreign libc {
// 7.13.2 Restore calling environment
longjmp :: proc(env: ^jmp_buf, val: int) -> ! ---;
}
// The C99 Rationale describes jmp_buf as being an array type for backward
// compatibility. Odin does not need to honor this and couldn't as arrays in
// Odin don't decay to pointers. It is somewhat easy for us to bind this, we
// just need to ensure the structure contains enough storage with appropriate
// alignment. Since there are no types in C with an alignment larger than
// that of max_align_t, which cannot be larger than sizeof(long double) as any
// other exposed type wouldn't be valid C, the maximum alignment possible in a
// strictly conformant C implementation is 16 on the platforms we care about.
// The choice of 4096 bytes for storage of this type is more than enough on all
// relevant platforms.
jmp_buf :: struct #align 16 { _: [4096]char, };
+43
View File
@@ -0,0 +1,43 @@
package libc
// 7.14 Signal handling
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
sig_atomic_t :: distinct atomic_int;
@(default_calling_convention="c")
foreign libc {
signal :: proc(sig: int, func: proc "c" (int)) -> proc "c" (int) ---;
raise :: proc(sig: int) -> int ---;
}
when ODIN_OS == "windows" {
SIG_ERR :: rawptr(~uintptr(0));
SIG_DFL :: rawptr(uintptr(0));
SIG_IGN :: rawptr(uintptr(1));
SIGABRT :: 22;
SIGFPE :: 8;
SIGILL :: 4;
SIGINT :: 2;
SIGSEGV :: 11;
SIGTERM :: 15;
}
when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
SIG_ERR :: rawptr(~uintptr(0));
SIG_DFL :: rawptr(uintptr(0));
SIG_IGN :: rawptr(uintptr(1));
SIGABRT :: 6;
SIGFPE :: 8;
SIGILL :: 4;
SIGINT :: 2;
SIGSEGV :: 11;
SIGTERM :: 15;
}
+43
View File
@@ -0,0 +1,43 @@
package libc
// 7.16 Variable arguments
import "core:intrinsics"
import "core:runtime"
import "core:mem"
@(private="file")
@(default_calling_convention="none")
foreign _ {
@(link_name="llvm.va_start") _va_start :: proc(arglist: ^i8) ---;
@(link_name="llvm.va_end") _va_end :: proc(arglist: ^i8) ---;
@(link_name="llvm.va_copy") _va_copy :: proc(dst, src: ^i8) ---;
}
// Since there are no types in C with an alignment larger than that of
// max_align_t, which cannot be larger than sizeof(long double) as any other
// exposed type wouldn't be valid C, the maximum alignment possible in a
// strictly conformant C implementation is 16 on the platforms we care about.
// The choice of 4096 bytes for storage of this type is more than enough on all
// relevant platforms.
va_list :: struct #align 16 {
_: [4096]u8,
};
va_start :: #force_inline proc(ap: ^va_list, _: any) {
_va_start(cast(^i8)ap);
}
va_end :: #force_inline proc(ap: ^va_list) {
_va_end(cast(^i8)ap);
}
va_copy :: #force_inline proc(dst, src: ^va_list) {
_va_copy(cast(^i8)dst, cast(^i8)src);
}
// We cannot provide va_arg as there is no way to create "C" style procedures
// in Odin which take variable arguments the C way. The #c_vararg attribute only
// exists for foreign imports. That being said, being able to copy a va_list,
// as well as start and end one is necessary in some functions, the va_list
// taking functions in libc as an example.
+416
View File
@@ -0,0 +1,416 @@
package libc
// 7.17 Atomics
import "core:intrinsics"
ATOMIC_BOOL_LOCK_FREE :: true;
ATOMIC_CHAR_LOCK_FREE :: true;
ATOMIC_CHAR16_T_LOCK_FREE :: true;
ATOMIC_CHAR32_T_LOCK_FREE :: true;
ATOMIC_WCHAR_T_LOCK_FREE :: true;
ATOMIC_SHORT_LOCK_FREE :: true;
ATOMIC_INT_LOCK_FREE :: true;
ATOMIC_LONG_LOCK_FREE :: true;
ATOMIC_LLONG_LOCK_FREE :: true;
ATOMIC_POINTER_LOCK_FREE :: true;
// 7.17.3 Order and consistency
memory_order :: enum int {
relaxed,
consume,
acquire,
release,
acq_rel,
seq_cst,
}
memory_order_relaxed :: memory_order.relaxed;
memory_order_consume :: memory_order.consume;
memory_order_acquire :: memory_order.acquire;
memory_order_release :: memory_order.release;
memory_order_acq_rel :: memory_order.acq_rel;
memory_order_seq_cst :: memory_order.seq_cst;
// 7.17.2 Initialization
ATOMIC_VAR_INIT :: #force_inline proc(value: $T) -> T {
return value;
}
atomic_init :: #force_inline proc(obj: ^$T, value: T) {
intrinsics.atomic_store(obj, value);
}
kill_dependency :: #force_inline proc(value: $T) -> T {
return value;
}
// 7.17.4 Fences
atomic_thread_fence :: #force_inline proc(order: memory_order) {
switch (order) {
case .relaxed:
return;
case .consume:
intrinsics.atomic_fence_acq();
case .acquire:
intrinsics.atomic_fence_acq();
case .release:
intrinsics.atomic_fence_rel();
case .acq_rel:
intrinsics.atomic_fence_acqrel();
case .seq_cst:
intrinsics.atomic_fence_acqrel();
}
}
atomic_signal_fence :: #force_inline proc(order: memory_order) {
atomic_thread_fence(order);
}
// 7.17.5 Lock-free property
atomic_is_lock_free :: #force_inline proc(obj: ^$T) -> bool {
return size_of(T) <= 8 && (intrinsics.type_is_integer(T) || intrinsics.type_is_pointer(T));
}
// 7.17.6 Atomic integer types
atomic_bool :: distinct bool;
atomic_char :: distinct char;
atomic_schar :: distinct char;
atomic_uchar :: distinct uchar;
atomic_short :: distinct short;
atomic_ushort :: distinct ushort;
atomic_int :: distinct int;
atomic_uint :: distinct uint;
atomic_long :: distinct long;
atomic_ulong :: distinct ulong;
atomic_llong :: distinct longlong;
atomic_ullong :: distinct ulonglong;
atomic_char16_t :: distinct char16_t;
atomic_char32_t :: distinct char32_t;
atomic_wchar_t :: distinct wchar_t;
atomic_int_least8_t :: distinct int_least8_t;
atomic_uint_least8_t :: distinct uint_least8_t;
atomic_int_least16_t :: distinct int_least16_t;
atomic_uint_least16_t :: distinct uint_least16_t;
atomic_int_least32_t :: distinct int_least32_t;
atomic_uint_least32_t :: distinct uint_least32_t;
atomic_int_least64_t :: distinct int_least64_t;
atomic_uint_least64_t :: distinct uint_least64_t;
atomic_int_fast8_t :: distinct int_fast8_t;
atomic_uint_fast8_t :: distinct uint_fast8_t;
atomic_int_fast16_t :: distinct int_fast16_t;
atomic_uint_fast16_t :: distinct uint_fast16_t;
atomic_int_fast32_t :: distinct int_fast32_t;
atomic_uint_fast32_t :: distinct uint_fast32_t;
atomic_int_fast64_t :: distinct int_fast64_t;
atomic_uint_fast64_t :: distinct uint_fast64_t;
atomic_intptr_t :: distinct intptr_t;
atomic_uintptr_t :: distinct uintptr_t;
atomic_size_t :: distinct size_t;
atomic_ptrdiff_t :: distinct ptrdiff_t;
atomic_intmax_t :: distinct intmax_t;
atomic_uintmax_t :: distinct uintmax_t;
// 7.17.7 Operations on atomic types
atomic_store :: #force_inline proc(object: ^$T, desired: T) {
intrinsics.atomic_store(object, desired);
}
atomic_store_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) {
assert(order != .consume);
assert(order != .acquire);
assert(order != .acq_rel);
#partial switch (order) {
case .relaxed:
intrinsics.atomic_store_relaxed(object, desired);
case .release:
intrinsics.atomic_store_rel(object, desired);
case .seq_cst:
intrinsics.atomic_store(object, desired);
}
}
atomic_load :: #force_inline proc(object: ^$T) -> T {
return intrinsics.atomic_load(object);
}
atomic_load_explicit :: #force_inline proc(object: ^$T, order: memory_order) {
assert(order != .release);
assert(order != .acq_rel);
#partial switch (order) {
case .relaxed:
return intrinsics.atomic_load_relaxed(object);
case .consume:
return intrinsics.atomic_load_acq(object);
case .acquire:
return intrinsics.atomic_load_acq(object);
case .seq_cst:
return intrinsics.atomic_load(object);
}
}
atomic_exchange :: #force_inline proc(object: ^$T, desired: T) -> T {
return intrinsics.atomic_xchg(object, desired);
}
atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_xchg_relaxed(object, desired);
case .consume:
return intrinsics.atomic_xchg_acq(object, desired);
case .acquire:
return intrinsics.atomic_xchg_acq(object, desired);
case .release:
return intrinsics.atomic_xchg_rel(object, desired);
case .acq_rel:
return intrinsics.atomic_xchg_acqrel(object, desired);
case .seq_cst:
return intrinsics.atomic_xchg(object, desired);
}
return false;
}
// C does not allow failure memory order to be order_release or acq_rel.
// Similarly, it does not allow the failure order to be stronger than success
// order. Since consume and acquire are both monotonic, we can count them as
// one, for a total of three memory orders that are relevant in compare exchange.
// relaxed, acquire (consume), seq_cst.
// The requirement that the failure order cannot be stronger than success limits
// the valid combinations for the failure order to this table:
// [success = seq_cst, failure = seq_cst] => _
// [success = acquire, failure = seq_cst] => acq
// [success = release, failure = seq_cst] => rel
// [success = acq_rel, failure = seq_cst] => acqrel
// [success = relaxed, failure = relaxed] => relaxed
// [success = seq_cst, failure = relaxed] => failrelaxed
// [success = seq_cst, failure = acquire] => failacq
// [success = acquire, failure = relaxed] => acq_failrelaxed
// [success = acq_rel, failure = relaxed] => acqrel_failrelaxed
atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) {
value, ok := intrinsics.atomic_cxchg(object, expected^, desired);
if !ok do expected^ = value;
return ok;
}
atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) {
assert(failure != .release);
assert(failure != .acq_rel);
value: T; ok: bool;
#partial switch (failure) {
case .seq_cst:
assert(success != .relaxed);
#partial switch (success) {
case .seq_cst:
value, ok := intrinsics.atomic_cxchg(object, expected^, desired);
case .acquire:
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired);
case .consume:
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired);
case .release:
value, ok := intrinsics.atomic_cxchg_rel(object, expected^, desired);
case .acq_rel:
value, ok := intrinsics.atomic_cxchg_acqrel(object, expected^, desired);
}
case .relaxed:
assert(success != .release);
#partial switch (success) {
case .relaxed:
value, ok := intrinsics.atomic_cxchg_relaxed(object, expected^, desired);
case .seq_cst:
value, ok := intrinsics.atomic_cxchg_failrelaxed(object, expected^, desired);
case .acquire:
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired);
case .consume:
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired);
case .acq_rel:
value, ok := intrinsics.atomic_cxchg_acqrel_failrelaxed(object, expected^, desired);
}
case .consume:
fallthrough;
case .acquire:
assert(success == .seq_cst);
value, ok := intrinsics.atomic_cxchg_failacq(object, expected^, desired);
}
if !ok do expected^ = value;
return ok;
}
atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) {
value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired);
if !ok do expected^ = value;
return ok;
}
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) {
assert(failure != .release);
assert(failure != .acq_rel);
value: T; ok: bool;
#partial switch (failure) {
case .seq_cst:
assert(success != .relaxed);
#partial switch (success) {
case .seq_cst:
value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired);
case .acquire:
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired);
case .consume:
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired);
case .release:
value, ok := intrinsics.atomic_cxchgweak_rel(object, expected^, desired);
case .acq_rel:
value, ok := intrinsics.atomic_cxchgweak_acqrel(object, expected^, desired);
}
case .relaxed:
assert(success != .release);
#partial switch (success) {
case .relaxed:
value, ok := intrinsics.atomic_cxchgweak_relaxed(object, expected^, desired);
case .seq_cst:
value, ok := intrinsics.atomic_cxchgweak_failrelaxed(object, expected^, desired);
case .acquire:
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired);
case .consume:
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired);
case .acq_rel:
value, ok := intrinsics.atomic_cxchgweak_acqrel_failrelaxed(object, expected^, desired);
}
case .consume:
fallthrough;
case .acquire:
assert(success == .seq_cst);
value, ok := intrinsics.atomic_cxchgweak_failacq(object, expected^, desired);
}
if !ok do expected^ = value;
return ok;
}
// 7.17.7.5 The atomic_fetch and modify generic functions
atomic_fetch_add :: #force_inline proc(object: ^$T, operand: T) -> T {
return intrinsics.atomic_add(object, operand);
}
atomic_fetch_add_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_add_relaxed(object, operand);
case .consume:
return intrinsics.atomic_add_acq(object, operand);
case .acquire:
return intrinsics.atomic_add_acq(object, operand);
case .release:
return intrinsics.atomic_add_rel(object, operand);
case .acq_rel:
return intrinsics.atomic_add_acqrel(object, operand);
case .seq_cst:
return intrinsics.atomic_add(object, operand);
}
}
atomic_fetch_sub :: #force_inline proc(object: ^$T, operand: T) -> T {
return intrinsics.atomic_sub(object, operand);
}
atomic_fetch_sub_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_sub_relaxed(object, operand);
case .consume:
return intrinsics.atomic_sub_acq(object, operand);
case .acquire:
return intrinsics.atomic_sub_acq(object, operand);
case .release:
return intrinsics.atomic_sub_rel(object, operand);
case .acq_rel:
return intrinsics.atomic_sub_acqrel(object, operand);
case .seq_cst:
return intrinsics.atomic_sub(object, operand);
}
}
atomic_fetch_or :: #force_inline proc(object: ^$T, operand: T) -> T {
return intrinsics.atomic_or(object, operand);
}
atomic_fetch_or_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_or_relaxed(object, operand);
case .consume:
return intrinsics.atomic_or_acq(object, operand);
case .acquire:
return intrinsics.atomic_or_acq(object, operand);
case .release:
return intrinsics.atomic_or_rel(object, operand);
case .acq_rel:
return intrinsics.atomic_or_acqrel(object, operand);
case .seq_cst:
return intrinsics.atomic_or(object, operand);
}
}
atomic_fetch_xor :: #force_inline proc(object: ^$T, operand: T) -> T {
return intrinsics.atomic_xor(object, operand);
}
atomic_fetch_xor_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_xor_relaxed(object, operand);
case .consume:
return intrinsics.atomic_xor_acq(object, operand);
case .acquire:
return intrinsics.atomic_xor_acq(object, operand);
case .release:
return intrinsics.atomic_xor_rel(object, operand);
case .acq_rel:
return intrinsics.atomic_xor_acqrel(object, operand);
case .seq_cst:
return intrinsics.atomic_xor(object, operand);
}
}
atomic_fetch_and :: #force_inline proc(object: ^$T, operand: T) -> T {
return intrinsics.atomic_and(object, operand);
}
atomic_fetch_and_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_and_relaxed(object, operand);
case .consume:
return intrinsics.atomic_and_acq(object, operand);
case .acquire:
return intrinsics.atomic_and_acq(object, operand);
case .release:
return intrinsics.atomic_and_rel(object, operand);
case .acq_rel:
return intrinsics.atomic_and_acqrel(object, operand);
case .seq_cst:
return intrinsics.atomic_and(object, operand);
}
}
// 7.17.8 Atomic flag type and operations
atomic_flag :: distinct atomic_bool;
atomic_flag_test_and_set :: #force_inline proc(flag: ^atomic_flag) -> bool {
return bool(atomic_exchange(flag, atomic_flag(true)));
}
atomic_flag_test_and_set_explicit :: #force_inline proc(flag: ^atomic_flag, order: memory_order) -> bool {
return bool(atomic_exchange_explicit(flag, atomic_flag(true), order));
}
atomic_flag_clear :: #force_inline proc(flag: ^atomic_flag) {
atomic_store(flag, atomic_flag(false));
}
atomic_flag_clear_explicit :: #force_inline proc(flag: ^atomic_flag, order: memory_order) {
atomic_store_explicit(flag, atomic_flag(false), order);
}
+135
View File
@@ -0,0 +1,135 @@
package libc
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
// 7.21 Input/output
FILE :: struct {};
// MSVCRT compatible.
when ODIN_OS == "windows" {
_IOFBF :: 0x0000;
_IONBF :: 0x0004;
_IOLBF :: 0x0040;
BUFSIZ :: 512;
EOF :: int(-1);
FOPEN_MAX :: 20;
FILENAME_MAX :: 260;
L_tmpnam :: 15; // "\\" + 12 + NUL
SEEK_SET :: 0;
SEEK_CUR :: 1;
SEEK_END :: 2;
TMP_MAX :: 32767; // SHRT_MAX
fpos_t :: distinct i64;
@(private="file")
@(default_calling_convention="c")
foreign libc {
__acrt_iob_func :: proc (index: uint) -> ^FILE ---;
}
stdin := __acrt_iob_func(0);
stdout := __acrt_iob_func(1);
stderr := __acrt_iob_func(2);
}
// GLIBC and MUSL compatible.
when ODIN_OS == "linux" {
fpos_t :: struct #raw_union { _: [16]char, _: longlong, _: double, };
_IOFBF :: 0;
_IOLBF :: 1;
_IONBF :: 2;
BUFSIZ :: 1024;
EOF :: int(-1);
FOPEN_MAX :: 1000;
FILENAME_MAX :: 4096;
L_tmpnam :: 20;
SEEK_SET :: 0;
SEEK_CUR :: 1;
SEEK_END :: 2;
TMP_MAX :: 10000;
foreign libc {
stderr: ^FILE;
stdin: ^FILE;
stdout: ^FILE;
}
}
@(default_calling_convention="c")
foreign libc {
// 7.21.4 Operations on files
remove :: proc(filename: cstring) -> int ---;
rename :: proc(old, new: cstring) -> int ---;
tmpfile :: proc() -> ^FILE ---;
tmpnam :: proc(s: ^char) -> ^char ---;
// 7.21.5 File access functions
fclose :: proc(stream: ^FILE) -> int ---;
fflush :: proc(stream: ^FILE) -> int ---;
fopen :: proc(filename, mode: cstring) -> ^FILE ---;
freopen :: proc(filename, mode: cstring, stream: ^FILE) -> ^FILE ---;
setbuf :: proc(stream: ^FILE, buf: ^char) ---;
setvbuf :: proc(stream: ^FILE, buf: ^char, mode: int, size: size_t) -> int ---;
// 7.21.6 Formatted input/output functions
fprintf :: proc(stream: ^FILE, format: cstring, #c_vararg args: ..any) -> int ---;
fscanf :: proc(stream: ^FILE, format: cstring, #c_vararg args: ..any) -> int ---;
printf :: proc(format: cstring, #c_vararg args: ..any) -> int ---;
scanf :: proc(format: cstring, #c_vararg args: ..any) -> int ---;
snprintf :: proc(s: ^char, format: cstring, #c_vararg args: ..any) -> int ---;
sscanf :: proc(s, format: cstring, #c_vararg args: ..any) -> int ---;
vfprintf :: proc(stream: ^FILE, format: cstring, arg: ^va_list) -> int ---;
vfscanf :: proc(stream: ^FILE, format: cstring, arg: ^va_list) -> int ---;
vprintf :: proc(format: cstring, arg: ^va_list) -> int ---;
vscanf :: proc(format: cstring, arg: ^va_list) -> int ---;
vsnprintf :: proc(s: ^char, n: size_t, format: cstring, arg: ^va_list) -> int ---;
vsprintf :: proc(s: ^char, format: cstring, arg: ^va_list) -> int ---;
vsscanf :: proc(s, format: cstring, arg: ^va_list) -> int ---;
// 7.21.7 Character input/output functions
fgetc :: proc(stream: ^FILE) -> int ---;
fgets :: proc(s: ^char, n: int, stream: ^FILE) -> ^char ---;
fputc :: proc(s: cstring, stream: ^FILE) -> int ---;
getc :: proc(stream: ^FILE) -> int ---;
getchar :: proc() -> int ---;
putc :: proc(c: int, stream: ^FILE) -> int ---;
putchar :: proc() -> int ---;
puts :: proc(s: cstring) -> int ---;
ungetc :: proc(c: int, stream: ^FILE) -> int ---;
fread :: proc(ptr: rawptr, size: size_t, stream: ^FILE) -> size_t ---;
fwrite :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---;
// 7.21.9 File positioning functions
fgetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---;
fseek :: proc(stream: ^FILE, offset: long, whence: int) -> int ---;
fsetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---;
ftell :: proc(stream: ^FILE) -> long ---;
rewind :: proc(stream: ^FILE) ---;
// 7.21.10 Error-handling functions
clearerr :: proc(stream: ^FILE) ---;
feof :: proc(stream: ^FILE) -> int ---;
ferror :: proc(stream: ^FILE) -> int ---;
perror :: proc(s: cstring) ---;
}
+109
View File
@@ -0,0 +1,109 @@
package libc
// 7.22 General utilities
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
when ODIN_OS == "windows" {
RAND_MAX :: 0x7fff;
@(private="file")
@(default_calling_convention="c")
foreign libc {
___mb_cur_max_func :: proc() -> int ---;
}
MB_CUR_MAX :: #force_inline proc() -> size_t {
return size_t(___mb_cur_max_func());
}
}
when ODIN_OS == "linux" {
RAND_MAX :: 0x7fffffff;
// GLIBC and MUSL only
@(private="file")
@(default_calling_convention="c")
foreign libc {
__ctype_get_mb_cur_max :: proc() -> size_t ---;
}
MB_CUR_MAX :: #force_inline proc() -> size_t {
return __ctype_get_mb_cur_max();
}
}
// C does not declare what these values should be, as an implementation is free
// to use any two distinct values it wants to indicate success or failure.
// However, nobody actually does and everyone appears to have agreed upon these
// values.
EXIT_SUCCESS :: 0;
EXIT_FAILURE :: 1;
// C does not declare which order 'quot' and 'rem' should be for the divide
// structures. An implementation could put 'rem' first. However, nobody actually
// does and everyone appears to have agreed upon this layout.
div_t :: struct { quot, rem: int, }
ldiv_t :: struct { quot, rem: long, }
lldiv_t :: struct { quot, rem: longlong, }
@(default_calling_convention="c")
foreign libc {
// 7.22.1 Numeric conversion functions
atof :: proc(nptr: cstring) -> double ---;
atoi :: proc(nptr: cstring) -> int ---;
atol :: proc(nptr: cstring) -> long ---;
atoll :: proc(nptr: cstring) -> longlong ---;
strtod :: proc(nptr: cstring, endptr: ^^char) -> double ---;
strtof :: proc(nptr: cstring, endptr: ^^char) -> float ---;
strtol :: proc(nptr: cstring, endptr: ^^char, base: int) -> long ---;
strtoll :: proc(nptr: cstring, endptr: ^^char, base: int) -> longlong ---;
strtoul :: proc(nptr: cstring, endptr: ^^char, base: int) -> ulong ---;
strtoull :: proc(nptr: cstring, endptr: ^^char, base: int) -> ulonglong ---;
// 7.22.2 Pseudo-random sequence generation functions
rand :: proc() -> int ---;
srand :: proc(seed: uint) ---;
// 7.22.3 Memory management functions
aligned_alloc :: proc(aligment, size: size_t) -> rawptr ---;
calloc :: proc(nmemb, size: size_t) -> rawptr ---;
free :: proc(ptr: rawptr) ---;
malloc :: proc(size: size_t) -> rawptr ---;
realloc :: proc(ptr: rawptr, size: size_t) -> rawptr ---;
// 7.22.4 Communication with the environment
abort :: proc() -> ! ---;
atexit :: proc(func: proc "c" ()) -> int ---;
at_quick_exit :: proc(func: proc "c" ()) -> int ---;
exit :: proc(status: int) -> ! ---;
_Exit :: proc(status: int) -> ! ---;
getenv :: proc(name: cstring) -> ^char ---;
quick_exit :: proc(status: int) -> ! ---;
system :: proc(cmd: cstring) -> int ---;
// 7.22.5 Searching and sorting utilities
bsearch :: proc(key, base: rawptr, nmemb, size: size_t, compar: proc "c" (lhs, rhs: rawptr) -> int) -> rawptr ---;
qsort :: proc(base: rawptr, nmemb, size: size_t, compar: proc "c" (lhs, rhs: rawptr) -> int) ---;
// 7.22.6 Integer arithmetic functions
abs :: proc(j: int) -> int ---;
labs :: proc(j: long) -> long ---;
llabs :: proc(j: longlong) -> longlong ---;
div :: proc(numer, denom: int) -> div_t ---;
ldiv :: proc(numer, denom: long) -> ldiv_t ---;
lldiv :: proc(numer, denom: longlong) -> lldiv_t ---;
// 7.22.7 Multibyte/wide character conversion functions
mblen :: proc(s: cstring, n: size_t) -> int ---;
mbtowc :: proc(pwc: ^wchar_t, s: cstring, n: size_t) -> int ---;
wctomb :: proc(s: ^char, wc: wchar_t) -> int ---;
// 7.22.8 Multibyte/wide string conversion functions
mbstowcs :: proc(pwcs: ^wchar_t, s: cstring, n: size_t) -> size_t ---;
wcstombs :: proc(s: ^char, pwcs: ^wchar_t, n: size_t) -> size_t ---;
}
+43
View File
@@ -0,0 +1,43 @@
package libc
// 7.24 String handling
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
foreign libc {
// 7.24.2 Copying functions
memcpy :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---;
memmove :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---;
strcpy :: proc(s1: ^char, s2: cstring) -> ^char ---;
strncpy :: proc(s1: ^char, s2: cstring, n: size_t) -> ^char ---;
// 7.24.3 Concatenation functions
strcat :: proc(s1: ^char, s2: cstring) -> ^char ---;
strncat :: proc(s1: ^char, s2: cstring, n: size_t) -> ^char ---;
// 7.24.4 Comparison functions
memcmp :: proc(s1, s2: rawptr, n: size_t) -> int ---;
strcmp :: proc(s1, s2: cstring) -> int ---;
strcoll :: proc(s1, s2: cstring) -> int ---;
strncmp :: proc(s1, s2: cstring, n: size_t) -> int ---;
strxfrm :: proc(s1: ^char, s2: cstring, n: size_t) -> size_t ---;
// 7.24.5 Search functions
memchr :: proc(s: rawptr, c: int, n: size_t) -> rawptr ---;
strchr :: proc(s: cstring, c: int) -> ^char ---;
strcspn :: proc(s1, s2: cstring) -> size_t ---;
strpbrk :: proc(s1, s2: cstring) -> ^char ---;
strrchr :: proc(s: ^char, c: int) -> ^char ---;
strcpn :: proc(s1, s2: cstring) -> ^char ---;
strtok :: proc(s1: ^char, s2: cstring) -> ^char ---;
// 7.24.6 Miscellaneous functions
memset :: proc(s: rawptr, c: int, n: size_t) -> rawptr ---;
strerror :: proc(errnum: int) -> ^char ---;
strlen :: proc(s: cstring) -> size_t ---;
}
+50
View File
@@ -0,0 +1,50 @@
package libc_tests
import "core:c/libc"
test_stdio :: proc() {
c: libc.char = 'C';
libc.puts("Hello from puts");
libc.printf("Hello from printf in %c\n", c);
}
test_thread :: proc() {
thread_proc :: proc "c" (rawptr) -> libc.int {
libc.printf("Hello from thread");
return 42;
}
thread: libc.thrd_t;
libc.thrd_create(&thread, thread_proc, nil);
result: libc.int;
libc.thrd_join(thread, &result);
libc.printf(" %d\n", result);
}
jmp: libc.jmp_buf;
test_sjlj :: proc() {
if libc.setjmp(&jmp) != 0 {
libc.printf("Hello from longjmp\n");
return;
}
libc.printf("Hello from setjmp\n");
libc.longjmp(&jmp, 1);
}
test_signal :: proc() {
handler :: proc "c" (sig: libc.int) {
libc.printf("Hello from signal handler\n");
}
libc.signal(libc.SIGABRT, handler);
libc.raise(libc.SIGABRT);
}
test_atexit :: proc() {
handler :: proc "c" () {
libc.printf("Hello from atexit\n");
}
libc.atexit(handler);
}
main :: proc() {
test_stdio();
test_thread();
test_sjlj();
test_signal();
test_atexit();
}
+138
View File
@@ -0,0 +1,138 @@
package libc
// 7.26 Threads
thrd_start_t :: proc "c" (rawptr) -> int;
tss_dtor_t :: proc "c" (rawptr);
when ODIN_OS == "windows" {
foreign import libc {
"system:libucrt.lib",
"system:msvcprt.lib"
}
thrd_success :: 0; // _Thrd_success
thrd_nomem :: 1; // _Thrd_nomem
thrd_timedout :: 2; // _Thrd_timedout
thrd_busy :: 3; // _Thrd_busy
thrd_error :: 4; // _Thrd_error
mtx_plain :: 1; // _Mtx_plain
mtx_recursive :: 0x100; // _Mtx_recursive
mtx_timed :: 4; // _Mtx_timed
TSS_DTOR_ITERATIONS :: 4; // _TSS_DTOR_ITERATIONS_IMP
once_flag :: distinct i8; // _Once_flag_imp_t
thrd_t :: struct { _: rawptr, _: uint, } // _Thrd_t
tss_t :: distinct int; // _Tss_imp_t
cnd_t :: distinct rawptr; // _Cnd_imp_t
mtx_t :: distinct rawptr; // _Mtx_imp_t
// MSVCRT does not expose the C11 symbol names as what they are in C11
// because they held off implementing <threads.h> and C11 support for so
// long that people started implementing their own. To prevent symbol
// conflict with existing customers code they had to namespace them
// differently. Thus we need to alias the correct symbol names with Odin's
// link_name attribute.
@(default_calling_convention="c")
foreign libc {
// 7.26.2 Initialization functions
@(link_name="_Call_once") call_once :: proc(flag: ^once_flag, func: proc "c" ()) ---;
// 7.26.3 Condition variable functions
@(link_name="_Cnd_broadcast") cnd_broadcast :: proc(cond: ^cnd_t) -> int ---;
@(link_name="_Cnd_destroy") cnd_destroy :: proc(cond: ^cnd_t) ---;
@(link_name="_Cnd_init") cnd_init :: proc(cond: ^cnd_t) -> int ---;
@(link_name="_Cnd_signal") cnd_signal :: proc(cond: ^cnd_t) -> int ---;
@(link_name="_Cnd_timedwait") cnd_timedwait :: proc(cond: ^cnd_t, ts: ^timespec) -> int ---;
@(link_name="_Cnd_wait") cnd_wait :: proc(cond: ^cnd_t, mtx: ^mtx_t) -> int ---;
// 7.26.4 Mutex functions
@(link_name="_Mtx_destroy") mtx_destroy :: proc(mtx: ^mtx_t) ---;
@(link_name="_Mtx_init") mtx_init :: proc(mtx: ^mtx_t, type: int) -> int ---;
@(link_name="_Mtx_lock") mtx_lock :: proc(mtx: ^mtx_t) -> int ---;
@(link_name="_Mtx_timedlock") mtx_timedlock :: proc(mtx: ^mtx_t, ts: ^timespec) -> int ---;
@(link_name="_Mtx_trylock") mtx_trylock :: proc(mtx: ^mtx_t) -> int ---;
@(link_name="_Mtx_unlock") mtx_unlock :: proc(mtx: ^mtx_t) -> int ---;
// 7.26.5 Thread functions
@(link_name="_Thrd_create") thrd_create :: proc(thr: ^thrd_t, func: thrd_start_t, arg: rawptr) -> int ---;
@(link_name="_Thrd_current") thrd_current :: proc() -> thrd_t ---;
@(link_name="_Thrd_detach") thrd_detach :: proc(thr: thrd_t) -> int ---;
@(link_name="_Thrd_equal") thrd_equal :: proc(lhs, rhs: thrd_t) -> int ---;
@(link_name="_Thrd_exit") thrd_exit :: proc(res: int) -> ! ---;
@(link_name="_Thrd_join") thrd_join :: proc(thr: thrd_t, res: ^int) -> int ---;
@(link_name="_Thrd_sleep") thrd_sleep :: proc(duration, remaining: ^timespec) -> int ---;
@(link_name="_Thrd_yield") thrd_yield :: proc() ---;
// 7.26.6 Thread-specific storage functions
@(link_name="_Tss_create") tss_create :: proc(key: ^tss_t, dtor: tss_dtor_t) -> int ---;
@(link_name="_Tss_delete") tss_delete :: proc(key: tss_t) ---;
@(link_name="_Tss_get") tss_get :: proc(key: tss_t) -> rawptr ---;
@(link_name="_Tss_set") tss_set :: proc(key: tss_t, val: rawptr) -> int ---;
}
}
// GLIBC and MUSL compatible constants and types.
when ODIN_OS == "linux" {
foreign import libc {
"system:c",
"system:pthread"
}
thrd_success :: 0;
thrd_busy :: 1;
thrd_error :: 2;
thrd_nomem :: 3;
thrd_timedout :: 4;
mtx_plain :: 0;
mtx_recursive :: 1;
mtx_timed :: 2;
TSS_DTOR_ITERATIONS :: 4;
once_flag :: distinct int;
thrd_t :: distinct ulong;
tss_t :: distinct uint;
cnd_t :: struct #raw_union { _: [12]int, _: [12 * size_of(int) / size_of(rawptr)]rawptr, };
mtx_t :: struct #raw_union { _: [10 when size_of(long) == 8 else 6]int, _: [5 when size_of(long) == 8 else 6]rawptr, };
@(default_calling_convention="c")
foreign libc {
// 7.26.2 Initialization functions
call_once :: proc(flag: ^once_flag, func: proc "c" ()) ---;
// 7.26.3 Condition variable functions
cnd_broadcast :: proc(cond: ^cnd_t) -> int ---;
cnd_destroy :: proc(cond: ^cnd_t) ---;
cnd_init :: proc(cond: ^cnd_t) -> int ---;
cnd_signal :: proc(cond: ^cnd_t) -> int ---;
cnd_timedwait :: proc(cond: ^cnd_t, ts: ^timespec) -> int ---;
cnd_wait :: proc(cond: ^cnd_t, mtx: ^mtx_t) -> int ---;
// 7.26.4 Mutex functions
mtx_destroy :: proc(mtx: ^mtx_t) ---;
mtx_init :: proc(mtx: ^mtx_t, type: int) -> int ---;
mtx_lock :: proc(mtx: ^mtx_t) -> int ---;
mtx_timedlock :: proc(mtx: ^mtx_t, ts: ^timespec) -> int ---;
mtx_trylock :: proc(mtx: ^mtx_t) -> int ---;
mtx_unlock :: proc(mtx: ^mtx_t) -> int ---;
// 7.26.5 Thread functions
thrd_create :: proc(thr: ^thrd_t, func: thrd_start_t, arg: rawptr) -> int ---;
thrd_current :: proc() -> thrd_t ---;
thrd_detach :: proc(thr: thrd_t) -> int ---;
thrd_equal :: proc(lhs, rhs: thrd_t) -> int ---;
thrd_exit :: proc(res: int) -> ! ---;
thrd_join :: proc(thr: thrd_t, res: ^int) -> int ---;
thrd_sleep :: proc(duration, remaining: ^timespec) -> int ---;
thrd_yield :: proc() ---;
// 7.26.6 Thread-specific storage functions
tss_create :: proc(key: ^tss_t, dtor: tss_dtor_t) -> int ---;
tss_delete :: proc(key: tss_t) ---;
tss_get :: proc(key: tss_t) -> rawptr ---;
tss_set :: proc(key: tss_t, val: rawptr) -> int ---;
}
}
+81
View File
@@ -0,0 +1,81 @@
package libc
// 7.27 Date and time
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
// We enforce 64-bit time_t and timespec as there is no reason to use 32-bit as
// we approach the 2038 problem. Windows has defaulted to this since VC8 (2005).
when ODIN_OS == "windows" {
foreign libc {
// 7.27.2 Time manipulation functions
clock :: proc() -> clock_t ---;
@(link_name="_difftime64") difftime :: proc(time1, time2: time_t) -> double ---;
mktime :: proc(timeptr: ^tm) -> time_t ---;
@(link_name="_time64") time :: proc(timer: ^time_t) -> time_t ---;
@(link_name="_timespec64_get") timespec_get :: proc(ts: ^timespec, base: int) -> int ---;
// 7.27.3 Time conversion functions
asctime :: proc(timeptr: ^tm) -> ^char ---;
@(link_name="_ctime64") ctime :: proc(timer: ^time_t) -> ^char ---;
@(link_name="_gmtime64") gmtime :: proc(timer: ^time_t) -> ^tm ---;
@(link_name="_localtime64") localtime :: proc(timer: ^time_t) -> ^tm ---;
strftime :: proc(s: ^char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---;
}
CLOCKS_PER_SEC :: 1000;
TIME_UTC :: 1;
clock_t :: distinct long;
time_t :: distinct i64;
timespec :: struct #align 8 {
tv_sec: time_t,
tv_nsec: long,
}
tm :: struct #align 8 {
tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst: int,
}
}
when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
@(default_calling_convention="c")
foreign libc {
// 7.27.2 Time manipulation functions
clock :: proc() -> clock_t ---;
difftime :: proc(time1, time2: time_t) -> double ---;
mktime :: proc(timeptr: ^tm) -> time_t ---;
time :: proc(timer: ^time_t) -> time_t ---;
timespec_get :: proc(ts: ^timespec, base: int) -> int ---;
// 7.27.3 Time conversion functions
asctime :: proc(timeptr: ^tm) -> ^char ---;
ctime :: proc(timer: ^time_t) -> ^char ---;
gmtime :: proc(timer: ^time_t) -> ^tm ---;
localtime :: proc(timer: ^time_t) -> ^tm ---;
strftime :: proc(s: ^char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---;
}
CLOCKS_PER_SEC :: 1000000;
TIME_UTC :: 1;
time_t :: distinct i64;
clock_t :: long;
timespec :: struct {
tv_sec: time_t,
tv_nsec: long,
}
tm :: struct {
tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst: int,
_: long,
_: rawptr,
}
}
+82
View File
@@ -0,0 +1,82 @@
package libc
import builtin "core:builtin"
char :: builtin.u8; // assuming -funsigned-char
short :: builtin.i16;
int :: builtin.i32;
long :: builtin.i32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.i64;
longlong :: builtin.i64;
uchar :: builtin.u8;
ushort :: builtin.u16;
uint :: builtin.u32;
ulong :: builtin.u32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.u64;
ulonglong :: builtin.u64;
bool :: distinct builtin.b8;
size_t :: builtin.uint;
wchar_t :: builtin.u16 when (ODIN_OS == "windows") else builtin.u32;
float :: builtin.f32;
double :: builtin.f64;
// 7.20.1 Integer types
int8_t :: builtin.i8;
uint8_t :: builtin.u8;
int16_t :: builtin.i16;
uint16_t :: builtin.u16;
int32_t :: builtin.i32;
uint32_t :: builtin.u32;
int64_t :: builtin.i64;
uint64_t :: builtin.u64;
// These are all the same in multiple libc's for multiple architectures.
int_least8_t :: builtin.i8;
uint_least8_t :: builtin.u8;
int_least16_t :: builtin.i16;
uint_least16_t :: builtin.u16;
int_least32_t :: builtin.i32;
uint_least32_t :: builtin.u32;
int_least64_t :: builtin.i64;
uint_least64_t :: builtin.u64;
// Same on Windows, Linux, and FreeBSD
when ODIN_ARCH == "386" || ODIN_ARCH == "amd64" {
int_fast8_t :: builtin.i8;
uint_fast8_t :: builtin.u8;
int_fast16_t :: builtin.i32;
uint_fast16_t :: builtin.u32;
int_fast32_t :: builtin.i32;
uint_fast32_t :: builtin.u32;
int_fast64_t :: builtin.i64;
uint_fast64_t :: builtin.u64;
}
intptr_t :: builtin.int;
uintptr_t :: builtin.uintptr;
ptrdiff_t :: distinct intptr_t;
intmax_t :: builtin.i64;
uintmax_t :: builtin.u64;
// Copy C's rules for type promotion here by forcing the type on the literals.
INT8_MAX :: int(0x7f);
INT16_MAX :: int(0x7fff);
INT32_MAX :: int(0x7fffffff);
INT64_MAX :: longlong(0x7fffffffffffffff);
UINT8_MAX :: int(0xff);
UINT16_MAX :: int(0xffff);
UINT32_MAX :: uint(0xffffffff);
UINT64_MAX :: ulonglong(0xffffffffffffffff);
INT8_MIN :: ~INT8_MAX;
INT16_MIN :: ~INT16_MAX;
INT32_MIN :: ~INT32_MAX;
INT64_MIN :: ~INT64_MAX;
NULL :: rawptr(uintptr(0));
NDEBUG :: !ODIN_DEBUG;
+21
View File
@@ -0,0 +1,21 @@
package libc
// 7.28 Unicode utilities
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
@(default_calling_convention="c")
foreign libc {
// 7.28.1 Restartable multibyte/wide character conversion functions
mbrtoc16 :: proc(pc16: ^char16_t, s: cstring, n: size_t, ps: ^mbstate_t) -> size_t ---;
c16rtomb :: proc(s: ^char, c16: char16_t, ps: ^mbstate_t) -> size_t ---;
mbrtoc32 :: proc(pc32: ^char32_t, s: cstring, n: size_t, ps: ^mbstate_t) -> size_t ---;
c32rtomb :: proc(s: ^char, c32: char32_t, ps: ^mbstate_t) -> size_t ---;
}
char16_t :: uint_least16_t;
char32_t :: uint_least32_t;
+108
View File
@@ -0,0 +1,108 @@
package libc
// 7.29 Extended multibyte and wide character utilities
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
@(default_calling_convention="c")
foreign libc {
// 7.29.2 Formatted wide character input/output functions
fwprintf :: proc(stream: ^FILE, format: ^wchar_t, #c_vararg arg: ..any) -> int ---;
fwscanf :: proc(stream: ^FILE, format: ^wchar_t, #c_vararg arg: ..any) -> int ---;
swprintf :: proc(stream: ^FILE, n: size_t, format: ^wchar_t, #c_vararg arg: ..any) -> int ---;
swscanf :: proc(s, format: ^wchar_t, #c_vararg arg: ..any) -> int ---;
vfwprintf :: proc(stream: ^FILE, format: ^wchar_t, arg: va_list) -> int ---;
vfwscanf :: proc(stream: ^FILE, format: ^wchar_t, arg: va_list) -> int ---;
vswprintf :: proc(s: ^wchar_t, n: size_t, format: ^wchar_t, arg: va_list) -> int ---;
vswscanf :: proc(s, format: ^wchar_t, arg: va_list) -> int ---;
vwprintf :: proc(format: ^wchar_t, arg: va_list) -> int ---;
vwscanf :: proc(format: ^wchar_t, arg: va_list) -> int ---;
wprintf :: proc(format: ^wchar_t, #c_vararg arg: ..any) -> int ---;
wscanf :: proc(format: ^wchar_t, #c_vararg arg: ..any) -> int ---;
// 7.29.3 Wide character input/output functions
fwgetc :: proc(stream: ^FILE) -> wint_t ---;
fgetws :: proc(s: ^wchar_t, n: int, stream: ^FILE) -> wchar_t ---;
fputwc :: proc(c: wchar_t, stream: ^FILE) -> wint_t ---;
fputws :: proc(s: ^wchar_t, stream: ^FILE) -> int ---;
fwide :: proc(stream: ^FILE, mode: int) -> int ---;
getwc :: proc(stream: ^FILE) -> wint_t ---;
getwchar :: proc() -> wint_t ---;
putwc :: proc(c: wchar_t, stream: ^FILE) -> wint_t ---;
putwchar :: proc(c: wchar_t) -> wint_t ---;
ungetwc :: proc(c: wchar_t, stream: ^FILE) -> wint_t ---;
// 7.29.4 General wide string utilities
wcstod :: proc(nptr: ^wchar_t, endptr: ^^wchar_t) -> double ---;
wcstof :: proc(nptr: ^wchar_t, endptr: ^^wchar_t) -> float ---;
wcstol :: proc(nptr: ^wchar_t, endptr: ^^wchar_t, base: int) -> long ---;
wcstoll :: proc(nptr: ^wchar_t, endptr: ^^wchar_t, base: int) -> longlong ---;
wcstoul :: proc(nptr: ^wchar_t, endptr: ^^wchar_t, base: int) -> ulong ---;
wcstoull :: proc(nptr: ^wchar_t, endptr: ^^wchar_t, base: int) -> ulonglong ---;
// 7.29.4.2 Wide string copying functions
wcscpy :: proc(s1, s2: ^wchar_t) -> ^wchar_t ---;
wcsncpy :: proc(s1, s2: ^wchar_t, n: size_t) -> ^wchar_t ---;
wmemcpy :: proc(s1, s2: ^wchar_t, n: size_t) -> ^wchar_t ---;
wmemmove :: proc(s1, s2: ^wchar_t, n: size_t) -> ^wchar_t ---;
// 7.29.4.3 Wide string concatenation functions
wcscat :: proc(s1, s2: ^wchar_t) -> ^wchar_t ---;
wcsncat :: proc(s1, s2: ^wchar_t, n: size_t) -> ^wchar_t ---;
// 7.29.4.4 Wide string comparison functions
wcscmp :: proc(s1, s2: ^wchar_t) -> int ---;
wcscoll :: proc(s1, s2: ^wchar_t) -> int ---;
wcsncmp :: proc(s1, s2: ^wchar_t, n: size_t) -> int ---;
wcsxfrm :: proc(s1, s2: ^wchar_t, n: size_t) -> size_t ---;
wmemcmp :: proc(s1, s2: ^wchar_t, n: size_t) -> int ---;
// 7.29.4.5 Wide string search functions
wcschr :: proc(s: ^wchar_t, c: wchar_t) -> ^wchar_t ---;
wcscspn :: proc(s1, s2: ^wchar_t) -> size_t ---;
wcspbrk :: proc(s1, s2: ^wchar_t) -> ^wchar_t ---;
wcsrchr :: proc(s: ^wchar_t, c: wchar_t) -> ^wchar_t ---;
wcsspn :: proc(s1, s2: ^wchar_t) -> size_t ---;
wcsstr :: proc(s1, s2: ^wchar_t) -> ^wchar_t ---;
wcstok :: proc(s1, s2: ^wchar_t, ptr: ^^wchar_t) -> ^wchar_t ---;
wmemchr :: proc(s: ^wchar_t, c: wchar_t, n: size_t) -> ^wchar_t ---;
// 7.29.4.6 Miscellaneous functions
wcslen :: proc(s: ^wchar_t) -> size_t ---;
wmemset :: proc(s: ^wchar_t, c: wchar_t, n: size_t) -> ^wchar_t ---;
// 7.29.5 Wide character time conversion functions
wcsftime :: proc(s: ^wchar_t, maxsize: size_t, format: ^wchar_t, timeptr: ^tm) -> size_t ---;
// 7.29.6.1 Single-byte/wide character conversion functions
btowc :: proc(c: int) -> wint_t ---;
wctob :: proc(c: wint_t) -> int ---;
// 7.29.6.2 Conversion state functions
mbsinit :: proc(ps: ^mbstate_t) -> int ---;
// 7.29.6.3 Restartable multibyte/wide character conversion functions
mbrlen :: proc(s: cstring, n: size_t, ps: ^mbstate_t) -> size_t ---;
mbrtowc :: proc(pwc: ^wchar_t, s: cstring, n: size_t, ps: ^mbstate_t) -> size_t ---;
wcrtomb :: proc(s: ^char, wc: wchar_t, ps: ^mbstate_t) -> size_t ---;
// 7.29.6.4 Restartable multibyte/wide string conversion functions
mbsrtowcs :: proc(dst: ^wchar_t, src: ^cstring, len: size_t, ps: ^mbstate_t) -> size_t ---;
wcsrtombs :: proc(dst: ^char, src: ^^wchar_t, len: size_t, ps: ^mbstate_t) -> size_t ---;
}
// Large enough and aligned enough for any wide-spread in-use libc.
mbstate_t :: struct #align 16 { _: [32]char, }
// Odin does not have default argument promotion so the need for a separate type
// here isn't necessary, though make it distinct just to be safe.
wint_t :: distinct wchar_t;
// Calculate these values correctly regardless of what type wchar_t actually is.
WINT_MIN :: 0;
WINT_MAX :: 1 << (size_of(wint_t) * 8);
WEOF :: ~wint_t(0);
+48
View File
@@ -0,0 +1,48 @@
package libc
// 7.30 Wide character classification and mapping utilities
when ODIN_OS == "windows" {
foreign import libc "system:libucrt.lib"
} else {
foreign import libc "system:c"
}
when ODIN_OS == "windows" {
wctrans_t :: distinct wchar_t;
wctype_t :: distinct ushort;
}
when ODIN_OS == "linux" {
wctrans_t :: distinct rawptr;
wctype_t :: distinct ulong;
}
@(default_calling_convention="c")
foreign libc {
// 7.30.2.1 Wide character classification functions
iswalnum :: proc(wc: wint_t) -> int ---;
iswalpha :: proc(wc: wint_t) -> int ---;
iswblank :: proc(wc: wint_t) -> int ---;
iswcntrl :: proc(wc: wint_t) -> int ---;
iswdigit :: proc(wc: wint_t) -> int ---;
iswgraph :: proc(wc: wint_t) -> int ---;
iswlower :: proc(wc: wint_t) -> int ---;
iswprint :: proc(wc: wint_t) -> int ---;
iswpunct :: proc(wc: wint_t) -> int ---;
iswspace :: proc(wc: wint_t) -> int ---;
iswupper :: proc(wc: wint_t) -> int ---;
iswxdigit :: proc(wc: wint_t) -> int ---;
// 7.30.2.2 Extensible wide character classification functions
iswctype :: proc(wc: wint_t, desc: wctype_t) -> int ---;
wctype :: proc(property: cstring) -> wctype_t ---;
// 7.30.3 Wide character case mapping utilities
towlower :: proc(wc: wint_t) -> wint_t ---;
towupper :: proc(wc: wint_t) -> wint_t ---;
// 7.30.3.2 Extensible wide character case mapping functions
towctrans :: proc(wc: wint_t, desc: wctrans_t) -> wint_t ---;
wctrans :: proc(property: cstring) -> wctrans_t ---;
}
+19 -58
View File
@@ -442,24 +442,22 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
return E_General.Unknown_Compression_Method;
}
cinfo := (cmf >> 4) & 0xf;
if cinfo > 7 {
if cinfo := (cmf >> 4) & 0xf; cinfo > 7 {
return E_ZLIB.Unsupported_Window_Size;
}
flg, _ := compress.read_u8(ctx);
fcheck := flg & 0x1f;
fcheck := flg & 0x1f;
fcheck_computed := (cmf << 8 | flg) & 0x1f;
if fcheck != fcheck_computed {
return E_General.Checksum_Failed;
}
fdict := (flg >> 5) & 1;
/*
We don't handle built-in dictionaries for now.
They're application specific and PNG doesn't use them.
*/
if fdict != 0 {
if fdict := (flg >> 5) & 1; fdict != 0 {
return E_ZLIB.FDICT_Unsupported;
}
@@ -473,10 +471,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
}
// Parse ZLIB stream without header.
err = inflate_raw(z=ctx, expected_output_size=expected_output_size);
if err != nil {
return err;
}
inflate_raw(z=ctx, expected_output_size=expected_output_size) or_return;
if !raw {
compress.discard_to_next_byte_lsb(ctx);
@@ -527,23 +522,14 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
z_repeat: ^Huffman_Table;
z_offset: ^Huffman_Table;
codelength_ht: ^Huffman_Table;
z_repeat, err = allocate_huffman_table(allocator=context.allocator);
if err != nil {
return err;
}
z_offset, err = allocate_huffman_table(allocator=context.allocator);
if err != nil {
return err;
}
codelength_ht, err = allocate_huffman_table(allocator=context.allocator);
if err != nil {
return err;
}
defer free(z_repeat);
defer free(z_offset);
defer free(codelength_ht);
z_repeat = allocate_huffman_table(allocator=context.allocator) or_return;
z_offset = allocate_huffman_table(allocator=context.allocator) or_return;
codelength_ht = allocate_huffman_table(allocator=context.allocator) or_return;
final := u32(0);
type := u32(0);
@@ -560,8 +546,8 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
// Discard bits until next byte boundary
compress.discard_to_next_byte_lsb(z);
uncompressed_len := i16(compress.read_bits_lsb(z, 16));
length_check := i16(compress.read_bits_lsb(z, 16));
uncompressed_len := i16(compress.read_bits_lsb(z, 16));
length_check := i16(compress.read_bits_lsb(z, 16));
// fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check);
@@ -586,14 +572,8 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
if type == 1 {
// Use fixed code lengths.
err = build_huffman(z_repeat, Z_FIXED_LENGTH[:]);
if err != nil {
return err;
}
err = build_huffman(z_offset, Z_FIXED_DIST[:]);
if err != nil {
return err;
}
build_huffman(z_repeat, Z_FIXED_LENGTH[:]) or_return;
build_huffman(z_offset, Z_FIXED_DIST[:]) or_return;
} else {
lencodes: [286+32+137]u8;
codelength_sizes: [19]u8;
@@ -611,19 +591,13 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
s := compress.read_bits_lsb(z, 3);
codelength_sizes[Z_LENGTH_DEZIGZAG[i]] = u8(s);
}
err = build_huffman(codelength_ht, codelength_sizes[:]);
if err != nil {
return err;
}
build_huffman(codelength_ht, codelength_sizes[:]) or_return;
n = 0;
c: u16;
for n < ntot {
c, err = decode_huffman(z, codelength_ht);
if err != nil {
return err;
}
c = decode_huffman(z, codelength_ht) or_return;
if c < 0 || c >= 19 {
return E_Deflate.Huffman_Bad_Code_Lengths;
@@ -664,21 +638,10 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
return E_Deflate.Huffman_Bad_Code_Lengths;
}
err = build_huffman(z_repeat, lencodes[:hlit]);
if err != nil {
return err;
}
err = build_huffman(z_offset, lencodes[hlit:ntot]);
if err != nil {
return err;
}
}
err = parse_huffman_block(z, z_repeat, z_offset);
// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
if err != nil {
return err;
build_huffman(z_repeat, lencodes[:hlit]) or_return;
build_huffman(z_offset, lencodes[hlit:ntot]) or_return;
}
parse_huffman_block(z, z_repeat, z_offset) or_return;
}
if final == 1 {
break;
@@ -698,9 +661,7 @@ inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, e
ctx.input_data = input;
ctx.output = buf;
err = inflate_from_context(ctx=&ctx, raw=raw, expected_output_size=expected_output_size);
return err;
return inflate_from_context(ctx=&ctx, raw=raw, expected_output_size=expected_output_size);
}
inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) {
@@ -712,4 +673,4 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals
return inflate_raw(z=&ctx, expected_output_size=expected_output_size);
}
inflate :: proc{inflate_from_context, inflate_from_byte_array};
inflate :: proc{inflate_from_context, inflate_from_byte_array};
+1 -1
View File
@@ -1,6 +1,6 @@
package container
import "intrinsics"
import "core:intrinsics"
_ :: intrinsics;
-852
View File
@@ -1,852 +0,0 @@
package cel;
import "core:fmt"
import "core:strconv"
import "core:unicode/utf8"
import "core:strings"
Array :: []Value;
Dict :: map[string]Value;
Nil_Value :: struct{};
Value :: union {
Nil_Value,
bool, i64, f64, string,
Array, Dict,
}
Parser :: struct {
tokens: [dynamic]Token,
prev_token: Token,
curr_token: Token,
curr_token_index: int,
allocated_strings: [dynamic]string,
error_count: int,
root: Dict,
dict_stack: [dynamic]^Dict, // NOTE: Pointers may be stored on the stack
}
print_value :: proc(value: Value, pretty := true, indent := 0) {
print_indent :: proc(indent: int) {
for _ in 0..<indent {
fmt.print("\t");
}
}
switch v in value {
case bool: fmt.print(v);
case i64: fmt.print(v);
case f64: fmt.print(v);
case string: fmt.print(v);
case Array:
fmt.print("[");
if pretty { fmt.println(); }
for e, i in v {
if pretty {
print_indent(indent+1);
print_value(e, pretty, indent+1);
fmt.println(",");
} else {
if i > 0 { fmt.print(", "); }
print_value(e);
}
}
if pretty { print_indent(indent); }
fmt.print("]");
case Dict:
fmt.print("{");
if pretty { fmt.println(); }
i := 0;
for name, val in v {
if pretty {
print_indent(indent+1);
fmt.printf("%s = ", name);
print_value(val, pretty, indent+1);
fmt.println(",");
} else {
if i > 0 { fmt.print(", "); }
fmt.printf("%s = ", name);
print_value(val, pretty, indent+1);
i += 1;
}
}
if pretty { print_indent(indent); }
fmt.print("}");
case:
fmt.print("nil");
case Nil_Value:
fmt.print("nil");
}
}
print :: proc(p: ^Parser, pretty := false) {
for name, val in p.root {
fmt.printf("%s = ", name);
print_value(val, pretty);
fmt.println(";");
}
}
create_from_string :: proc(src: string) -> (^Parser, bool) {
return init(transmute([]byte)src);
}
init :: proc(src: []byte) -> (^Parser, bool) {
t: Tokenizer;
tokenizer_init(&t, src);
return create_from_tokenizer(&t);
}
create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
p := new(Parser);
for {
tok := scan(t);
if tok.kind == .Illegal {
return p, false;
}
append(&p.tokens, tok);
if tok.kind == .EOF {
break;
}
}
if t.error_count > 0 {
return p, false;
}
if len(p.tokens) == 0 {
tok := Token{kind = .EOF};
tok.line, tok.column = 1, 1;
append(&p.tokens, tok);
return p, true;
}
p.curr_token_index = 0;
p.prev_token = p.tokens[p.curr_token_index];
p.curr_token = p.tokens[p.curr_token_index];
p.root = Dict{};
p.dict_stack = make([dynamic]^Dict, 0, 4);
append(&p.dict_stack, &p.root);
for p.curr_token.kind != .EOF &&
p.curr_token.kind != .Illegal &&
p.curr_token_index < len(p.tokens) {
if !parse_assignment(p) {
break;
}
}
return p, true;
}
destroy :: proc(p: ^Parser) {
destroy_value :: proc(value: Value) {
#partial switch v in value {
case Array:
for elem in v {
destroy_value(elem);
}
delete(v);
case Dict:
for _, dv in v {
destroy_value(dv);
}
delete(v);
}
}
delete(p.tokens);
for s in p.allocated_strings {
delete(s);
}
delete(p.allocated_strings);
delete(p.dict_stack);
destroy_value(p.root);
free(p);
}
error :: proc(p: ^Parser, pos: Pos, msg: string, args: ..any) {
fmt.eprintf("%s(%d:%d) Error: ", pos.file, pos.line, pos.column);
fmt.eprintf(msg, ..args);
fmt.eprintln();
p.error_count += 1;
}
next_token :: proc(p: ^Parser) -> Token {
p.prev_token = p.curr_token;
prev := p.prev_token;
if p.curr_token_index+1 < len(p.tokens) {
p.curr_token_index += 1;
p.curr_token = p.tokens[p.curr_token_index];
return prev;
}
p.curr_token_index = len(p.tokens);
p.curr_token = p.tokens[p.curr_token_index-1];
error(p, prev.pos, "Token is EOF");
return prev;
}
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
hex_to_int :: proc(c: byte) -> int {
switch c {
case '0'..='9': return int(c-'0');
case 'a'..='f': return int(c-'a')+10;
case 'A'..='F': return int(c-'A')+10;
}
return -1;
}
w: int;
if str[0] == quote && quote == '"' {
return;
} else if str[0] >= 0x80 {
r, w = utf8.decode_rune_in_string(str);
return r, true, str[w:], true;
} else if str[0] != '\\' {
return rune(str[0]), false, str[1:], true;
}
if len(str) <= 1 {
return;
}
s := str;
c := s[1];
s = s[2:];
switch c {
case:
return;
case 'a': r = '\a';
case 'b': r = '\b';
case 'f': r = '\f';
case 'n': r = '\n';
case 'r': r = '\r';
case 't': r = '\t';
case 'v': r = '\v';
case '\\': r = '\\';
case '"': r = '"';
case '\'': r = '\'';
case '0'..='7':
v := int(c-'0');
if len(s) < 2 {
return;
}
for i in 0..<len(s) {
d := int(s[i]-'0');
if d < 0 || d > 7 {
return;
}
v = (v<<3) | d;
}
s = s[2:];
if v > 0xff {
return;
}
r = rune(v);
case 'x', 'u', 'U':
count: int;
switch c {
case 'x': count = 2;
case 'u': count = 4;
case 'U': count = 8;
}
if len(s) < count {
return;
}
for i in 0..<count {
d := hex_to_int(s[i]);
if d < 0 {
return;
}
r = (r<<4) | rune(d);
}
s = s[count:];
if c == 'x' {
break;
}
if r > utf8.MAX_RUNE {
return;
}
multiple_bytes = true;
}
success = true;
tail_string = s;
return;
}
unquote_string :: proc(p: ^Parser, t: Token) -> (string, bool) {
if t.kind != .String {
return t.lit, true;
}
s := t.lit;
quote := '"';
if s == `""` {
return "", true;
}
if strings.contains_rune(s, '\n') >= 0 {
return s, false;
}
if strings.contains_rune(s, '\\') < 0 && strings.contains_rune(s, quote) < 0 {
if quote == '"' {
return s, true;
}
}
buf_len := 3*len(s) / 2;
buf := make([]byte, buf_len);
offset := 0;
for len(s) > 0 {
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote));
if !ok {
delete(buf);
return s, false;
}
s = tail_string;
if r < 0x80 || !multiple_bytes {
buf[offset] = byte(r);
offset += 1;
} else {
b, w := utf8.encode_rune(r);
copy(buf[offset:], b[:w]);
offset += w;
}
}
new_string := string(buf[:offset]);
append(&p.allocated_strings, new_string);
return new_string, true;
}
allow_token :: proc(p: ^Parser, kind: Kind) -> bool {
if p.curr_token.kind == kind {
next_token(p);
return true;
}
return false;
}
expect_token :: proc(p: ^Parser, kind: Kind) -> Token {
prev := p.curr_token;
if prev.kind != kind {
got := prev.lit;
if got == "\n" {
got = ";";
}
error(p, prev.pos, "Expected %s, got %s", kind_to_string[kind], got);
}
next_token(p);
return prev;
}
expect_operator :: proc(p: ^Parser) -> Token {
prev := p.curr_token;
if !is_operator(prev.kind) {
error(p, prev.pos, "Expected an operator, got %s", prev.lit);
}
next_token(p);
return prev;
}
fix_advance :: proc(p: ^Parser) {
for {
#partial switch t := p.curr_token; t.kind {
case .EOF, .Semicolon:
return;
}
next_token(p);
}
}
copy_value :: proc(value: Value) -> Value {
#partial switch v in value {
case Array:
a := make(Array, len(v));
for elem, idx in v {
a[idx] = copy_value(elem);
}
return a;
case Dict:
d := make(Dict, cap(v));
for key, val in v {
d[key] = copy_value(val);
}
return d;
}
return value;
}
lookup_value :: proc(p: ^Parser, name: string) -> (Value, bool) {
for i := len(p.dict_stack)-1; i >= 0; i -= 1 {
d := p.dict_stack[i];
if val, ok := d[name]; ok {
return copy_value(val), true;
}
}
return nil, false;
}
parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
tok := p.curr_token;
#partial switch p.curr_token.kind {
case .Ident:
next_token(p);
v, ok := lookup_value(p, tok.lit);
if !ok { error(p, tok.pos, "Undeclared identifier %s", tok.lit); }
return v, tok.pos;
case .True:
next_token(p);
return true, tok.pos;
case .False:
next_token(p);
return false, tok.pos;
case .Nil:
next_token(p);
return Nil_Value{}, tok.pos;
case .Integer:
next_token(p);
i, _ := strconv.parse_i64(tok.lit);
return i, tok.pos;
case .Float:
next_token(p);
f, _ := strconv.parse_f64(tok.lit);
return f, tok.pos;
case .String:
next_token(p);
str, ok := unquote_string(p, tok);
if !ok { error(p, tok.pos, "Unable to unquote string"); }
return string(str), tok.pos;
case .Open_Paren:
expect_token(p, .Open_Paren);
expr, _ := parse_expr(p);
expect_token(p, .Close_Paren);
return expr, tok.pos;
case .Open_Bracket:
expect_token(p, .Open_Bracket);
elems := make([dynamic]Value, 0, 4);
for p.curr_token.kind != .Close_Bracket &&
p.curr_token.kind != .EOF {
elem, _ := parse_expr(p);
append(&elems, elem);
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
next_token(p);
} else if !allow_token(p, .Comma) {
break;
}
}
expect_token(p, .Close_Bracket);
return Array(elems[:]), tok.pos;
case .Open_Brace:
expect_token(p, .Open_Brace);
dict := Dict{};
append(&p.dict_stack, &dict);
defer pop(&p.dict_stack);
for p.curr_token.kind != .Close_Brace &&
p.curr_token.kind != .EOF {
name_tok := p.curr_token;
if !allow_token(p, .Ident) && !allow_token(p, .String) {
name_tok = expect_token(p, .Ident);
}
name, ok := unquote_string(p, name_tok);
if !ok { error(p, tok.pos, "Unable to unquote string"); }
expect_token(p, .Assign);
elem, _ := parse_expr(p);
if _, ok2 := dict[name]; ok2 {
error(p, name_tok.pos, "Previous declaration of %s in this scope", name);
} else {
dict[name] = elem;
}
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
next_token(p);
} else if !allow_token(p, .Comma) {
break;
}
}
expect_token(p, .Close_Brace);
return dict, tok.pos;
}
return nil, tok.pos;
}
parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
loop := true;
for operand := operand; loop; {
#partial switch p.curr_token.kind {
case .Period:
next_token(p);
tok := next_token(p);
#partial switch tok.kind {
case .Ident:
d, ok := operand.(Dict);
if !ok || d == nil {
error(p, tok.pos, "Expected a dictionary");
operand = nil;
continue;
}
name, usok := unquote_string(p, tok);
if !usok { error(p, tok.pos, "Unable to unquote string"); }
val, found := d[name];
if !found {
error(p, tok.pos, "Field %s not found in dictionary", name);
operand = nil;
continue;
}
operand = val;
case:
error(p, tok.pos, "Expected a selector, got %s", tok.kind);
operand = nil;
}
case .Open_Bracket:
expect_token(p, .Open_Bracket);
index, index_pos := parse_expr(p);
expect_token(p, .Close_Bracket);
#partial switch a in operand {
case Array:
i, ok := index.(i64);
if !ok {
error(p, index_pos, "Index must be an integer for an array");
operand = nil;
continue;
}
if 0 <= i && i < i64(len(a)) {
operand = a[i];
} else {
error(p, index_pos, "Index %d out of bounds range 0..%d", i, len(a));
operand = nil;
continue;
}
case Dict:
key, ok := index.(string);
if !ok {
error(p, index_pos, "Index must be a string for a dictionary");
operand = nil;
continue;
}
val, found := a[key];
if found {
operand = val;
} else {
error(p, index_pos, "`%s` was not found in the dictionary", key);
operand = nil;
continue;
}
case:
error(p, index_pos, "Indexing is only allowed on an array or dictionary");
}
case:
loop = false;
}
}
return operand, pos;
}
parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
op := p.curr_token;
#partial switch p.curr_token.kind {
case .At:
next_token(p);
tok := expect_token(p, .String);
v, ok := lookup_value(p, tok.lit);
if !ok { error(p, tok.pos, "Undeclared identifier %s", tok.lit); }
return parse_atom_expr(p, v, tok.pos);
case .Add, .Sub:
next_token(p);
// TODO(bill): Calcuate values as you go!
expr, pos := parse_unary_expr(p);
#partial switch e in expr {
case i64: if op.kind == .Sub { return -e, pos; }
case f64: if op.kind == .Sub { return -e, pos; }
case:
error(p, op.pos, "Unary operator %s can only be used on integers or floats", op.lit);
return nil, op.pos;
}
return expr, op.pos;
case .Not:
next_token(p);
expr, _ := parse_unary_expr(p);
if v, ok := expr.(bool); ok {
return !v, op.pos;
}
error(p, op.pos, "Unary operator %s can only be used on booleans", op.lit);
return nil, op.pos;
}
return parse_atom_expr(p, parse_operand(p));
}
value_order :: proc(v: Value) -> int {
#partial switch _ in v {
case bool, string:
return 1;
case i64:
return 2;
case f64:
return 3;
}
return 0;
}
match_values :: proc(left, right: ^Value) -> bool {
if value_order(right^) < value_order(left^) {
return match_values(right, left);
}
#partial switch x in left^ {
case:
right^ = left^;
case bool, string:
return true;
case i64:
#partial switch y in right^ {
case i64:
return true;
case f64:
left^ = f64(x);
return true;
}
case f64:
#partial switch y in right {
case f64:
return true;
}
}
return false;
}
calculate_binary_value :: proc(p: ^Parser, op: Kind, a_, b_: Value) -> (Value, bool) {
// TODO(bill): Calculate value as you go!
x, y := a_, b_;
match_values(&x, &y);
#partial switch a in x {
case: return x, true;
case bool:
b, ok := y.(bool);
if !ok { return nil, false; }
#partial switch op {
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .And: return a && b, true;
case .Or: return a || b, true;
}
case i64:
b, ok := y.(i64);
if !ok { return nil, false; }
#partial switch op {
case .Add: return a + b, true;
case .Sub: return a - b, true;
case .Mul: return a * b, true;
case .Quo: return a / b, true;
case .Rem: return a % b, true;
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .Lt: return a < b, true;
case .Gt: return a > b, true;
case .LtEq: return a <= b, true;
case .GtEq: return a >= b, true;
}
case f64:
b, ok := y.(f64);
if !ok { return nil, false; }
#partial switch op {
case .Add: return a + b, true;
case .Sub: return a - b, true;
case .Mul: return a * b, true;
case .Quo: return a / b, true;
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .Lt: return a < b, true;
case .Gt: return a > b, true;
case .LtEq: return a <= b, true;
case .GtEq: return a >= b, true;
}
case string:
b, ok := y.(string);
if !ok { return nil, false; }
#partial switch op {
case .Add:
n := len(a) + len(b);
data := make([]byte, n);
copy(data[:], a);
copy(data[len(a):], b);
s := string(data);
append(&p.allocated_strings, s);
return s, true;
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .Lt: return a < b, true;
case .Gt: return a > b, true;
case .LtEq: return a <= b, true;
case .GtEq: return a >= b, true;
}
}
return nil, false;
}
parse_binary_expr :: proc(p: ^Parser, prec_in: int) -> (Value, Pos) {
expr, pos := parse_unary_expr(p);
for prec := precedence(p.curr_token.kind); prec >= prec_in; prec -= 1 {
for {
op := p.curr_token;
op_prec := precedence(op.kind);
if op_prec != prec {
break;
}
expect_operator(p);
if op.kind == .Question {
cond := expr;
x, _ := parse_expr(p);
expect_token(p, .Colon);
y, _ := parse_expr(p);
if t, ok := cond.(bool); ok {
expr = t ? x : y;
} else {
error(p, pos, "Condition must be a boolean");
}
} else {
right, right_pos := parse_binary_expr(p, prec+1);
if right == nil {
error(p, right_pos, "Expected expression on the right-hand side of the binary operator %s", op.lit);
}
left := expr;
ok: bool;
expr, ok = calculate_binary_value(p, op.kind, left, right);
if !ok {
error(p, pos, "Invalid binary operation");
}
}
}
}
return expr, pos;
}
parse_expr :: proc(p: ^Parser) -> (Value, Pos) {
return parse_binary_expr(p, 1);
}
expect_semicolon :: proc(p: ^Parser) {
kind := p.curr_token.kind;
#partial switch kind {
case .Comma:
error(p, p.curr_token.pos, "Expected ';', got ','");
next_token(p);
case .Semicolon:
next_token(p);
case .EOF:
// okay
case:
error(p, p.curr_token.pos, "Expected ';', got %s", p.curr_token.lit);
fix_advance(p);
}
}
parse_assignment :: proc(p: ^Parser) -> bool {
top_dict :: proc(p: ^Parser) -> ^Dict {
assert(len(p.dict_stack) > 0);
return p.dict_stack[len(p.dict_stack)-1];
}
if p.curr_token.kind == .Semicolon {
next_token(p);
return true;
}
if p.curr_token.kind == .EOF {
return false;
}
tok := p.curr_token;
if allow_token(p, .Ident) || allow_token(p, .String) {
expect_token(p, .Assign);
name, ok := unquote_string(p, tok);
if !ok { error(p, tok.pos, "Unable to unquote string"); }
expr, _ := parse_expr(p);
d := top_dict(p);
if _, ok2 := d[name]; ok2 {
error(p, tok.pos, "Previous declaration of %s", name);
} else {
d[name] = expr;
}
expect_semicolon(p);
return true;
}
error(p, tok.pos, "Expected an assignment, got %s", kind_to_string[tok.kind]);
fix_advance(p);
return false;
}
-51
View File
@@ -1,51 +0,0 @@
/*
package cel
sample := `
x = 123;
y = 321.456;
z = x * (y - 1) / 2;
w = "foo" + "bar";
# This is a comment
asd = "Semicolons are optional"
a = {id = {b = 123}} # Dict
b = a.id.b
f = [1, 4, 9] # Array
g = f[2]
h = x < y and w == "foobar"
i = h ? 123 : "google"
j = nil
"127.0.0.1" = "value" # Keys can be strings
"foo" = {
"bar" = {
"baz" = 123, # optional commas if newline is present
"zab" = 456,
"abz" = 789,
},
};
bar = @"foo"["bar"].baz
`;
main :: proc() {
p, ok := create_from_string(sample);
if !ok {
return;
}
defer destroy(p);
if p.error_count == 0 {
print(p);
}
}
*/
package cel
-523
View File
@@ -1,523 +0,0 @@
package cel
import "core:fmt"
import "core:unicode/utf8"
using Kind :: enum {
Illegal,
EOF,
Comment,
_literal_start,
Ident,
Integer,
Float,
Char,
String,
_literal_end,
_keyword_start,
True, // true
False, // false
Nil, // nil
_keyword_end,
_operator_start,
Question, // ?
And, // and
Or, // or
Add, // +
Sub, // -
Mul, // *
Quo, // /
Rem, // %
Not, // !
Eq, // ==
NotEq, // !=
Lt, // <
Gt, // >
LtEq, // <=
GtEq, // >=
At, // @
_operator_end,
_punc_start,
Assign, // =
Open_Paren, // (
Close_Paren, // )
Open_Bracket, // [
Close_Bracket, // ]
Open_Brace, // {
Close_Brace, // }
Colon, // :
Semicolon, // ;
Comma, // ,
Period, // .
_punc_end,
}
Pos :: struct {
file: string,
line: int,
column: int,
}
Token :: struct {
kind: Kind,
using pos: Pos,
lit: string,
}
Tokenizer :: struct {
src: []byte,
file: string, // May not be used
curr_rune: rune,
offset: int,
read_offset: int,
line_offset: int,
line_count: int,
insert_semi: bool,
error_count: int,
}
keywords := map[string]Kind{
"true" = True,
"false" = False,
"nil" = Nil,
"and" = And,
"or" = Or,
};
kind_to_string := [len(Kind)]string{
"illegal",
"EOF",
"comment",
"",
"identifier",
"integer",
"float",
"character",
"string",
"",
"",
"true", "false", "nil",
"",
"",
"?", "and", "or",
"+", "-", "*", "/", "%",
"!",
"==", "!=", "<", ">", "<=", ">=",
"@",
"",
"",
"=",
"(", ")",
"[", "]",
"{", "}",
":", ";", ",", ".",
"",
};
precedence :: proc(op: Kind) -> int {
#partial switch op {
case Question:
return 1;
case Or:
return 2;
case And:
return 3;
case Eq, NotEq, Lt, Gt, LtEq, GtEq:
return 4;
case Add, Sub:
return 5;
case Mul, Quo, Rem:
return 6;
}
return 0;
}
token_lookup :: proc(ident: string) -> Kind {
if tok, is_keyword := keywords[ident]; is_keyword {
return tok;
}
return Ident;
}
is_literal :: proc(tok: Kind) -> bool { return _literal_start < tok && tok < _literal_end; }
is_operator :: proc(tok: Kind) -> bool { return _operator_start < tok && tok < _operator_end; }
is_keyword :: proc(tok: Kind) -> bool { return _keyword_start < tok && tok < _keyword_end; }
tokenizer_init :: proc(t: ^Tokenizer, src: []byte, file := "") {
t.src = src;
t.file = file;
t.curr_rune = ' ';
t.offset = 0;
t.read_offset = 0;
t.line_offset = 0;
t.line_count = 1;
advance_to_next_rune(t);
if t.curr_rune == utf8.RUNE_BOM {
advance_to_next_rune(t);
}
}
token_error :: proc(t: ^Tokenizer, msg: string, args: ..any) {
fmt.eprintf("%s(%d:%d) Error: ", t.file, t.line_count, t.read_offset-t.line_offset+1);
fmt.eprintf(msg, ..args);
fmt.eprintln();
t.error_count += 1;
}
advance_to_next_rune :: proc(t: ^Tokenizer) {
if t.read_offset < len(t.src) {
t.offset = t.read_offset;
if t.curr_rune == '\n' {
t.line_offset = t.offset;
t.line_count += 1;
}
r, w := rune(t.src[t.read_offset]), 1;
switch {
case r == 0:
token_error(t, "Illegal character NUL");
case r >= utf8.RUNE_SELF:
r, w = utf8.decode_rune(t.src[t.read_offset:]);
if r == utf8.RUNE_ERROR && w == 1 {
token_error(t, "Illegal utf-8 encoding");
} else if r == utf8.RUNE_BOM && t.offset > 0 {
token_error(t, "Illegal byte order mark");
}
}
t.read_offset += w;
t.curr_rune = r;
} else {
t.offset = len(t.src);
if t.curr_rune == '\n' {
t.line_offset = t.offset;
t.line_count += 1;
}
t.curr_rune = utf8.RUNE_EOF;
}
}
get_pos :: proc(t: ^Tokenizer) -> Pos {
return Pos {
file = t.file,
line = t.line_count,
column = t.offset - t.line_offset + 1,
};
}
is_letter :: proc(r: rune) -> bool {
switch r {
case 'a'..='z', 'A'..='Z', '_':
return true;
}
return false;
}
is_digit :: proc(r: rune) -> bool {
switch r {
case '0'..='9':
return true;
}
return false;
}
skip_whitespace :: proc(t: ^Tokenizer) {
loop: for {
switch t.curr_rune {
case '\n':
if t.insert_semi {
break loop;
}
fallthrough;
case ' ', '\t', '\r', '\v', '\f':
advance_to_next_rune(t);
case:
break loop;
}
}
}
scan_identifier :: proc(t: ^Tokenizer) -> string {
offset := t.offset;
for is_letter(t.curr_rune) || is_digit(t.curr_rune) {
advance_to_next_rune(t);
}
return string(t.src[offset : t.offset]);
}
digit_value :: proc(r: rune) -> int {
switch r {
case '0'..='9': return int(r - '0');
case 'a'..='f': return int(r - 'a' + 10);
case 'A'..='F': return int(r - 'A' + 10);
}
return 16;
}
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) {
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
for digit_value(t.curr_rune) < base || t.curr_rune == '_' {
advance_to_next_rune(t);
}
}
scan_exponent :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
kind = tok;
if t.curr_rune == 'e' || t.curr_rune == 'E' {
kind = Float;
advance_to_next_rune(t);
if t.curr_rune == '-' || t.curr_rune == '+' {
advance_to_next_rune(t);
}
if digit_value(t.curr_rune) < 10 {
scan_mantissa(t, 10);
} else {
token_error(t, "Illegal floating point exponent");
}
}
text = string(t.src[offset : t.offset]);
return;
}
scan_fraction :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
kind = tok;
if t.curr_rune == '.' {
kind = Float;
advance_to_next_rune(t);
scan_mantissa(t, 10);
}
return scan_exponent(t, kind, offset);
}
offset := t.offset;
tok := Integer;
if seen_decimal_point {
offset -= 1;
tok = Float;
scan_mantissa(t, 10);
return scan_exponent(t, tok, offset);
}
if t.curr_rune == '0' {
offset = t.offset;
advance_to_next_rune(t);
switch t.curr_rune {
case 'b', 'B':
advance_to_next_rune(t);
scan_mantissa(t, 2);
if t.offset - offset <= 2 {
token_error(t, "Illegal binary number");
}
case 'o', 'O':
advance_to_next_rune(t);
scan_mantissa(t, 8);
if t.offset - offset <= 2 {
token_error(t, "Illegal octal number");
}
case 'x', 'X':
advance_to_next_rune(t);
scan_mantissa(t, 16);
if t.offset - offset <= 2 {
token_error(t, "Illegal hexadecimal number");
}
case:
scan_mantissa(t, 10);
switch t.curr_rune {
case '.', 'e', 'E':
return scan_fraction(t, tok, offset);
}
}
return tok, string(t.src[offset:t.offset]);
}
scan_mantissa(t, 10);
return scan_fraction(t, tok, offset);
}
scan :: proc(t: ^Tokenizer) -> Token {
skip_whitespace(t);
offset := t.offset;
tok: Kind;
pos := get_pos(t);
lit: string;
insert_semi := false;
switch r := t.curr_rune; {
case is_letter(r):
insert_semi = true;
lit = scan_identifier(t);
tok = Ident;
if len(lit) > 1 {
tok = token_lookup(lit);
}
case '0' <= r && r <= '9':
insert_semi = true;
tok, lit = scan_number(t, false);
case:
advance_to_next_rune(t);
switch r {
case -1:
if t.insert_semi {
t.insert_semi = false;
return Token{Semicolon, pos, "\n"};
}
return Token{EOF, pos, "\n"};
case '\n':
t.insert_semi = false;
return Token{Semicolon, pos, "\n"};
case '"':
insert_semi = true;
quote := r;
tok = String;
for {
this_r := t.curr_rune;
if this_r == '\n' || r < 0 {
token_error(t, "String literal not terminated");
break;
}
advance_to_next_rune(t);
if this_r == quote {
break;
}
// TODO(bill); Handle properly
if this_r == '\\' && t.curr_rune == quote {
advance_to_next_rune(t);
}
}
lit = string(t.src[offset+1:t.offset-1]);
case '#':
for t.curr_rune != '\n' && t.curr_rune >= 0 {
advance_to_next_rune(t);
}
if t.insert_semi {
t.insert_semi = false;
return Token{Semicolon, pos, "\n"};
}
// Recursive!
return scan(t);
case '?': tok = Question;
case ':': tok = Colon;
case '@': tok = At;
case ';':
tok = Semicolon;
lit = ";";
case ',': tok = Comma;
case '(':
tok = Open_Paren;
case ')':
insert_semi = true;
tok = Close_Paren;
case '[':
tok = Open_Bracket;
case ']':
insert_semi = true;
tok = Close_Bracket;
case '{':
tok = Open_Brace;
case '}':
insert_semi = true;
tok = Close_Brace;
case '+': tok = Add;
case '-': tok = Sub;
case '*': tok = Mul;
case '/': tok = Quo;
case '%': tok = Rem;
case '!':
tok = Not;
if t.curr_rune == '=' {
advance_to_next_rune(t);
tok = NotEq;
}
case '=':
tok = Assign;
if t.curr_rune == '=' {
advance_to_next_rune(t);
tok = Eq;
}
case '<':
tok = Lt;
if t.curr_rune == '=' {
advance_to_next_rune(t);
tok = LtEq;
}
case '>':
tok = Gt;
if t.curr_rune == '=' {
advance_to_next_rune(t);
tok = GtEq;
}
case '.':
if '0' <= t.curr_rune && t.curr_rune <= '9' {
insert_semi = true;
tok, lit = scan_number(t, true);
} else {
tok = Period;
}
case:
if r != utf8.RUNE_BOM {
token_error(t, "Illegal character '%r'", r);
}
insert_semi = t.insert_semi;
tok = Illegal;
}
}
t.insert_semi = insert_semi;
if lit == "" {
lit = string(t.src[offset:t.offset]);
}
return Token{tok, pos, lit};
}
+10 -31
View File
@@ -64,21 +64,15 @@ write :: proc(w: ^Writer, record: []string) -> io.Error {
field := record[field_idx];
if field_idx > 0 {
if _, err := io.write_rune(w.w, w.comma); err != nil {
return err;
}
io.write_rune(w.w, w.comma) or_return;
}
if !field_needs_quoting(w, field) {
if _, err := io.write_string(w.w, field); err != nil {
return err;
}
io.write_string(w.w, field) or_return;
continue;
}
if err := io.write_byte(w.w, '"'); err != nil {
return err;
}
io.write_byte(w.w, '"') or_return;
for len(field) > 0 {
i := strings.index_any(field, CHAR_SET);
@@ -86,40 +80,28 @@ write :: proc(w: ^Writer, record: []string) -> io.Error {
i = len(field);
}
if _, err := io.write_string(w.w, field[:i]); err != nil {
return err;
}
io.write_string(w.w, field[:i]) or_return;
field = field[i:];
if len(field) > 0 {
switch field[0] {
case '\r':
if !w.use_crlf {
if err := io.write_byte(w.w, '\r'); err != nil {
return err;
}
io.write_byte(w.w, '\r') or_return;
}
case '\n':
if w.use_crlf {
if _, err := io.write_string(w.w, "\r\n"); err != nil {
return err;
}
io.write_string(w.w, "\r\n") or_return;
} else {
if err := io.write_byte(w.w, '\n'); err != nil {
return err;
}
io.write_byte(w.w, '\n') or_return;
}
case '"':
if _, err := io.write_string(w.w, `""`); err != nil {
return err;
}
io.write_string(w.w, `""`) or_return;
}
field = field[1:];
}
}
if err := io.write_byte(w.w, '"'); err != nil {
return err;
}
io.write_byte(w.w, '"') or_return;
}
if w.use_crlf {
@@ -132,10 +114,7 @@ write :: proc(w: ^Writer, record: []string) -> io.Error {
// write_all writes multiple CSV records to w using write, and then flushes (if necessary).
write_all :: proc(w: ^Writer, records: [][]string) -> io.Error {
for record in records {
err := write(w, record);
if err != nil {
return err;
}
write(w, record) or_return;
}
return writer_flush(w);
}
+83
View File
@@ -0,0 +1,83 @@
// Implementation of the HxA 3D asset format
// HxA is a interchangeable graphics asset format.
// Designed by Eskil Steenberg. @quelsolaar / eskil 'at' obsession 'dot' se / www.quelsolaar.com
//
// Author of this Odin package: Ginger Bill
//
// Following comment is copied from the original C-implementation
// ---------
// -Does the world need another Graphics file format?
// Unfortunately, Yes. All existing formats are either too large and complicated to be implemented from
// scratch, or don't have some basic features needed in modern computer graphics.
// -Who is this format for?
// For people who want a capable open Graphics format that can be implemented from scratch in
// a few hours. It is ideal for graphics researchers, game developers or other people who
// wants to build custom graphics pipelines. Given how easy it is to parse and write, it
// should be easy to write utilities that process assets to preform tasks like: generating
// normals, light-maps, tangent spaces, Error detection, GPU optimization, LOD generation,
// and UV mapping.
// -Why store images in the format when there are so many good image formats already?
// Yes there are, but only for 2D RGB/RGBA images. A lot of computer graphics rendering rely
// on 1D, 3D, cube, multilayer, multi channel, floating point bitmap buffers. There almost no
// formats for this kind of data. Also 3D files that reference separate image files rely on
// file paths, and this often creates issues when the assets are moved. By including the
// texture data in the files directly the assets become self contained.
// -Why doesn't the format support <insert whatever>?
// Because the entire point is to make a format that can be implemented. Features like NURBSs,
// Construction history, or BSP trees would make the format too large to serve its purpose.
// The facilities of the formats to store meta data should make the format flexible enough
// for most uses. Adding HxA support should be something anyone can do in a days work.
// Structure:
// ----------
// HxA is designed to be extremely simple to parse, and is therefore based around conventions. It has
// a few basic structures, and depending on how they are used they mean different things. This means
// that you can implement a tool that loads the entire file, modifies the parts it cares about and
// leaves the rest intact. It is also possible to write a tool that makes all data in the file
// editable without the need to understand its use. It is also possible for anyone to use the format
// to store data axillary data. Anyone who wants to store data not covered by a convention can submit
// a convention to extend the format. There should never be a convention for storing the same data in
// two differed ways.
// The data is story in a number of nodes that are stored in an array. Each node stores an array of
// meta data. Meta data can describe anything you want, and a lot of conventions will use meta data
// to store additional information, for things like transforms, lights, shaders and animation.
// Data for Vertices, Corners, Faces, and Pixels are stored in named layer stacks. Each stack consists
// of a number of named layers. All layers in the stack have the same number of elements. Each layer
// describes one property of the primitive. Each layer can have multiple channels and each layer can
// store data of a different type.
// HaX stores 3 kinds of nodes
// - Pixel data.
// - Polygon geometry data.
// - Meta data only.
// Pixel Nodes stores pixels in a layer stack. A layer may store things like Albedo, Roughness,
// Reflectance, Light maps, Masks, Normal maps, and Displacement. Layers use the channels of the
// layers to store things like color. The length of the layer stack is determined by the type and
// dimensions stored in the
// Geometry data is stored in 3 separate layer stacks for: vertex data, corner data and face data. The
// vertex data stores things like verities, blend shapes, weight maps, and vertex colors. The first
// layer in a vertex stack has to be a 3 channel layer named "position" describing the base position
// of the vertices. The corner stack describes data per corner or edge of the polygons. It can be used
// for things like UV, normals, and adjacency. The first layer in a corner stack has to be a 1 channel
// integer layer named "index" describing the vertices used to form polygons. The last value in each
// polygon has a negative - 1 index to indicate the end of the polygon.
// Example:
// A quad and a tri with the vertex index:
// [0, 1, 2, 3] [1, 4, 2]
// is stored:
// [0, 1, 2, -4, 1, 4, -3]
// The face stack stores values per face. the length of the face stack has to match the number of
// negative values in the index layer in the corner stack. The face stack can be used to store things
// like material index.
// Storage
// -------
// All data is stored in little endian byte order with no padding. The layout mirrors the structs
// defined below with a few exceptions. All names are stored as a 8-bit unsigned integer indicating
// the length of the name followed by that many characters. Termination is not stored in the file.
// Text strings stored in meta data are stored the same way as names, but instead of a 8-bit unsigned
// integer a 32-bit unsigned integer is used.
package encoding_hxa
+193
View File
@@ -0,0 +1,193 @@
package encoding_hxa
import "core:mem"
LATEST_VERSION :: 3;
VERSION_API :: "0.3";
MAGIC_NUMBER :: 'H'<<0 | 'x'<<8 | 'A'<<16 | '\x00'<<24;
Header :: struct #packed {
magic_number: u32le,
version: u32le,
internal_node_count: u32le,
}
File :: struct {
using header: Header,
backing: []byte,
allocator: mem.Allocator,
nodes: []Node,
}
Node_Type :: enum u8 {
Meta_Only = 0, // node only containing meta data.
Geometry = 1, // node containing a geometry mesh, and meta data.
Image = 2, // node containing a 1D, 2D, 3D, or Cube image, and meta data.
}
Layer_Data_Type :: enum u8 {
Uint8 = 0, // 8-bit unsigned integer,
Int32 = 1, // 32-bit little-endian signed integer
Float = 2, // 32-bit little-endian IEEE 754 floating point value
Double = 3, // 64-bit little-endian IEEE 754 floating point value
}
// Pixel data is arranged in the following configurations
Image_Type :: enum u8 {
Image_Cube = 0, // 6 sided qube, in the order of: +x, -x, +y, -y, +z, -z.
Image_1D = 1, // One dimensional pixel data.
Image_2D = 2, // Two dimensional pixel data.
Image_3D = 3, // Three dimensional pixel data.
}
Meta_Value_Type :: enum u8 {
Int64 = 0,
Double = 1,
Node = 2,
Text = 3,
Binary = 4,
Meta = 5,
};
Meta :: struct {
name: string, // name of the meta data value (maximum length is 255)
value: union {
[]i64le,
[]f64le,
[]Node_Index, // a reference to another node
string, // text
[]byte, // binary data
[]Meta,
},
}
Layer :: struct {
name: string, // name of the layer (maximum length is 255)
components: u8, // 2 for uv, 3 for xyz/rgb, 4 for rgba
data: union {
[]u8,
[]i32le,
[]f32le,
[]f64le,
},
}
// Layers stacks are arrays of layers where all the layers have the same number of entries (polygons, edges, vertices or pixels)
Layer_Stack :: distinct []Layer;
Node_Geometry :: struct {
vertex_count: u32le, // number of vertices
vertex_stack: Layer_Stack, // stack of vertex arrays. the first layer is always the vertex positions
edge_corner_count: u32le, // number of corners
corner_stack: Layer_Stack, // stack of corner arrays, the first layer is always a reference array (see below)
edge_stack: Layer_Stack, // stack of edge arrays
face_count: u32le, // number of polygons
face_stack: Layer_Stack, // stack of per polygon data.
}
Node_Image :: struct {
type: Image_Type,
resolution: [3]u32le,
image_stack: Layer_Stack,
}
Node_Index :: distinct u32le;
// A file consists of an array of nodes, All nodes have meta data. Geometry nodes have geometry, image nodes have pixels
Node :: struct {
meta_data: []Meta,
content: union {
Node_Geometry,
Node_Image,
},
}
/* Conventions */
/* ------------
Much of HxA's use is based on convention. HxA lets users store arbitrary data in its structure that can be parsed but whose semantic meaning does not need to be understood.
A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basicly recomendations of how to store common data.
If you use HxA for something not covered by the conventions but need a convention for your use case. Please let us know so that we can add it!
*/
/* Hard conventions */
/* ---------------- */
CONVENTION_HARD_BASE_VERTEX_LAYER_NAME :: "vertex";
CONVENTION_HARD_BASE_VERTEX_LAYER_ID :: 0;
CONVENTION_HARD_BASE_VERTEX_LAYER_COMPONENTS :: 3;
CONVENTION_HARD_BASE_CORNER_LAYER_NAME :: "reference";
CONVENTION_HARD_BASE_CORNER_LAYER_ID :: 0;
CONVENTION_HARD_BASE_CORNER_LAYER_COMPONENTS :: 1;
CONVENTION_HARD_BASE_CORNER_LAYER_TYPE :: Layer_Data_Type.Int32;
CONVENTION_HARD_EDGE_NEIGHBOUR_LAYER_NAME :: "neighbour";
CONVENTION_HARD_EDGE_NEIGHBOUR_LAYER_TYPE :: Layer_Data_Type.Int32;
/* Soft Conventions */
/* ---------------- */
/* geometry layers */
CONVENTION_SOFT_LAYER_SEQUENCE0 :: "sequence";
CONVENTION_SOFT_LAYER_NAME_UV0 :: "uv";
CONVENTION_SOFT_LAYER_NORMALS :: "normal";
CONVENTION_SOFT_LAYER_BINORMAL :: "binormal";
CONVENTION_SOFT_LAYER_TANGENT :: "tangent";
CONVENTION_SOFT_LAYER_COLOR :: "color";
CONVENTION_SOFT_LAYER_CREASES :: "creases";
CONVENTION_SOFT_LAYER_SELECTION :: "select";
CONVENTION_SOFT_LAYER_SKIN_WEIGHT :: "skining_weight";
CONVENTION_SOFT_LAYER_SKIN_REFERENCE :: "skining_reference";
CONVENTION_SOFT_LAYER_BLENDSHAPE :: "blendshape";
CONVENTION_SOFT_LAYER_ADD_BLENDSHAPE :: "addblendshape";
CONVENTION_SOFT_LAYER_MATERIAL_ID :: "material";
/* Image layers */
CONVENTION_SOFT_ALBEDO :: "albedo";
CONVENTION_SOFT_LIGHT :: "light";
CONVENTION_SOFT_DISPLACEMENT :: "displacement";
CONVENTION_SOFT_DISTORTION :: "distortion";
CONVENTION_SOFT_AMBIENT_OCCLUSION :: "ambient_occlusion";
/* tags layers */
CONVENTION_SOFT_NAME :: "name";
CONVENTION_SOFT_TRANSFORM :: "transform";
/* destroy procedures */
meta_destroy :: proc(meta: Meta, allocator := context.allocator) {
if nested, ok := meta.value.([]Meta); ok {
for m in nested {
meta_destroy(m);
}
delete(nested, allocator);
}
}
nodes_destroy :: proc(nodes: []Node, allocator := context.allocator) {
for node in nodes {
for meta in node.meta_data {
meta_destroy(meta);
}
delete(node.meta_data, allocator);
switch n in node.content {
case Node_Geometry:
delete(n.corner_stack, allocator);
delete(n.edge_stack, allocator);
delete(n.face_stack, allocator);
case Node_Image:
delete(n.image_stack, allocator);
}
}
delete(nodes, allocator);
}
file_destroy :: proc(file: File) {
nodes_destroy(file.nodes, file.allocator);
delete(file.backing, file.allocator);
}
+215
View File
@@ -0,0 +1,215 @@
package encoding_hxa
import "core:fmt"
import "core:os"
import "core:mem"
Read_Error :: enum {
None,
Short_Read,
Invalid_Data,
Unable_To_Read_File,
}
read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) {
context.allocator = allocator;
data, ok := os.read_entire_file(filename);
if !ok {
err = .Unable_To_Read_File;
return;
}
defer if !ok {
delete(data);
} else {
file.backing = data;
}
file, err = read(data, filename, print_error, allocator);
return;
}
read :: proc(data: []byte, filename := "<input>", print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) {
Reader :: struct {
filename: string,
data: []byte,
offset: int,
print_error: bool,
};
read_value :: proc(r: ^Reader, $T: typeid) -> (value: T, err: Read_Error) {
remaining := len(r.data) - r.offset;
if remaining < size_of(T) {
err = .Short_Read;
return;
}
ptr := raw_data(r.data[r.offset:]);
value = (^T)(ptr)^;
r.offset += size_of(T);
return;
}
read_array :: proc(r: ^Reader, $T: typeid, count: int) -> (value: []T, err: Read_Error) {
remaining := len(r.data) - r.offset;
if remaining < size_of(T)*count {
err = .Short_Read;
return;
}
ptr := raw_data(r.data[r.offset:]);
value = mem.slice_ptr((^T)(ptr), count);
r.offset += size_of(T)*count;
return;
}
read_string :: proc(r: ^Reader, count: int) -> (string, Read_Error) {
buf, err := read_array(r, byte, count);
return string(buf), err;
}
read_name :: proc(r: ^Reader) -> (value: string, err: Read_Error) {
len := read_value(r, u8) or_return;
data := read_array(r, byte, int(len)) or_return;
return string(data[:len]), nil;
}
read_meta :: proc(r: ^Reader, capacity: u32le) -> (meta_data: []Meta, err: Read_Error) {
meta_data = make([]Meta, int(capacity));
count := 0;
defer meta_data = meta_data[:count];
for m in &meta_data {
m.name = read_name(r) or_return;
type := read_value(r, Meta_Value_Type) or_return;
if type > max(Meta_Value_Type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is ", r.filename, u8(type), u8(max(Meta_Value_Type)));
}
err = .Invalid_Data;
return;
}
array_length := read_value(r, u32le) or_return;
switch type {
case .Int64: m.value = read_array(r, i64le, int(array_length)) or_return;
case .Double: m.value = read_array(r, f64le, int(array_length)) or_return;
case .Node: m.value = read_array(r, Node_Index, int(array_length)) or_return;
case .Text: m.value = read_string(r, int(array_length)) or_return;
case .Binary: m.value = read_array(r, byte, int(array_length)) or_return;
case .Meta: m.value = read_meta(r, array_length) or_return;
}
count += 1;
}
return;
}
read_layer_stack :: proc(r: ^Reader, capacity: u32le) -> (layers: Layer_Stack, err: Read_Error) {
stack_count := read_value(r, u32le) or_return;
layer_count := 0;
layers = make(Layer_Stack, stack_count);
defer layers = layers[:layer_count];
for layer in &layers {
layer.name = read_name(r) or_return;
layer.components = read_value(r, u8) or_return;
type := read_value(r, Layer_Data_Type) or_return;
if type > max(type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is ", r.filename, u8(type), u8(max(Layer_Data_Type)));
}
err = .Invalid_Data;
return;
}
data_len := int(layer.components) * int(capacity);
switch type {
case .Uint8: layer.data = read_array(r, u8, data_len) or_return;
case .Int32: layer.data = read_array(r, i32le, data_len) or_return;
case .Float: layer.data = read_array(r, f32le, data_len) or_return;
case .Double: layer.data = read_array(r, f64le, data_len) or_return;
}
layer_count += 1;
}
return;
}
if len(data) < size_of(Header) {
return;
}
context.allocator = allocator;
header := cast(^Header)raw_data(data);
assert(header.magic_number == MAGIC_NUMBER);
r := &Reader{
filename = filename,
data = data[:],
offset = size_of(Header),
print_error = print_error,
};
node_count := 0;
file.nodes = make([]Node, header.internal_node_count);
defer if err != nil {
nodes_destroy(file.nodes);
file.nodes = nil;
}
defer file.nodes = file.nodes[:node_count];
for node_idx in 0..<header.internal_node_count {
node := &file.nodes[node_count];
type := read_value(r, Node_Type) or_return;
if type > max(Node_Type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is ", r.filename, u8(type), u8(max(Node_Type)));
}
err = .Invalid_Data;
return;
}
node_count += 1;
node.meta_data = read_meta(r, read_value(r, u32le) or_return) or_return;
switch type {
case .Meta_Only:
// Okay
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;
if header.version > 2 {
g.edge_stack = read_layer_stack(r, g.edge_corner_count) or_return;
}
g.face_count = read_value(r, u32le) or_return;
g.face_stack = read_layer_stack(r, g.face_count) or_return;
node.content = g;
case .Image:
img: Node_Image;
img.type = read_value(r, Image_Type) or_return;
dimensions := int(img.type);
if img.type == .Image_Cube {
dimensions = 2;
}
img.resolution = {1, 1, 1};
for d in 0..<dimensions {
img.resolution[d] = read_value(r, u32le) or_return;
}
size := img.resolution[0]*img.resolution[1]*img.resolution[2];
if img.type == .Image_Cube {
size *= 6;
}
img.image_stack = read_layer_stack(r, size) or_return;
node.content = img;
}
}
return;
}
+193
View File
@@ -0,0 +1,193 @@
package encoding_hxa
import "core:os"
import "core:mem"
Write_Error :: enum {
None,
Buffer_Too_Small,
Failed_File_Write,
}
write_to_file :: proc(filepath: string, file: File) -> (err: Write_Error) {
required := required_write_size(file);
buf, alloc_err := make([]byte, required);
if alloc_err == .Out_Of_Memory {
return .Failed_File_Write;
}
defer delete(buf);
write_internal(&Writer{data = buf}, file);
if !os.write_entire_file(filepath, buf) {
err =.Failed_File_Write;
}
return;
}
write :: proc(buf: []byte, file: File) -> (n: int, err: Write_Error) {
required := required_write_size(file);
if len(buf) < required {
err = .Buffer_Too_Small;
return;
}
n = required;
write_internal(&Writer{data = buf}, file);
return;
}
required_write_size :: proc(file: File) -> (n: int) {
writer := &Writer{dummy_pass = true};
write_internal(writer, file);
n = writer.offset;
return;
}
@(private)
Writer :: struct {
data: []byte,
offset: int,
dummy_pass: bool,
};
@(private)
write_internal :: proc(w: ^Writer, file: File) {
write_value :: proc(w: ^Writer, value: $T) {
if !w.dummy_pass {
remaining := len(w.data) - w.offset;
assert(size_of(T) <= remaining);
ptr := raw_data(w.data[w.offset:]);
(^T)(ptr)^ = value;
}
w.offset += size_of(T);
}
write_array :: proc(w: ^Writer, array: []$T) {
if !w.dummy_pass {
remaining := len(w.data) - w.offset;
assert(size_of(T)*len(array) <= remaining);
ptr := raw_data(w.data[w.offset:]);
dst := mem.slice_ptr((^T)(ptr), len(array));
copy(dst, array);
}
w.offset += size_of(T)*len(array);
}
write_string :: proc(w: ^Writer, str: string) {
if !w.dummy_pass {
remaining := len(w.data) - w.offset;
assert(size_of(byte)*len(str) <= remaining);
ptr := raw_data(w.data[w.offset:]);
dst := mem.slice_ptr((^byte)(ptr), len(str));
copy(dst, str);
}
w.offset += size_of(byte)*len(str);
}
write_metadata :: proc(w: ^Writer, meta_data: []Meta) {
for m in meta_data {
name_len := max(len(m.name), 255);
write_value(w, u8(name_len));
write_string(w, m.name[:name_len]);
meta_data_type: Meta_Value_Type;
length: u32le = 0;
switch v in m.value {
case []i64le:
meta_data_type = .Int64;
length = u32le(len(v));
case []f64le:
meta_data_type = .Double;
length = u32le(len(v));
case []Node_Index:
meta_data_type = .Node;
length = u32le(len(v));
case string:
meta_data_type = .Text;
length = u32le(len(v));
case []byte:
meta_data_type = .Binary;
length = u32le(len(v));
case []Meta:
meta_data_type = .Meta;
length = u32le(len(v));
}
write_value(w, meta_data_type);
write_value(w, length);
switch v in m.value {
case []i64le: write_array(w, v);
case []f64le: write_array(w, v);
case []Node_Index: write_array(w, v);
case string: write_string(w, v);
case []byte: write_array(w, v);
case []Meta: write_metadata(w, v);
}
}
return;
}
write_layer_stack :: proc(w: ^Writer, layers: Layer_Stack) {
write_value(w, u32(len(layers)));
for layer in layers {
name_len := max(len(layer.name), 255);
write_value(w, u8(name_len));
write_string(w, layer .name[:name_len]);
write_value(w, layer.components);
layer_data_type: Layer_Data_Type;
switch v in layer.data {
case []u8: layer_data_type = .Uint8;
case []i32le: layer_data_type = .Int32;
case []f32le: layer_data_type = .Float;
case []f64le: layer_data_type = .Double;
}
write_value(w, layer_data_type);
switch v in layer.data {
case []u8: write_array(w, v);
case []i32le: write_array(w, v);
case []f32le: write_array(w, v);
case []f64le: write_array(w, v);
}
}
return;
}
write_value(w, &Header{
magic_number = MAGIC_NUMBER,
version = LATEST_VERSION,
internal_node_count = u32le(len(file.nodes)),
});
for node in file.nodes {
node_type: Node_Type;
switch content in node.content {
case Node_Geometry: node_type = .Geometry;
case Node_Image: node_type = .Image;
}
write_value(w, node_type);
write_value(w, u32(len(node.meta_data)));
write_metadata(w, node.meta_data);
switch content in node.content {
case Node_Geometry:
write_value(w, content.vertex_count);
write_layer_stack(w, content.vertex_stack);
write_value(w, content.edge_corner_count);
write_layer_stack(w, content.corner_stack);
write_layer_stack(w, content.edge_stack);
write_value(w, content.face_count);
write_layer_stack(w, content.face_stack);
case Node_Image:
write_value(w, content.type);
dimensions := int(content.type);
if content.type == .Image_Cube {
dimensions = 2;
}
for d in 0..<dimensions {
write_value(w, content.resolution[d]);
}
write_layer_stack(w, content.image_stack);
}
}
}
+61 -60
View File
@@ -31,21 +31,19 @@ marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Erro
marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
using strings;
using runtime;
if v == nil {
write_string(b, "null");
strings.write_string(b, "null");
return .None;
}
ti := type_info_base(type_info_of(v.id));
ti := runtime.type_info_base(type_info_of(v.id));
a := any{v.data, ti.id};
switch info in ti.variant {
case Type_Info_Named:
case runtime.Type_Info_Named:
unreachable();
case Type_Info_Integer:
case runtime.Type_Info_Integer:
buf: [21]byte;
u: u64;
switch i in a {
@@ -77,16 +75,16 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
}
s := strconv.append_bits(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil);
write_string(b, s);
strings.write_string(b, s);
case Type_Info_Rune:
case runtime.Type_Info_Rune:
r := a.(rune);
write_byte(b, '"');
write_escaped_rune(b, r, '"', true);
write_byte(b, '"');
strings.write_byte(b, '"');
strings.write_escaped_rune(b, r, '"', true);
strings.write_byte(b, '"');
case Type_Info_Float:
case runtime.Type_Info_Float:
val: f64;
switch f in a {
case f16: val = f64(f);
@@ -107,21 +105,21 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
s = s[1:];
}
write_string(b, string(s));
strings.write_string(b, string(s));
case Type_Info_Complex:
case runtime.Type_Info_Complex:
return .Unsupported_Type;
case Type_Info_Quaternion:
case runtime.Type_Info_Quaternion:
return .Unsupported_Type;
case Type_Info_String:
case runtime.Type_Info_String:
switch s in a {
case string: write_quoted_string(b, s);
case cstring: write_quoted_string(b, string(s));
case string: strings.write_quoted_string(b, s);
case cstring: strings.write_quoted_string(b, string(s));
}
case Type_Info_Boolean:
case runtime.Type_Info_Boolean:
val: bool;
switch b in a {
case bool: val = bool(b);
@@ -130,109 +128,112 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
case b32: val = bool(b);
case b64: val = bool(b);
}
write_string_builder(b, val ? "true" : "false");
strings.write_string(b, val ? "true" : "false");
case Type_Info_Any:
case runtime.Type_Info_Any:
return .Unsupported_Type;
case Type_Info_Type_Id:
case runtime.Type_Info_Type_Id:
return .Unsupported_Type;
case Type_Info_Pointer:
case runtime.Type_Info_Pointer:
return .Unsupported_Type;
case Type_Info_Procedure:
case runtime.Type_Info_Multi_Pointer:
return .Unsupported_Type;
case Type_Info_Tuple:
case runtime.Type_Info_Procedure:
return .Unsupported_Type;
case Type_Info_Enumerated_Array:
case runtime.Type_Info_Tuple:
return .Unsupported_Type;
case Type_Info_Simd_Vector:
case runtime.Type_Info_Enumerated_Array:
return .Unsupported_Type;
case Type_Info_Relative_Pointer:
case runtime.Type_Info_Simd_Vector:
return .Unsupported_Type;
case Type_Info_Relative_Slice:
case runtime.Type_Info_Relative_Pointer:
return .Unsupported_Type;
case Type_Info_Array:
write_byte(b, '[');
case runtime.Type_Info_Relative_Slice:
return .Unsupported_Type;
case runtime.Type_Info_Array:
strings.write_byte(b, '[');
for i in 0..<info.count {
if i > 0 { write_string(b, ", "); }
if i > 0 { strings.write_string(b, ", "); }
data := uintptr(v.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
}
write_byte(b, ']');
strings.write_byte(b, ']');
case Type_Info_Dynamic_Array:
write_byte(b, '[');
case runtime.Type_Info_Dynamic_Array:
strings.write_byte(b, '[');
array := cast(^mem.Raw_Dynamic_Array)v.data;
for i in 0..<array.len {
if i > 0 { write_string(b, ", "); }
if i > 0 { strings.write_string(b, ", "); }
data := uintptr(array.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
}
write_byte(b, ']');
strings.write_byte(b, ']');
case Type_Info_Slice:
write_byte(b, '[');
case runtime.Type_Info_Slice:
strings.write_byte(b, '[');
slice := cast(^mem.Raw_Slice)v.data;
for i in 0..<slice.len {
if i > 0 { write_string(b, ", "); }
if i > 0 { strings.write_string(b, ", "); }
data := uintptr(slice.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
}
write_byte(b, ']');
strings.write_byte(b, ']');
case Type_Info_Map:
case runtime.Type_Info_Map:
m := (^mem.Raw_Map)(v.data);
write_byte(b, '{');
strings.write_byte(b, '{');
if m != nil {
if info.generated_struct == nil {
return .Unsupported_Type;
}
entries := &m.entries;
gs := type_info_base(info.generated_struct).variant.(Type_Info_Struct);
ed := type_info_base(gs.types[1]).variant.(Type_Info_Dynamic_Array);
entry_type := ed.elem.variant.(Type_Info_Struct);
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct);
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array);
entry_type := ed.elem.variant.(runtime.Type_Info_Struct);
entry_size := ed.elem_size;
for i in 0..<entries.len {
if i > 0 { write_string(b, ", "); }
if i > 0 { strings.write_string(b, ", "); }
data := uintptr(entries.data) + uintptr(i*entry_size);
key := rawptr(data + entry_type.offsets[2]);
value := rawptr(data + entry_type.offsets[3]);
marshal_arg(b, any{key, info.key.id});
write_string(b, ": ");
strings.write_string(b, ": ");
marshal_arg(b, any{value, info.value.id});
}
}
write_byte(b, '}');
strings.write_byte(b, '}');
case Type_Info_Struct:
write_byte(b, '{');
case runtime.Type_Info_Struct:
strings.write_byte(b, '{');
for name, i in info.names {
if i > 0 { write_string(b, ", "); }
write_quoted_string(b, name);
write_string(b, ": ");
if i > 0 { strings.write_string(b, ", "); }
strings.write_quoted_string(b, name);
strings.write_string(b, ": ");
id := info.types[i].id;
data := rawptr(uintptr(v.data) + info.offsets[i]);
marshal_arg(b, any{data, id});
}
write_byte(b, '}');
strings.write_byte(b, '}');
case Type_Info_Union:
case runtime.Type_Info_Union:
tag_ptr := uintptr(v.data) + info.tag_offset;
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
@@ -250,16 +251,16 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
}
if v.data == nil || tag == 0 {
write_string(b, "null");
strings.write_string(b, "null");
} else {
id := info.variants[tag-1].id;
marshal_arg(b, any{v.data, id});
}
case Type_Info_Enum:
case runtime.Type_Info_Enum:
return marshal_arg(b, any{v.data, info.base.id});
case Type_Info_Bit_Set:
case runtime.Type_Info_Bit_Set:
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
if ti == nil {
return false;
@@ -306,7 +307,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
bit_data = u64(x);
case: panic("unknown bit_size size");
}
write_u64(b, bit_data);
strings.write_u64(b, bit_data);
return .Unsupported_Type;
+16 -48
View File
@@ -68,36 +68,33 @@ expect_token :: proc(p: ^Parser, kind: Token_Kind) -> Error {
parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
value.pos = p.curr_token.pos;
defer value.end = token_end_pos(p.prev_token);
token := p.curr_token;
#partial switch token.kind {
case .Null:
value.value = Null{};
value = Null{};
advance_token(p);
return;
case .False:
value.value = Boolean(false);
value = Boolean(false);
advance_token(p);
return;
case .True:
value.value = Boolean(true);
value = Boolean(true);
advance_token(p);
return;
case .Integer:
i, _ := strconv.parse_i64(token.text);
value.value = Integer(i);
value = Integer(i);
advance_token(p);
return;
case .Float:
f, _ := strconv.parse_f64(token.text);
value.value = Float(f);
value = Float(f);
advance_token(p);
return;
case .String:
value.value = String(unquote_string(token, p.spec, p.allocator));
value = String(unquote_string(token, p.spec, p.allocator));
advance_token(p);
return;
@@ -115,7 +112,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
if token.text[0] == '-' {
inf = 0xfff0000000000000;
}
value.value = transmute(f64)inf;
value = transmute(f64)inf;
advance_token(p);
return;
case .NaN:
@@ -123,7 +120,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
if token.text[0] == '-' {
nan = 0xfff7ffffffffffff;
}
value.value = transmute(f64)nan;
value = transmute(f64)nan;
advance_token(p);
return;
}
@@ -136,11 +133,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
}
parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
value.pos = p.curr_token.pos;
defer value.end = token_end_pos(p.prev_token);
if err = expect_token(p, .Open_Bracket); err != .None {
return;
}
expect_token(p, .Open_Bracket) or_return;
array: Array;
array.allocator = p.allocator;
@@ -152,11 +145,7 @@ parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
}
for p.curr_token.kind != .Close_Bracket {
elem, elem_err := parse_value(p);
if elem_err != .None {
err = elem_err;
return;
}
elem := parse_value(p) or_return;
append(&array, elem);
// Disallow trailing commas for the time being
@@ -167,11 +156,8 @@ parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
}
}
if err = expect_token(p, .Close_Bracket); err != .None {
return;
}
value.value = array;
expect_token(p, .Close_Bracket) or_return;
value = array;
return;
}
@@ -205,13 +191,7 @@ parse_object_key :: proc(p: ^Parser) -> (key: string, err: Error) {
}
parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
value.pos = p.curr_token.pos;
defer value.end = token_end_pos(p.prev_token);
if err = expect_token(p, .Open_Brace); err != .None {
value.pos = p.curr_token.pos;
return;
}
expect_token(p, .Open_Brace) or_return;
obj: Object;
obj.allocator = p.allocator;
@@ -228,26 +208,18 @@ parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
key, err = parse_object_key(p);
if err != .None {
delete(key, p.allocator);
value.pos = p.curr_token.pos;
return;
}
if colon_err := expect_token(p, .Colon); colon_err != .None {
err = .Expected_Colon_After_Key;
value.pos = p.curr_token.pos;
return;
}
elem, elem_err := parse_value(p);
if elem_err != .None {
err = elem_err;
value.pos = p.curr_token.pos;
return;
}
elem := parse_value(p) or_return;
if key in obj {
err = .Duplicate_Object_Key;
value.pos = p.curr_token.pos;
delete(key, p.allocator);
return;
}
@@ -269,12 +241,8 @@ parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
}
}
if err = expect_token(p, .Close_Brace); err != .None {
value.pos = p.curr_token.pos;
return;
}
value.value = obj;
expect_token(p, .Close_Brace) or_return;
value = obj;
return;
}
+7
View File
@@ -2,6 +2,12 @@ package json
import "core:unicode/utf8"
Pos :: struct {
offset: int,
line: int,
column: int,
}
Token :: struct {
using pos: Pos,
kind: Token_Kind,
@@ -106,6 +112,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
return false;
}
}
return true;
case:
// Ignore the next rune regardless
next_rune(t);
+9 -19
View File
@@ -14,26 +14,16 @@ String :: string;
Array :: distinct [dynamic]Value;
Object :: distinct map[string]Value;
Value :: struct {
pos, end: Pos,
value: union {
Null,
Integer,
Float,
Boolean,
String,
Array,
Object,
},
Value :: union {
Null,
Integer,
Float,
Boolean,
String,
Array,
Object,
}
Pos :: struct {
offset: int,
line: int,
column: int,
}
Error :: enum {
None,
@@ -57,7 +47,7 @@ Error :: enum {
destroy_value :: proc(value: Value) {
#partial switch v in value.value {
#partial switch v in value {
case Object:
for key, elem in v {
delete(key);
+48 -7
View File
@@ -10,7 +10,7 @@ import "core:strconv"
import "core:strings"
import "core:time"
import "core:unicode/utf8"
import "intrinsics"
import "core:intrinsics"
Info :: struct {
minus: bool,
@@ -27,6 +27,7 @@ Info :: struct {
reordered: bool,
good_arg_index: bool,
ignore_user_formatters: bool,
writer: io.Writer,
arg: any, // Temporary
@@ -93,11 +94,6 @@ eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr,
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args); }
@(deprecated="prefer eprint") print_err :: proc(args: ..any) -> int { return eprint(..args); }
@(deprecated="prefer eprintf") printf_err :: proc(fmt: string, args: ..any) -> int { return eprintf(fmt, ..args); }
@(deprecated="prefer eprintln") println_err :: proc(args: ..any) -> int { return eprintln(..args); }
// aprint* procedures return a string that was allocated with the current context
// They must be freed accordingly
aprint :: proc(args: ..any, sep := " ") -> string {
@@ -1267,15 +1263,18 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
return;
}
if _user_formatters != nil {
if _user_formatters != nil && !fi.ignore_user_formatters {
formatter := _user_formatters[v.id];
if formatter != nil {
fi.ignore_user_formatters = false;
if ok := formatter(fi, v, verb); !ok {
fi.ignore_user_formatters = true;
fmt_bad_verb(fi, verb);
}
return;
}
}
fi.ignore_user_formatters = false;
type_info := type_info_of(v.id);
switch info in type_info.variant {
@@ -1570,6 +1569,48 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fmt_pointer(fi, ptr, verb);
}
case runtime.Type_Info_Multi_Pointer:
ptr := (^rawptr)(v.data)^;
if verb != 'p' && info.elem != nil {
a := any{ptr, info.elem.id};
elem := runtime.type_info_base(info.elem);
if elem != nil {
#partial switch e in elem.variant {
case runtime.Type_Info_Array,
runtime.Type_Info_Slice,
runtime.Type_Info_Dynamic_Array,
runtime.Type_Info_Map:
if ptr == nil {
io.write_string(fi.writer, "<nil>");
return;
}
if fi.record_level < 1 {
fi.record_level += 1;
defer fi.record_level -= 1;
io.write_byte(fi.writer, '&');
fmt_value(fi, a, verb);
return;
}
case runtime.Type_Info_Struct,
runtime.Type_Info_Union:
if ptr == nil {
io.write_string(fi.writer, "<nil>");
return;
}
if fi.record_level < 1 {
fi.record_level += 1;
defer fi.record_level -= 1;
io.write_byte(fi.writer, '&');
fmt_value(fi, a, verb);
return;
}
}
}
}
fmt_pointer(fi, ptr, verb);
case runtime.Type_Info_Array:
if (verb == 's' || verb == 'q') && reflect.is_byte(info.elem) {
s := strings.string_from_ptr((^byte)(v.data), info.count);
+1 -1
View File
@@ -1,6 +1,6 @@
package hash
import "intrinsics"
import "core:intrinsics"
@(optimization_mode="speed")
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
+1 -1
View File
@@ -1,7 +1,7 @@
package hash
import "core:mem"
import "intrinsics"
import "core:intrinsics"
@(optimization_mode="speed")
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
+9 -30
View File
@@ -372,8 +372,7 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont
defer delete(data);
if ok {
img, err = load_from_slice(data, options, allocator);
return;
return load_from_slice(data, options, allocator);
} else {
img = new(Image);
return img, E_General.File_Not_Found;
@@ -453,10 +452,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
seen_ihdr = true;
header, err = read_header(ctx);
if err != nil {
return img, err;
}
header = read_header(ctx) or_return;
if .Paletted in header.color_type {
// Color type 3
@@ -506,10 +502,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return img, E_PNG.PLTE_Encountered_Unexpectedly;
}
c, err = read_chunk(ctx);
if err != nil {
return img, err;
}
c = read_chunk(ctx) or_return;
if c.header.length % 3 != 0 || c.header.length > 768 {
return img, E_PNG.PLTE_Invalid_Length;
@@ -540,10 +533,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
next := ch.type;
for next == .IDAT {
c, err = read_chunk(ctx);
if err != nil {
return img, err;
}
c = read_chunk(ctx) or_return;
bytes.buffer_write(&idat_b, c.data);
idat_length += c.header.length;
@@ -560,19 +550,13 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
seen_idat = true;
case .IEND:
c, err = read_chunk(ctx);
if err != nil {
return img, err;
}
c = read_chunk(ctx) or_return;
seen_iend = true;
case .bKGD:
// TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be
c, err = read_chunk(ctx);
if err != nil {
return img, err;
}
c = read_chunk(ctx) or_return;
seen_bkgd = true;
if .return_metadata in options {
append(&info.chunks, c);
@@ -604,10 +588,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])};
}
case .tRNS:
c, err = read_chunk(ctx);
if err != nil {
return img, err;
}
c = read_chunk(ctx) or_return;
if .Alpha in info.header.color_type {
return img, E_PNG.TRNS_Encountered_Unexpectedly;
@@ -646,10 +627,8 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return img, E_PNG.PNG_Does_Not_Adhere_to_Spec;
case:
// Unhandled type
c, err = read_chunk(ctx);
if err != nil {
return img, err;
}
c = read_chunk(ctx) or_return;
if .return_metadata in options {
// NOTE: Chunk cata is currently allocated on the temp allocator.
append(&info.chunks, c);
+1 -1
View File
@@ -1,6 +1,6 @@
package io
import "intrinsics"
import "core:intrinsics"
import "core:runtime"
import "core:unicode/utf8"
+18
View File
@@ -1,7 +1,25 @@
package io
import "core:mem"
import "core:strconv"
read_ptr :: proc(r: Reader, p: rawptr, byte_size: int) -> (n: int, err: Error) {
return read(r, mem.byte_slice(p, byte_size));
}
write_ptr :: proc(w: Writer, p: rawptr, byte_size: int) -> (n: int, err: Error) {
return write(w, mem.byte_slice(p, byte_size));
}
read_ptr_at :: proc(r: Reader_At, p: rawptr, byte_size: int, offset: i64) -> (n: int, err: Error) {
return read_at(r, mem.byte_slice(p, byte_size), offset);
}
write_ptr_at :: proc(w: Writer_At, p: rawptr, byte_size: int, offset: i64) -> (n: int, err: Error) {
return write_at(w, mem.byte_slice(p, byte_size), offset);
}
write_u64 :: proc(w: Writer, i: u64, base: int = 10) -> (n: int, err: Error) {
buf: [32]byte;
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil);
+152
View File
@@ -0,0 +1,152 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
An arbitrary precision mathematics implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
This file collects public proc maps and their aliases.
=== === === === === === === === === === === === === === === === === === === === === === === ===
Basic arithmetic.
See `public.odin`.
=== === === === === === === === === === === === === === === === === === === === === === === ===
*/
/*
High-level addition. Handles sign.
*/
add :: proc {
/*
int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error)
*/
int_add,
/*
Adds the unsigned `DIGIT` immediate to an `Int`, such that the
`DIGIT` doesn't have to be turned into an `Int` first.
int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error)
*/
int_add_digit,
};
/*
err = sub(dest, a, b);
*/
sub :: proc {
/*
int_sub :: proc(dest, a, b: ^Int) -> (err: Error)
*/
int_sub,
/*
int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error)
*/
int_sub_digit,
};
/*
=== === === === === === === === === === === === === === === === === === === === === === === ===
Comparisons.
See `compare.odin`.
=== === === === === === === === === === === === === === === === === === === === === === === ===
*/
is_initialized :: proc {
/*
int_is_initialized :: proc(a: ^Int) -> bool
*/
int_is_initialized,
};
is_zero :: proc {
/*
int_is_zero :: proc(a: ^Int) -> bool
*/
int_is_zero,
};
is_positive :: proc {
/*
int_is_positive :: proc(a: ^Int) -> bool
*/
int_is_positive,
};
is_pos :: is_positive;
is_negative :: proc {
/*
int_is_negative :: proc(a: ^Int) -> bool
*/
int_is_negative,
};
is_neg :: is_negative;
is_even :: proc {
/*
int_is_even :: proc(a: ^Int) -> bool
*/
int_is_even,
};
is_odd :: proc {
/*
int_is_odd :: proc(a: ^Int) -> bool
*/
int_is_odd,
};
is_power_of_two :: proc {
/*
platform_int_is_power_of_two :: proc(a: int) -> bool
*/
platform_int_is_power_of_two,
/*
int_is_power_of_two :: proc(a: ^Int) -> (res: bool)
*/
int_is_power_of_two,
};
compare :: proc {
/*
Compare two `Int`s, signed.
int_compare :: proc(a, b: ^Int) -> Comparison_Flag
*/
int_compare,
/*
Compare an `Int` to an unsigned number upto the size of the backing type.
int_compare_digit :: proc(a: ^Int, u: DIGIT) -> Comparison_Flag
*/
int_compare_digit,
};
cmp :: compare;
compare_magnitude :: proc {
/*
Compare the magnitude of two `Int`s, unsigned.
*/
int_compare_magnitude,
};
cmp_mag :: compare_magnitude;
/*
=== === === === === === === === === === === === === === === === === === === === === === === ===
Initialization and other helpers.
See `helpers.odin`.
=== === === === === === === === === === === === === === === === === === === === === === === ===
*/
destroy :: proc {
/*
Clears one or more `Int`s and dellocates their backing memory.
int_destroy :: proc(integers: ..^Int)
*/
int_destroy,
};
+9
View File
@@ -0,0 +1,9 @@
@echo off
:odin run . -vet
set TEST_ARGS=-fast-tests
:odin build . -build-mode:shared -show-timings -o:minimal -no-bounds-check -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS%
odin build . -build-mode:shared -show-timings -o:size -no-bounds-check -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS%
:odin build . -build-mode:shared -show-timings -o:size -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS%
:odin build . -build-mode:shared -show-timings -o:speed -no-bounds-check -define:MATH_BIG_EXE=false && python test.py %TEST_ARGS%
:odin build . -build-mode:shared -show-timings -o:speed -define:MATH_BIG_EXE=false && python test.py -fast-tests %TEST_ARGS%
+218
View File
@@ -0,0 +1,218 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
An arbitrary precision mathematics implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
import "core:intrinsics"
/*
TODO: Make the tunables runtime adjustable where practical.
This allows to benchmark and/or setting optimized values for a certain CPU without recompiling.
*/
/*
========================== TUNABLES ==========================
`initialize_constants` returns `#config(MUL_KARATSUBA_CUTOFF, _DEFAULT_MUL_KARATSUBA_CUTOFF)`
and we initialize this cutoff that way so that the procedure is used and called,
because it handles initializing the constants ONE, ZERO, MINUS_ONE, NAN and INF.
`initialize_constants` also replaces the other `_DEFAULT_*` cutoffs with custom compile-time values if so `#config`ured.
*/
/*
There is a bug with DLL globals. They don't get set.
To allow tests to run we add `-define:MATH_BIG_EXE=false` to hardcode the cutoffs for now.
*/
when #config(MATH_BIG_EXE, true) {
MUL_KARATSUBA_CUTOFF := initialize_constants();
SQR_KARATSUBA_CUTOFF := _DEFAULT_SQR_KARATSUBA_CUTOFF;
MUL_TOOM_CUTOFF := _DEFAULT_MUL_TOOM_CUTOFF;
SQR_TOOM_CUTOFF := _DEFAULT_SQR_TOOM_CUTOFF;
} else {
MUL_KARATSUBA_CUTOFF := _DEFAULT_MUL_KARATSUBA_CUTOFF;
SQR_KARATSUBA_CUTOFF := _DEFAULT_SQR_KARATSUBA_CUTOFF;
MUL_TOOM_CUTOFF := _DEFAULT_MUL_TOOM_CUTOFF;
SQR_TOOM_CUTOFF := _DEFAULT_SQR_TOOM_CUTOFF;
}
/*
These defaults were tuned on an AMD A8-6600K (64-bit) using libTomMath's `make tune`.
TODO(Jeroen): Port this tuning algorithm and tune them for more modern processors.
It would also be cool if we collected some data across various processor families.
This would let uss set reasonable defaults at runtime as this library initializes
itself by using `cpuid` or the ARM equivalent.
IMPORTANT: The 32_BIT path has largely gone untested. It needs to be tested and
debugged where necessary.
*/
_DEFAULT_MUL_KARATSUBA_CUTOFF :: #config(MUL_KARATSUBA_CUTOFF, 80);
_DEFAULT_SQR_KARATSUBA_CUTOFF :: #config(SQR_KARATSUBA_CUTOFF, 120);
_DEFAULT_MUL_TOOM_CUTOFF :: #config(MUL_TOOM_CUTOFF, 350);
_DEFAULT_SQR_TOOM_CUTOFF :: #config(SQR_TOOM_CUTOFF, 400);
MAX_ITERATIONS_ROOT_N := 500;
/*
Largest `N` for which we'll compute `N!`
*/
FACTORIAL_MAX_N := 1_000_000;
/*
Cutoff to switch to int_factorial_binary_split, and its max recursion level.
*/
FACTORIAL_BINARY_SPLIT_CUTOFF := 6100;
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS := 100;
/*
We don't allow these to be switched at runtime for two reasons:
1) 32-bit and 64-bit versions of procedures use different types for their storage,
so we'd have to double the number of procedures, and they couldn't interact.
2) Optimizations thanks to precomputed masks wouldn't work.
*/
MATH_BIG_FORCE_64_BIT :: #config(MATH_BIG_FORCE_64_BIT, false);
MATH_BIG_FORCE_32_BIT :: #config(MATH_BIG_FORCE_32_BIT, false);
when (MATH_BIG_FORCE_32_BIT && MATH_BIG_FORCE_64_BIT) { #panic("Cannot force 32-bit and 64-bit big backend simultaneously."); };
_LOW_MEMORY :: #config(BIGINT_SMALL_MEMORY, false);
when _LOW_MEMORY {
_DEFAULT_DIGIT_COUNT :: 8;
} else {
_DEFAULT_DIGIT_COUNT :: 32;
}
/*
======================= END OF TUNABLES =======================
*/
Sign :: enum u8 {
Zero_or_Positive = 0,
Negative = 1,
};
Int :: struct {
used: int,
digit: [dynamic]DIGIT,
sign: Sign,
flags: Flags,
};
Flag :: enum u8 {
NaN,
Inf,
Immutable,
};
Flags :: bit_set[Flag; u8];
/*
Errors are a strict superset of runtime.Allocation_Error.
*/
Error :: enum int {
Okay = 0,
Out_Of_Memory = 1,
Invalid_Pointer = 2,
Invalid_Argument = 3,
Assignment_To_Immutable = 4,
Max_Iterations_Reached = 5,
Buffer_Overflow = 6,
Integer_Overflow = 7,
Division_by_Zero = 8,
Math_Domain_Error = 9,
Unimplemented = 127,
};
Error_String :: #partial [Error]string{
.Out_Of_Memory = "Out of memory",
.Invalid_Pointer = "Invalid pointer",
.Invalid_Argument = "Invalid argument",
.Assignment_To_Immutable = "Assignment to immutable",
.Max_Iterations_Reached = "Max iterations reached",
.Buffer_Overflow = "Buffer overflow",
.Integer_Overflow = "Integer overflow",
.Division_by_Zero = "Division by zero",
.Math_Domain_Error = "Math domain error",
.Unimplemented = "Unimplemented",
};
Primality_Flag :: enum u8 {
Blum_Blum_Shub = 0, /* BBS style prime */
Safe = 1, /* Safe prime (p-1)/2 == prime */
Second_MSB_On = 3, /* force 2nd MSB to 1 */
};
Primality_Flags :: bit_set[Primality_Flag; u8];
/*
How do we store the Ints?
Minimum number of available digits in `Int`, `_DEFAULT_DIGIT_COUNT` >= `_MIN_DIGIT_COUNT`
- Must be at least 3 for `_div_school`.
- Must be large enough such that `init_integer` can store `u128` in the `Int` without growing.
*/
_MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS);
#assert(_DEFAULT_DIGIT_COUNT >= _MIN_DIGIT_COUNT);
/*
Maximum number of digits.
- Must be small enough such that `_bit_count` does not overflow.
- Must be small enough such that `_radix_size` for base 2 does not overflow.
`_radix_size` needs two additional bytes for zero termination and sign.
*/
_MAX_BIT_COUNT :: (max(int) - 2);
_MAX_DIGIT_COUNT :: _MAX_BIT_COUNT / _DIGIT_BITS;
when MATH_BIG_FORCE_64_BIT || (!MATH_BIG_FORCE_32_BIT && size_of(rawptr) == 8) {
/*
We can use u128 as an intermediary.
*/
DIGIT :: distinct u64;
_WORD :: distinct u128;
} else {
DIGIT :: distinct u32;
_WORD :: distinct u64;
}
#assert(size_of(_WORD) == 2 * size_of(DIGIT));
_DIGIT_TYPE_BITS :: 8 * size_of(DIGIT);
_WORD_TYPE_BITS :: 8 * size_of(_WORD);
_DIGIT_BITS :: _DIGIT_TYPE_BITS - 4;
_WORD_BITS :: 2 * _DIGIT_BITS;
_MASK :: (DIGIT(1) << DIGIT(_DIGIT_BITS)) - DIGIT(1);
_DIGIT_MAX :: _MASK;
_MAX_COMBA :: 1 << (_WORD_TYPE_BITS - (2 * _DIGIT_BITS)) ;
_WARRAY :: 1 << ((_WORD_TYPE_BITS - (2 * _DIGIT_BITS)) + 1);
Order :: enum i8 {
LSB_First = -1,
MSB_First = 1,
};
Endianness :: enum i8 {
Little = -1,
Platform = 0,
Big = 1,
};
+232
View File
@@ -0,0 +1,232 @@
//+ignore
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
import "core:fmt"
import "core:mem"
print_configation :: proc() {
fmt.printf(
`
Configuration:
_DIGIT_BITS %v
_MIN_DIGIT_COUNT %v
_MAX_DIGIT_COUNT %v
_DEFAULT_DIGIT_COUNT %v
_MAX_COMBA %v
_WARRAY %v
Runtime tunable:
MUL_KARATSUBA_CUTOFF %v
SQR_KARATSUBA_CUTOFF %v
MUL_TOOM_CUTOFF %v
SQR_TOOM_CUTOFF %v
MAX_ITERATIONS_ROOT_N %v
FACTORIAL_MAX_N %v
FACTORIAL_BINARY_SPLIT_CUTOFF %v
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS %v
`, _DIGIT_BITS,
_MIN_DIGIT_COUNT,
_MAX_DIGIT_COUNT,
_DEFAULT_DIGIT_COUNT,
_MAX_COMBA,
_WARRAY,
MUL_KARATSUBA_CUTOFF,
SQR_KARATSUBA_CUTOFF,
MUL_TOOM_CUTOFF,
SQR_TOOM_CUTOFF,
MAX_ITERATIONS_ROOT_N,
FACTORIAL_MAX_N,
FACTORIAL_BINARY_SPLIT_CUTOFF,
FACTORIAL_BINARY_SPLIT_MAX_RECURSIONS,
);
}
print :: proc(name: string, a: ^Int, base := i8(10), print_name := true, newline := true, print_extra_info := false) {
assert_if_nil(a);
as, err := itoa(a, base);
defer delete(as);
cb := internal_count_bits(a);
if print_name {
fmt.printf("%v", name);
}
if err != nil {
fmt.printf("%v (error: %v | %v)", name, err, a);
}
fmt.printf("%v", as);
if print_extra_info {
fmt.printf(" (base: %v, bits: %v (digits: %v), flags: %v)", base, cb, a.used, a.flags);
}
if newline {
fmt.println();
}
}
int_to_byte :: proc(v: ^Int) {
err: Error;
size: int;
print("v: ", v);
fmt.println();
t := &Int{};
defer destroy(t);
if size, err = int_to_bytes_size(v); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err);
return;
}
b1 := make([]u8, size, context.temp_allocator);
err = int_to_bytes_big(v, b1);
int_from_bytes_big(t, b1);
fmt.printf("big: %v | err: %v\n", b1, err);
int_from_bytes_big(t, b1);
if internal_cmp_mag(t, v) != 0 {
print("\tError parsing t: ", t);
}
if size, err = int_to_bytes_size(v); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err);
return;
}
b2 := make([]u8, size, context.temp_allocator);
err = int_to_bytes_big_python(v, b2);
fmt.printf("big python: %v | err: %v\n", b2, err);
if err == nil {
int_from_bytes_big_python(t, b2);
if internal_cmp_mag(t, v) != 0 {
print("\tError parsing t: ", t);
}
}
if size, err = int_to_bytes_size(v, true); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err);
return;
}
b3 := make([]u8, size, context.temp_allocator);
err = int_to_bytes_big(v, b3, true);
fmt.printf("big signed: %v | err: %v\n", b3, err);
int_from_bytes_big(t, b3, true);
if internal_cmp(t, v) != 0 {
print("\tError parsing t: ", t);
}
if size, err = int_to_bytes_size(v, true); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err);
return;
}
b4 := make([]u8, size, context.temp_allocator);
err = int_to_bytes_big_python(v, b4, true);
fmt.printf("big signed python: %v | err: %v\n", b4, err);
int_from_bytes_big_python(t, b4, true);
if internal_cmp(t, v) != 0 {
print("\tError parsing t: ", t);
}
}
int_to_byte_little :: proc(v: ^Int) {
err: Error;
size: int;
print("v: ", v);
fmt.println();
t := &Int{};
defer destroy(t);
if size, err = int_to_bytes_size(v); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err);
return;
}
b1 := make([]u8, size, context.temp_allocator);
err = int_to_bytes_little(v, b1);
fmt.printf("little: %v | err: %v\n", b1, err);
int_from_bytes_little(t, b1);
if internal_cmp_mag(t, v) != 0 {
print("\tError parsing t: ", t);
}
if size, err = int_to_bytes_size(v); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err);
return;
}
b2 := make([]u8, size, context.temp_allocator);
err = int_to_bytes_little_python(v, b2);
fmt.printf("little python: %v | err: %v\n", b2, err);
if err == nil {
int_from_bytes_little_python(t, b2);
if internal_cmp_mag(t, v) != 0 {
print("\tError parsing t: ", t);
}
}
if size, err = int_to_bytes_size(v, true); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err);
return;
}
b3 := make([]u8, size, context.temp_allocator);
err = int_to_bytes_little(v, b3, true);
fmt.printf("little signed: %v | err: %v\n", b3, err);
int_from_bytes_little(t, b3, true);
if internal_cmp(t, v) != 0 {
print("\tError parsing t: ", t);
}
if size, err = int_to_bytes_size(v, true); err != nil {
fmt.printf("int_to_bytes_size returned: %v\n", err);
return;
}
b4 := make([]u8, size, context.temp_allocator);
err = int_to_bytes_little_python(v, b4, true);
fmt.printf("little signed python: %v | err: %v\n", b4, err);
int_from_bytes_little_python(t, b4, true);
if internal_cmp(t, v) != 0 {
print("\tError parsing t: ", t);
}
}
demo :: proc() {
a, b, c, d, e, f := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
defer destroy(a, b, c, d, e, f);
}
main :: proc() {
ta := mem.Tracking_Allocator{};
mem.tracking_allocator_init(&ta, context.allocator);
context.allocator = mem.tracking_allocator(&ta);
demo();
print_configation();
print_timings();
if len(ta.allocation_map) > 0 {
for _, v in ta.allocation_map {
fmt.printf("Leaked %v bytes @ %v\n", v.size, v.location);
}
}
if len(ta.bad_free_array) > 0 {
fmt.println("Bad frees:");
for v in ta.bad_free_array {
fmt.println(v);
}
}
}
+800
View File
@@ -0,0 +1,800 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
An arbitrary precision mathematics implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
import "core:intrinsics"
import rnd "core:math/rand"
// import "core:fmt"
/*
TODO: Int.flags and Constants like ONE, NAN, etc, are not yet properly handled everywhere.
*/
/*
Deallocates the backing memory of one or more `Int`s.
*/
int_destroy :: proc(integers: ..^Int) {
integers := integers;
for a in &integers {
assert_if_nil(a);
}
#force_inline internal_int_destroy(..integers);
}
/*
Helpers to set an `Int` to a specific value.
*/
int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, allocator := context.allocator) -> (err: Error)
where intrinsics.type_is_integer(T) {
context.allocator = allocator;
src := src;
/*
Check that `src` is usable and `dest` isn't immutable.
*/
assert_if_nil(dest);
#force_inline internal_error_if_immutable(dest) or_return;
return #force_inline internal_int_set_from_integer(dest, src, minimize);
}
set :: proc { int_set_from_integer, int_copy, int_atoi, };
/*
Copy one `Int` to another.
*/
int_copy :: proc(dest, src: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) {
/*
If dest == src, do nothing
*/
if (dest == src) { return nil; }
/*
Check that `src` is usable and `dest` isn't immutable.
*/
assert_if_nil(dest, src);
context.allocator = allocator;
#force_inline internal_clear_if_uninitialized(src) or_return;
#force_inline internal_error_if_immutable(dest) or_return;
return #force_inline internal_int_copy(dest, src, minimize);
}
copy :: proc { int_copy, };
/*
In normal code, you can also write `a, b = b, a`.
However, that only swaps within the current scope.
This helper swaps completely.
*/
int_swap :: proc(a, b: ^Int) {
assert_if_nil(a, b);
#force_inline internal_swap(a, b);
}
swap :: proc { int_swap, };
/*
Set `dest` to |`src`|.
*/
int_abs :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
/*
Check that `src` is usable and `dest` isn't immutable.
*/
assert_if_nil(dest, src);
context.allocator = allocator;
#force_inline internal_clear_if_uninitialized(src) or_return;
#force_inline internal_error_if_immutable(dest) or_return;
return #force_inline internal_int_abs(dest, src);
}
platform_abs :: proc(n: $T) -> T where intrinsics.type_is_integer(T) {
return n if n >= 0 else -n;
}
abs :: proc{ int_abs, platform_abs, };
/*
Set `dest` to `-src`.
*/
int_neg :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
/*
Check that `src` is usable and `dest` isn't immutable.
*/
assert_if_nil(dest, src);
context.allocator = allocator;
#force_inline internal_clear_if_uninitialized(src) or_return;
#force_inline internal_error_if_immutable(dest) or_return;
return #force_inline internal_int_neg(dest, src);
}
neg :: proc { int_neg, };
/*
Helpers to extract values from the `Int`.
*/
int_bitfield_extract_single :: proc(a: ^Int, offset: int, allocator := context.allocator) -> (bit: _WORD, err: Error) {
return #force_inline int_bitfield_extract(a, offset, 1, allocator);
}
int_bitfield_extract :: proc(a: ^Int, offset, count: int, allocator := context.allocator) -> (res: _WORD, err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
context.allocator = allocator;
#force_inline internal_clear_if_uninitialized(a) or_return;
return #force_inline internal_int_bitfield_extract(a, offset, count);
}
/*
Resize backing store.
*/
shrink :: proc(a: ^Int, allocator := context.allocator) -> (err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
context.allocator = allocator;
#force_inline internal_clear_if_uninitialized(a) or_return;
return #force_inline internal_shrink(a);
}
int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator := context.allocator) -> (err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return #force_inline internal_int_grow(a, digits, allow_shrink, allocator);
}
grow :: proc { int_grow, };
/*
Clear `Int` and resize it to the default size.
*/
int_clear :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return #force_inline internal_int_clear(a, minimize, allocator);
}
clear :: proc { int_clear, };
zero :: clear;
/*
Set the `Int` to 1 and optionally shrink it to the minimum backing size.
*/
int_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return #force_inline internal_one(a, minimize, allocator);
}
one :: proc { int_one, };
/*
Set the `Int` to -1 and optionally shrink it to the minimum backing size.
*/
int_minus_one :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return #force_inline internal_minus_one(a, minimize, allocator);
}
minus_one :: proc { int_minus_one, };
/*
Set the `Int` to Inf and optionally shrink it to the minimum backing size.
*/
int_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return #force_inline internal_inf(a, minimize, allocator);
}
inf :: proc { int_inf, };
/*
Set the `Int` to -Inf and optionally shrink it to the minimum backing size.
*/
int_minus_inf :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return #force_inline internal_minus_inf(a, minimize, allocator);
}
minus_inf :: proc { int_inf, };
/*
Set the `Int` to NaN and optionally shrink it to the minimum backing size.
*/
int_nan :: proc(a: ^Int, minimize := false, allocator := context.allocator) -> (err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return #force_inline internal_nan(a, minimize, allocator);
}
nan :: proc { int_nan, };
power_of_two :: proc(a: ^Int, power: int, allocator := context.allocator) -> (err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return #force_inline internal_int_power_of_two(a, power, allocator);
}
int_get_u128 :: proc(a: ^Int, allocator := context.allocator) -> (res: u128, err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return int_get(a, u128, allocator);
}
get_u128 :: proc { int_get_u128, };
int_get_i128 :: proc(a: ^Int, allocator := context.allocator) -> (res: i128, err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return int_get(a, i128, allocator);
}
get_i128 :: proc { int_get_i128, };
int_get_u64 :: proc(a: ^Int, allocator := context.allocator) -> (res: u64, err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return int_get(a, u64, allocator);
}
get_u64 :: proc { int_get_u64, };
int_get_i64 :: proc(a: ^Int, allocator := context.allocator) -> (res: i64, err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return int_get(a, i64, allocator);
}
get_i64 :: proc { int_get_i64, };
int_get_u32 :: proc(a: ^Int, allocator := context.allocator) -> (res: u32, err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return int_get(a, u32, allocator);
}
get_u32 :: proc { int_get_u32, };
int_get_i32 :: proc(a: ^Int, allocator := context.allocator) -> (res: i32, err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
return int_get(a, i32, allocator);
}
get_i32 :: proc { int_get_i32, };
/*
TODO: Think about using `count_bits` to check if the value could be returned completely,
and maybe return max(T), .Integer_Overflow if not?
*/
int_get :: proc(a: ^Int, $T: typeid, allocator := context.allocator) -> (res: T, err: Error) where intrinsics.type_is_integer(T) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
#force_inline internal_clear_if_uninitialized(a, allocator) or_return;
return #force_inline internal_int_get(a, T);
}
get :: proc { int_get, };
int_get_float :: proc(a: ^Int, allocator := context.allocator) -> (res: f64, err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
#force_inline internal_clear_if_uninitialized(a, allocator) or_return;
return #force_inline internal_int_get_float(a);
}
/*
Count bits in an `Int`.
*/
count_bits :: proc(a: ^Int, allocator := context.allocator) -> (count: int, err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
#force_inline internal_clear_if_uninitialized(a, allocator) or_return;
return #force_inline internal_count_bits(a), nil;
}
/*
Returns the number of trailing zeroes before the first one.
Differs from regular `ctz` in that 0 returns 0.
*/
int_count_lsb :: proc(a: ^Int, allocator := context.allocator) -> (count: int, err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(a);
#force_inline internal_clear_if_uninitialized(a, allocator) or_return;
return #force_inline internal_int_count_lsb(a);
}
platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
where intrinsics.type_is_integer(T) && intrinsics.type_is_unsigned(T) {
return int(intrinsics.count_trailing_zeros(a)) if a > 0 else 0;
}
count_lsb :: proc { int_count_lsb, platform_count_lsb, };
int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
when _DIGIT_BITS == 60 { // DIGIT = u64
return DIGIT(rnd.uint64(r)) & _MASK;
} else when _DIGIT_BITS == 28 { // DIGIT = u32
return DIGIT(rnd.uint32(r)) & _MASK;
} else {
panic("Unsupported DIGIT size.");
}
return 0; // We shouldn't get here.
}
int_rand :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
/*
Check that `a` is usable.
*/
assert_if_nil(dest);
return #force_inline internal_int_rand(dest, bits, r, allocator);
}
rand :: proc { int_rand, };
/*
Internal helpers.
*/
assert_initialized :: proc(a: ^Int, loc := #caller_location) {
assert_if_nil(a);
assert(is_initialized(a), "`Int` was not properly initialized.", loc);
}
zero_unused :: proc(dest: ^Int, old_used := -1) {
assert_if_nil(dest);
if ! #force_inline is_initialized(dest) { return; }
#force_inline internal_zero_unused(dest, old_used);
}
clear_if_uninitialized_single :: proc(arg: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(arg);
return #force_inline internal_clear_if_uninitialized_single(arg, allocator);
}
clear_if_uninitialized_multi :: proc(args: ..^Int, allocator := context.allocator) -> (err: Error) {
args := args;
assert_if_nil(..args);
for i in &args {
#force_inline internal_clear_if_uninitialized_single(i, allocator) or_return;
}
return err;
}
clear_if_uninitialized :: proc {clear_if_uninitialized_single, clear_if_uninitialized_multi, };
error_if_immutable_single :: proc(arg: ^Int) -> (err: Error) {
if arg != nil && .Immutable in arg.flags { return .Assignment_To_Immutable; }
return nil;
}
error_if_immutable_multi :: proc(args: ..^Int) -> (err: Error) {
for i in args {
if i != nil && .Immutable in i.flags { return .Assignment_To_Immutable; }
}
return nil;
}
error_if_immutable :: proc {error_if_immutable_single, error_if_immutable_multi, };
/*
Allocates several `Int`s at once.
*/
int_init_multi :: proc(integers: ..^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(..integers);
integers := integers;
for a in &integers {
#force_inline internal_clear(a, true, allocator) or_return;
}
return nil;
}
init_multi :: proc { int_init_multi, };
copy_digits :: proc(dest, src: ^Int, digits: int, offset := int(0), allocator := context.allocator) -> (err: Error) {
context.allocator = allocator;
/*
Check that `src` is usable and `dest` isn't immutable.
*/
assert_if_nil(dest, src);
#force_inline internal_clear_if_uninitialized(src) or_return;
return #force_inline internal_copy_digits(dest, src, digits, offset);
}
/*
Trim unused digits.
This is used to ensure that leading zero digits are trimmed and the leading "used" digit will be non-zero.
Typically very fast. Also fixes the sign if there are no more leading digits.
*/
clamp :: proc(a: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(a);
#force_inline internal_clear_if_uninitialized(a, allocator) or_return;
for a.used > 0 && a.digit[a.used - 1] == 0 {
a.used -= 1;
}
if z, _ := is_zero(a); z {
a.sign = .Zero_or_Positive;
}
return nil;
}
/*
Size binary representation
*/
int_to_bytes_size :: proc(a: ^Int, signed := false, allocator := context.allocator) -> (size_in_bytes: int, err: Error) {
assert_if_nil(a);
#force_inline internal_clear_if_uninitialized(a, allocator) or_return;
size_in_bits := internal_count_bits(a);
size_in_bytes = (size_in_bits / 8);
size_in_bytes += 0 if size_in_bits % 8 == 0 else 1;
size_in_bytes += 1 if signed else 0;
return;
}
/*
Return Little Endian binary representation of `a`, either signed or unsigned.
If `a` is negative and we ask for the default unsigned representation, we return abs(a).
*/
int_to_bytes_little :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
assert_if_nil(a);
size_in_bytes := int_to_bytes_size(a, signed, allocator) or_return;
l := len(buf);
if size_in_bytes > l { return .Buffer_Overflow; }
size_in_bits := internal_count_bits(a);
i := 0;
if signed {
buf[l - 1] = 1 if a.sign == .Negative else 0;
}
for offset := 0; offset < size_in_bits; offset += 8 {
bits, _ := internal_int_bitfield_extract(a, offset, 8);
buf[i] = u8(bits & 255); i += 1;
}
return;
}
/*
Return Big Endian binary representation of `a`, either signed or unsigned.
If `a` is negative and we ask for the default unsigned representation, we return abs(a).
*/
int_to_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
assert_if_nil(a);
size_in_bytes := int_to_bytes_size(a, signed, allocator) or_return;
l := len(buf);
if size_in_bytes > l { return .Buffer_Overflow; }
size_in_bits := internal_count_bits(a);
i := l - 1;
if signed {
buf[0] = 1 if a.sign == .Negative else 0;
}
for offset := 0; offset < size_in_bits; offset += 8 {
bits, _ := internal_int_bitfield_extract(a, offset, 8);
buf[i] = u8(bits & 255); i -= 1;
}
return;
}
/*
Return Python 3.x compatible Little Endian binary representation of `a`, either signed or unsigned.
If `a` is negative when asking for an unsigned number, we return an error like Python does.
*/
int_to_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
assert_if_nil(a);
if !signed && a.sign == .Negative { return .Invalid_Argument; }
l := len(buf);
size_in_bytes := int_to_bytes_size(a, signed, allocator) or_return;
if size_in_bytes > l { return .Buffer_Overflow; }
if a.sign == .Negative {
t := &Int{};
defer destroy(t);
internal_complement(t, a, allocator) or_return;
size_in_bits := internal_count_bits(t);
i := 0;
for offset := 0; offset < size_in_bits; offset += 8 {
bits, _ := internal_int_bitfield_extract(t, offset, 8);
buf[i] = 255 - u8(bits & 255); i += 1;
}
buf[l-1] = 255;
} else {
size_in_bits := internal_count_bits(a);
i := 0;
for offset := 0; offset < size_in_bits; offset += 8 {
bits, _ := internal_int_bitfield_extract(a, offset, 8);
buf[i] = u8(bits & 255); i += 1;
}
}
return;
}
/*
Return Python 3.x compatible Big Endian binary representation of `a`, either signed or unsigned.
If `a` is negative when asking for an unsigned number, we return an error like Python does.
*/
int_to_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
assert_if_nil(a);
if !signed && a.sign == .Negative { return .Invalid_Argument; }
if a.sign == .Zero_or_Positive { return int_to_bytes_big(a, buf, signed, allocator); }
l := len(buf);
size_in_bytes := int_to_bytes_size(a, signed, allocator) or_return;
if size_in_bytes > l { return .Buffer_Overflow; }
t := &Int{};
defer destroy(t);
internal_complement(t, a, allocator) or_return;
size_in_bits := internal_count_bits(t);
i := l - 1;
for offset := 0; offset < size_in_bits; offset += 8 {
bits, _ := internal_int_bitfield_extract(t, offset, 8);
buf[i] = 255 - u8(bits & 255); i -= 1;
}
buf[0] = 255;
return;
}
/*
Read `Int` from a Big Endian binary representation.
Sign is detected from the first byte if `signed` is true.
*/
int_from_bytes_big :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
assert_if_nil(a);
buf := buf;
l := len(buf);
if l == 0 { return .Invalid_Argument; }
sign: Sign;
size_in_bits := l * 8;
if signed {
/*
First byte denotes the sign.
*/
size_in_bits -= 8;
}
size_in_digits := (size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS;
size_in_digits += 0 if size_in_bits % 8 == 0 else 1;
internal_zero(a, false, allocator) or_return;
internal_grow(a, size_in_digits, false, allocator) or_return;
if signed {
sign = .Zero_or_Positive if buf[0] == 0 else .Negative;
buf = buf[1:];
}
for v in buf {
internal_shl(a, a, 8) or_return;
a.digit[0] |= DIGIT(v);
}
a.sign = sign;
a.used = size_in_digits;
return internal_clamp(a);
}
/*
Read `Int` from a Big Endian Python binary representation.
Sign is detected from the first byte if `signed` is true.
*/
int_from_bytes_big_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
assert_if_nil(a);
buf := buf;
l := len(buf);
if l == 0 { return .Invalid_Argument; }
sign: Sign;
size_in_bits := l * 8;
if signed {
/*
First byte denotes the sign.
*/
size_in_bits -= 8;
}
size_in_digits := (size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS;
size_in_digits += 0 if size_in_bits % 8 == 0 else 1;
internal_zero(a, false, allocator) or_return;
internal_grow(a, size_in_digits, false, allocator) or_return;
if signed {
sign = .Zero_or_Positive if buf[0] == 0 else .Negative;
buf = buf[1:];
}
for v in buf {
internal_shl(a, a, 8) or_return;
if signed && sign == .Negative {
a.digit[0] |= DIGIT(255 - v);
} else {
a.digit[0] |= DIGIT(v);
}
}
a.sign = sign;
a.used = size_in_digits;
internal_clamp(a) or_return;
if signed && sign == .Negative {
return internal_sub(a, a, 1);
}
return nil;
}
/*
Read `Int` from a Little Endian binary representation.
Sign is detected from the last byte if `signed` is true.
*/
int_from_bytes_little :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
assert_if_nil(a);
buf := buf;
l := len(buf);
if l == 0 { return .Invalid_Argument; }
sign: Sign;
size_in_bits := l * 8;
if signed {
/*
First byte denotes the sign.
*/
size_in_bits -= 8;
}
size_in_digits := (size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS;
size_in_digits += 0 if size_in_bits % 8 == 0 else 1;
internal_zero(a, false, allocator) or_return;
internal_grow(a, size_in_digits, false, allocator) or_return;
if signed {
sign = .Zero_or_Positive if buf[l-1] == 0 else .Negative;
buf = buf[:l-1];
l -= 1;
}
for _, i in buf {
internal_shl(a, a, 8) or_return;
a.digit[0] |= DIGIT(buf[l-i-1]);
}
a.sign = sign;
a.used = size_in_digits;
return internal_clamp(a);
}
/*
Read `Int` from a Little Endian Python binary representation.
Sign is detected from the first byte if `signed` is true.
*/
int_from_bytes_little_python :: proc(a: ^Int, buf: []u8, signed := false, allocator := context.allocator) -> (err: Error) {
assert_if_nil(a);
buf := buf;
l := len(buf);
if l == 0 { return .Invalid_Argument; }
sign: Sign;
size_in_bits := l * 8;
if signed {
/*
First byte denotes the sign.
*/
size_in_bits -= 8;
}
size_in_digits := (size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS;
size_in_digits += 0 if size_in_bits % 8 == 0 else 1;
internal_zero(a, false, allocator) or_return;
internal_grow(a, size_in_digits, false, allocator) or_return;
if signed {
sign = .Zero_or_Positive if buf[l-1] == 0 else .Negative;
buf = buf[:l-1];
l -= 1;
}
for _, i in buf {
internal_shl(a, a, 8) or_return;
if signed && sign == .Negative {
a.digit[0] |= DIGIT(255 - buf[l-i-1]);
} else {
a.digit[0] |= DIGIT(buf[l-i-1]);
}
}
a.sign = sign;
a.used = size_in_digits;
internal_clamp(a) or_return;
if signed && sign == .Negative {
return internal_sub(a, a, 1);
}
return nil;
}
/*
Initialize constants.
*/
INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN := &Int{}, &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
initialize_constants :: proc() -> (res: int) {
internal_set( INT_ZERO, 0); INT_ZERO.flags = {.Immutable};
internal_set( INT_ONE, 1); INT_ONE.flags = {.Immutable};
internal_set(INT_MINUS_ONE, -1); INT_MINUS_ONE.flags = {.Immutable};
/*
We set these special values to -1 or 1 so they don't get mistake for zero accidentally.
This allows for shortcut tests of is_zero as .used == 0.
*/
internal_set( INT_NAN, 1); INT_NAN.flags = {.Immutable, .NaN};
internal_set( INT_INF, 1); INT_INF.flags = {.Immutable, .Inf};
internal_set( INT_INF, -1); INT_MINUS_INF.flags = {.Immutable, .Inf};
return _DEFAULT_MUL_KARATSUBA_CUTOFF;
}
/*
Destroy constants.
Optional for an EXE, as this would be called at the very end of a process.
*/
destroy_constants :: proc() {
internal_destroy(INT_ONE, INT_ZERO, INT_MINUS_ONE, INT_INF, INT_MINUS_INF, INT_NAN);
}
assert_if_nil :: #force_inline proc(integers: ..^Int, loc := #caller_location) {
integers := integers;
for i in &integers {
assert(i != nil, "(nil)", loc);
}
}
File diff suppressed because it is too large Load Diff
+144
View File
@@ -0,0 +1,144 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
An arbitrary precision mathematics implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
This file contains logical operations like `and`, `or` and `xor`.
*/
/*
The `and`, `or` and `xor` binops differ in two lines only.
We could handle those with a switch, but that adds overhead.
TODO: Implement versions that take a DIGIT immediate.
*/
/*
2's complement `and`, returns `dest = a & b;`
*/
int_and :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, a, b);
context.allocator = allocator;
internal_clear_if_uninitialized(a, b) or_return;
return #force_inline internal_int_and(dest, a, b);
}
and :: proc { int_and, };
/*
2's complement `or`, returns `dest = a | b;`
*/
int_or :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, a, b);
context.allocator = allocator;
internal_clear_if_uninitialized(a, b) or_return;
return #force_inline internal_int_or(dest, a, b);
}
or :: proc { int_or, };
/*
2's complement `xor`, returns `dest = a ^ b;`
*/
int_xor :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, a, b);
context.allocator = allocator;
internal_clear_if_uninitialized(a, b) or_return;
return #force_inline internal_int_xor(dest, a, b);
}
xor :: proc { int_xor, };
/*
dest = ~src
*/
int_complement :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
/*
Check that `src` and `dest` are usable.
*/
assert_if_nil(dest, src);
context.allocator = allocator;
internal_clear_if_uninitialized(dest, src) or_return;
return #force_inline internal_int_complement(dest, src);
}
complement :: proc { int_complement, };
/*
quotient, remainder := numerator >> bits;
`remainder` is allowed to be passed a `nil`, in which case `mod` won't be computed.
*/
int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(quotient, numerator);
context.allocator = allocator;
if err = internal_clear_if_uninitialized(quotient, numerator); err != nil { return err; }
return #force_inline internal_int_shrmod(quotient, remainder, numerator, bits);
}
shrmod :: proc { int_shrmod, };
int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
return #force_inline shrmod(dest, nil, source, bits, allocator);
}
shr :: proc { int_shr, };
/*
Shift right by `digits` * _DIGIT_BITS bits.
*/
int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
/*
Check that `quotient` is usable.
*/
assert_if_nil(quotient);
context.allocator = allocator;
internal_clear_if_uninitialized(quotient) or_return;
return #force_inline internal_int_shr_digit(quotient, digits);
}
shr_digit :: proc { int_shr_digit, };
/*
Shift right by a certain bit count with sign extension.
*/
int_shr_signed :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, src);
context.allocator = allocator;
internal_clear_if_uninitialized(dest, src) or_return;
return #force_inline internal_int_shr_signed(dest, src, bits);
}
shr_signed :: proc { int_shr_signed, };
/*
Shift left by a certain bit count.
*/
int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, src);
context.allocator = allocator;
internal_clear_if_uninitialized(dest, src) or_return;
return #force_inline internal_int_shl(dest, src, bits);
}
shl :: proc { int_shl, };
/*
Shift left by `digits` * _DIGIT_BITS bits.
*/
int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
/*
Check that `quotient` is usable.
*/
assert_if_nil(quotient);
context.allocator = allocator;
internal_clear_if_uninitialized(quotient) or_return;
return #force_inline internal_int_shl_digit(quotient, digits);
}
shl_digit :: proc { int_shl_digit, };
+256
View File
@@ -0,0 +1,256 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
An arbitrary precision mathematics implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
This file contains prime finding operations.
*/
/*
Determines if an Integer is divisible by one of the _PRIME_TABLE primes.
Returns true if it is, false if not.
*/
int_prime_is_divisible :: proc(a: ^Int, allocator := context.allocator) -> (res: bool, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
for prime in _private_prime_table {
rem := #force_inline int_mod_digit(a, prime) or_return;
if rem == 0 {
return true, nil;
}
}
/*
Default to not divisible.
*/
return false, nil;
}
/*
Computes xR**-1 == x (mod N) via Montgomery Reduction.
*/
internal_int_montgomery_reduce :: proc(x, n: ^Int, rho: DIGIT, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator;
/*
Can the fast reduction [comba] method be used?
Note that unlike in mul, you're safely allowed *less* than the available columns [255 per default],
since carries are fixed up in the inner loop.
*/
digs := (n.used * 2) + 1;
if digs < _WARRAY && x.used <= _WARRAY && n.used < _MAX_COMBA {
return _private_montgomery_reduce_comba(x, n, rho);
}
/*
Grow the input as required
*/
internal_grow(x, digs) or_return;
x.used = digs;
for ix := 0; ix < n.used; ix += 1 {
/*
`mu = ai * rho mod b`
The value of rho must be precalculated via `int_montgomery_setup()`,
such that it equals -1/n0 mod b this allows the following inner loop
to reduce the input one digit at a time.
*/
mu := DIGIT((_WORD(x.digit[ix]) * _WORD(rho)) & _WORD(_MASK));
/*
a = a + mu * m * b**i
Multiply and add in place.
*/
u := DIGIT(0);
iy := int(0);
for ; iy < n.used; iy += 1 {
/*
Compute product and sum.
*/
r := (_WORD(mu) * _WORD(n.digit[iy]) + _WORD(u) + _WORD(x.digit[ix + iy]));
/*
Get carry.
*/
u = DIGIT(r >> _DIGIT_BITS);
/*
Fix digit.
*/
x.digit[ix + iy] = DIGIT(r & _WORD(_MASK));
}
/*
At this point the ix'th digit of x should be zero.
Propagate carries upwards as required.
*/
for u != 0 {
x.digit[ix + iy] += u;
u = x.digit[ix + iy] >> _DIGIT_BITS;
x.digit[ix + iy] &= _MASK;
iy += 1;
}
}
/*
At this point the n.used'th least significant digits of x are all zero,
which means we can shift x to the right by n.used digits and the
residue is unchanged.
x = x/b**n.used.
*/
internal_clamp(x);
internal_shr_digit(x, n.used);
/*
if x >= n then x = x - n
*/
if internal_cmp_mag(x, n) != -1 {
return internal_sub(x, x, n);
}
return nil;
}
int_montgomery_reduce :: proc(x, n: ^Int, rho: DIGIT, allocator := context.allocator) -> (err: Error) {
assert_if_nil(x, n);
context.allocator = allocator;
internal_clear_if_uninitialized(x, n) or_return;
return #force_inline internal_int_montgomery_reduce(x, n, rho);
}
/*
Shifts with subtractions when the result is greater than b.
The method is slightly modified to shift B unconditionally upto just under
the leading bit of b. This saves alot of multiple precision shifting.
*/
internal_int_montgomery_calc_normalization :: proc(a, b: ^Int, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator;
/*
How many bits of last digit does b use.
*/
bits := internal_count_bits(b) % _DIGIT_BITS;
if b.used > 1 {
power := ((b.used - 1) * _DIGIT_BITS) + bits - 1;
internal_int_power_of_two(a, power) or_return;
} else {
internal_one(a);
bits = 1;
}
/*
Now compute C = A * B mod b.
*/
for x := bits - 1; x < _DIGIT_BITS; x += 1 {
internal_int_shl1(a, a) or_return;
if internal_cmp_mag(a, b) != -1 {
internal_sub(a, a, b) or_return;
}
}
return nil;
}
int_montgomery_calc_normalization :: proc(a, b: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(a, b);
context.allocator = allocator;
internal_clear_if_uninitialized(a, b) or_return;
return #force_inline internal_int_montgomery_calc_normalization(a, b);
}
/*
Sets up the Montgomery reduction stuff.
*/
internal_int_montgomery_setup :: proc(n: ^Int) -> (rho: DIGIT, err: Error) {
/*
Fast inversion mod 2**k
Based on the fact that:
XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n)
=> 2*X*A - X*X*A*A = 1
=> 2*(1) - (1) = 1
*/
b := n.digit[0];
if b & 1 == 0 { return 0, .Invalid_Argument; }
x := (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */
x *= 2 - (b * x); /* here x*a==1 mod 2**8 */
x *= 2 - (b * x); /* here x*a==1 mod 2**16 */
when _WORD_TYPE_BITS == 64 {
x *= 2 - (b * x); /* here x*a==1 mod 2**32 */
x *= 2 - (b * x); /* here x*a==1 mod 2**64 */
}
/*
rho = -1/m mod b
*/
rho = DIGIT(((_WORD(1) << _WORD(_DIGIT_BITS)) - _WORD(x)) & _WORD(_MASK));
return rho, nil;
}
int_montgomery_setup :: proc(n: ^Int, allocator := context.allocator) -> (rho: DIGIT, err: Error) {
assert_if_nil(n);
internal_clear_if_uninitialized(n, allocator) or_return;
return #force_inline internal_int_montgomery_setup(n);
}
/*
Returns the number of Rabin-Miller trials needed for a given bit size.
*/
number_of_rabin_miller_trials :: proc(bit_size: int) -> (number_of_trials: int) {
switch {
case bit_size <= 80:
return - 1; /* Use deterministic algorithm for size <= 80 bits */
case bit_size >= 81 && bit_size < 96:
return 37; /* max. error = 2^(-96) */
case bit_size >= 96 && bit_size < 128:
return 32; /* max. error = 2^(-96) */
case bit_size >= 128 && bit_size < 160:
return 40; /* max. error = 2^(-112) */
case bit_size >= 160 && bit_size < 256:
return 35; /* max. error = 2^(-112) */
case bit_size >= 256 && bit_size < 384:
return 27; /* max. error = 2^(-128) */
case bit_size >= 384 && bit_size < 512:
return 16; /* max. error = 2^(-128) */
case bit_size >= 512 && bit_size < 768:
return 18; /* max. error = 2^(-160) */
case bit_size >= 768 && bit_size < 896:
return 11; /* max. error = 2^(-160) */
case bit_size >= 896 && bit_size < 1_024:
return 10; /* max. error = 2^(-160) */
case bit_size >= 1_024 && bit_size < 1_536:
return 12; /* max. error = 2^(-192) */
case bit_size >= 1_536 && bit_size < 2_048:
return 8; /* max. error = 2^(-192) */
case bit_size >= 2_048 && bit_size < 3_072:
return 6; /* max. error = 2^(-192) */
case bit_size >= 3_072 && bit_size < 4_096:
return 4; /* max. error = 2^(-192) */
case bit_size >= 4_096 && bit_size < 5_120:
return 5; /* max. error = 2^(-256) */
case bit_size >= 5_120 && bit_size < 6_144:
return 4; /* max. error = 2^(-256) */
case bit_size >= 6_144 && bit_size < 8_192:
return 4; /* max. error = 2^(-256) */
case bit_size >= 8_192 && bit_size < 9_216:
return 3; /* max. error = 2^(-256) */
case bit_size >= 9_216 && bit_size < 10_240:
return 3; /* max. error = 2^(-256) */
case:
return 2; /* For keysizes bigger than 10_240 use always at least 2 Rounds */
}
}
File diff suppressed because it is too large Load Diff
+573
View File
@@ -0,0 +1,573 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
An arbitrary precision mathematics implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
This file contains basic arithmetic operations like `add`, `sub`, `mul`, `div`, ...
*/
/*
===========================
User-level routines
===========================
*/
/*
High-level addition. Handles sign.
*/
int_add :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, a, b);
context.allocator = allocator;
internal_clear_if_uninitialized(dest, a, b) or_return;
/*
All parameters have been initialized.
*/
return #force_inline internal_int_add_signed(dest, a, b);
}
/*
Adds the unsigned `DIGIT` immediate to an `Int`,
such that the `DIGIT` doesn't have to be turned into an `Int` first.
dest = a + digit;
*/
int_add_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
/*
Grow destination as required.
*/
grow(dest, a.used + 1) or_return;
/*
All parameters have been initialized.
*/
return #force_inline internal_int_add_digit(dest, a, digit);
}
/*
High-level subtraction, dest = number - decrease. Handles signs.
*/
int_sub :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, number, decrease);
context.allocator = allocator;
internal_clear_if_uninitialized(dest, number, decrease) or_return;
/*
All parameters have been initialized.
*/
return #force_inline internal_int_sub_signed(dest, number, decrease);
}
/*
Adds the unsigned `DIGIT` immediate to an `Int`,
such that the `DIGIT` doesn't have to be turned into an `Int` first.
dest = a - digit;
*/
int_sub_digit :: proc(dest, a: ^Int, digit: DIGIT, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
/*
Grow destination as required.
*/
grow(dest, a.used + 1) or_return;
/*
All parameters have been initialized.
*/
return #force_inline internal_int_sub_digit(dest, a, digit);
}
/*
dest = src / 2
dest = src >> 1
*/
int_halve :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, src);
context.allocator = allocator;
internal_clear_if_uninitialized(dest, src) or_return;
/*
Grow destination as required.
*/
if dest != src { grow(dest, src.used + 1) or_return }
return #force_inline internal_int_shr1(dest, src);
}
halve :: proc { int_halve, };
shr1 :: halve;
/*
dest = src * 2
dest = src << 1
*/
int_double :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, src);
context.allocator = allocator;
internal_clear_if_uninitialized(dest, src) or_return;
/*
Grow destination as required.
*/
if dest != src { grow(dest, src.used + 1) or_return; }
return #force_inline internal_int_shl1(dest, src);
}
double :: proc { int_double, };
shl1 :: double;
/*
Multiply by a DIGIT.
*/
int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, src);
context.allocator = allocator;
internal_clear_if_uninitialized(src, dest) or_return;
return #force_inline internal_int_mul_digit(dest, src, multiplier);
}
/*
High level multiplication (handles sign).
*/
int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, src, multiplier);
context.allocator = allocator;
internal_clear_if_uninitialized(dest, src, multiplier) or_return;
return #force_inline internal_int_mul(dest, src, multiplier);
}
mul :: proc { int_mul, int_mul_digit, };
sqr :: proc(dest, src: ^Int) -> (err: Error) { return mul(dest, src, src); }
/*
divmod.
Both the quotient and remainder are optional and may be passed a nil.
*/
int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator;
/*
Early out if neither of the results is wanted.
*/
if quotient == nil && remainder == nil { return nil; }
internal_clear_if_uninitialized(numerator, denominator) or_return;
return #force_inline internal_divmod(quotient, remainder, numerator, denominator);
}
int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT, allocator := context.allocator) -> (remainder: DIGIT, err: Error) {
assert_if_nil(quotient, numerator);
context.allocator = allocator;
internal_clear_if_uninitialized(numerator) or_return;
return #force_inline internal_divmod(quotient, numerator, denominator);
}
divmod :: proc{ int_divmod, int_divmod_digit, };
int_div :: proc(quotient, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(quotient, numerator, denominator);
context.allocator = allocator;
internal_clear_if_uninitialized(numerator, denominator) or_return;
return #force_inline internal_divmod(quotient, nil, numerator, denominator);
}
int_div_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT, allocator := context.allocator) -> (err: Error) {
assert_if_nil(quotient, numerator);
context.allocator = allocator;
internal_clear_if_uninitialized(numerator) or_return;
_ = #force_inline internal_divmod(quotient, numerator, denominator) or_return;
return;
}
div :: proc { int_div, int_div_digit, };
/*
remainder = numerator % denominator.
0 <= remainder < denominator if denominator > 0
denominator < remainder <= 0 if denominator < 0
*/
int_mod :: proc(remainder, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(remainder, numerator, denominator);
context.allocator = allocator;
internal_clear_if_uninitialized(numerator, denominator) or_return;
return #force_inline internal_int_mod(remainder, numerator, denominator);
}
int_mod_digit :: proc(numerator: ^Int, denominator: DIGIT, allocator := context.allocator) -> (remainder: DIGIT, err: Error) {
return #force_inline internal_divmod(nil, numerator, denominator, allocator);
}
mod :: proc { int_mod, int_mod_digit, };
/*
remainder = (number + addend) % modulus.
*/
int_addmod :: proc(remainder, number, addend, modulus: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(remainder, number, addend);
context.allocator = allocator;
internal_clear_if_uninitialized(number, addend, modulus) or_return;
return #force_inline internal_addmod(remainder, number, addend, modulus);
}
addmod :: proc { int_addmod, };
/*
remainder = (number - decrease) % modulus.
*/
int_submod :: proc(remainder, number, decrease, modulus: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(remainder, number, decrease);
context.allocator = allocator;
internal_clear_if_uninitialized(number, decrease, modulus) or_return;
return #force_inline internal_submod(remainder, number, decrease, modulus);
}
submod :: proc { int_submod, };
/*
remainder = (number * multiplicand) % modulus.
*/
int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(remainder, number, multiplicand);
context.allocator = allocator;
internal_clear_if_uninitialized(number, multiplicand, modulus) or_return;
return #force_inline internal_mulmod(remainder, number, multiplicand, modulus);
}
mulmod :: proc { int_mulmod, };
/*
remainder = (number * number) % modulus.
*/
int_sqrmod :: proc(remainder, number, modulus: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(remainder, number, modulus);
context.allocator = allocator;
internal_clear_if_uninitialized(number, modulus) or_return;
return #force_inline internal_sqrmod(remainder, number, modulus);
}
sqrmod :: proc { int_sqrmod, };
int_factorial :: proc(res: ^Int, n: int, allocator := context.allocator) -> (err: Error) {
if n < 0 || n > FACTORIAL_MAX_N { return .Invalid_Argument; }
assert_if_nil(res);
return #force_inline internal_int_factorial(res, n, allocator);
}
factorial :: proc { int_factorial, };
/*
Number of ways to choose `k` items from `n` items.
Also known as the binomial coefficient.
TODO: Speed up.
Could be done faster by reusing code from factorial and reusing the common "prefix" results for n!, k! and n-k!
We know that n >= k, otherwise we early out with res = 0.
So:
n-k, keep result
n, start from previous result
k, start from previous result
*/
int_choose_digit :: proc(res: ^Int, n, k: int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(res);
context.allocator = allocator;
if n < 0 || n > FACTORIAL_MAX_N { return .Invalid_Argument; }
if k > n { return internal_zero(res); }
/*
res = n! / (k! * (n - k)!)
*/
n_fac, k_fac, n_minus_k_fac := &Int{}, &Int{}, &Int{};
defer internal_destroy(n_fac, k_fac, n_minus_k_fac);
#force_inline internal_int_factorial(n_minus_k_fac, n - k) or_return;
#force_inline internal_int_factorial(k_fac, k) or_return;
#force_inline internal_mul(k_fac, k_fac, n_minus_k_fac) or_return;
#force_inline internal_int_factorial(n_fac, n) or_return;
#force_inline internal_div(res, n_fac, k_fac) or_return;
return;
}
choose :: proc { int_choose_digit, };
/*
Function computing both GCD and (if target isn't `nil`) also LCM.
*/
int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
if res_gcd == nil && res_lcm == nil { return nil; }
assert_if_nil(a, b);
context.allocator = allocator;
internal_clear_if_uninitialized(a, b) or_return;
return #force_inline internal_int_gcd_lcm(res_gcd, res_lcm, a, b);
}
gcd_lcm :: proc { int_gcd_lcm, };
/*
Greatest Common Divisor.
*/
int_gcd :: proc(res, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
return #force_inline int_gcd_lcm(res, nil, a, b, allocator);
}
gcd :: proc { int_gcd, };
/*
Least Common Multiple.
*/
int_lcm :: proc(res, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
return #force_inline int_gcd_lcm(nil, res, a, b, allocator);
}
lcm :: proc { int_lcm, };
/*
remainder = numerator % (1 << bits)
*/
int_mod_bits :: proc(remainder, numerator: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(remainder, numerator);
context.allocator = allocator;
internal_clear_if_uninitialized(remainder, numerator) or_return;
if bits < 0 { return .Invalid_Argument; }
return #force_inline internal_int_mod_bits(remainder, numerator, bits);
}
mod_bits :: proc { int_mod_bits, };
/*
Logs and roots and such.
*/
int_log :: proc(a: ^Int, base: DIGIT, allocator := context.allocator) -> (res: int, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
return #force_inline internal_int_log(a, base);
}
digit_log :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) {
return #force_inline internal_digit_log(a, base);
}
log :: proc { int_log, digit_log, };
/*
Calculate `dest = base^power` using a square-multiply algorithm.
*/
int_pow :: proc(dest, base: ^Int, power: int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, base);
context.allocator = allocator;
internal_clear_if_uninitialized(dest, base) or_return;
return #force_inline internal_int_pow(dest, base, power);
}
/*
Calculate `dest = base^power` using a square-multiply algorithm.
*/
int_pow_int :: proc(dest: ^Int, base, power: int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest);
return #force_inline internal_pow(dest, base, power, allocator);
}
pow :: proc { int_pow, int_pow_int, small_pow, };
exp :: pow;
small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) {
return #force_inline internal_small_pow(base, exponent);
}
/*
This function is less generic than `root_n`, simpler and faster.
*/
int_sqrt :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dest, src);
context.allocator = allocator;
internal_clear_if_uninitialized(dest, src) or_return;
return #force_inline internal_int_sqrt(dest, src);
}
sqrt :: proc { int_sqrt, };
/*
Find the nth root of an Integer.
Result found such that `(dest)**n <= src` and `(dest+1)**n > src`
This algorithm uses Newton's approximation `x[i+1] = x[i] - f(x[i])/f'(x[i])`,
which will find the root in `log(n)` time where each step involves a fair bit.
*/
int_root_n :: proc(dest, src: ^Int, n: int, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator;
/*
Fast path for n == 2.
*/
if n == 2 { return sqrt(dest, src); }
assert_if_nil(dest, src);
/*
Initialize dest + src if needed.
*/
internal_clear_if_uninitialized(dest, src) or_return;
return #force_inline internal_int_root_n(dest, src, n);
}
root_n :: proc { int_root_n, };
/*
Comparison routines.
*/
int_is_initialized :: proc(a: ^Int) -> bool {
if a == nil { return false; }
return #force_inline internal_int_is_initialized(a);
}
int_is_zero :: proc(a: ^Int, allocator := context.allocator) -> (zero: bool, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
return #force_inline internal_is_zero(a), nil;
}
int_is_positive :: proc(a: ^Int, allocator := context.allocator) -> (positive: bool, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
return #force_inline internal_is_positive(a), nil;
}
int_is_negative :: proc(a: ^Int, allocator := context.allocator) -> (negative: bool, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
return #force_inline internal_is_negative(a), nil;
}
int_is_even :: proc(a: ^Int, allocator := context.allocator) -> (even: bool, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
return #force_inline internal_is_even(a), nil;
}
int_is_odd :: proc(a: ^Int, allocator := context.allocator) -> (odd: bool, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
return #force_inline internal_is_odd(a), nil;
}
platform_int_is_power_of_two :: #force_inline proc(a: int) -> bool {
return ((a) != 0) && (((a) & ((a) - 1)) == 0);
}
int_is_power_of_two :: proc(a: ^Int, allocator := context.allocator) -> (res: bool, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
return #force_inline internal_is_power_of_two(a), nil;
}
/*
Compare two `Int`s, signed.
*/
int_compare :: proc(a, b: ^Int, allocator := context.allocator) -> (comparison: int, err: Error) {
assert_if_nil(a, b);
context.allocator = allocator;
internal_clear_if_uninitialized(a, b) or_return;
return #force_inline internal_cmp(a, b), nil;
}
int_cmp :: int_compare;
/*
Compare an `Int` to an unsigned number upto the size of the backing type.
*/
int_compare_digit :: proc(a: ^Int, b: DIGIT, allocator := context.allocator) -> (comparison: int, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
return #force_inline internal_cmp_digit(a, b), nil;
}
int_cmp_digit :: int_compare_digit;
/*
Compare the magnitude of two `Int`s, unsigned.
*/
int_compare_magnitude :: proc(a, b: ^Int, allocator := context.allocator) -> (res: int, err: Error) {
assert_if_nil(a, b);
context.allocator = allocator;
internal_clear_if_uninitialized(a, b) or_return;
return #force_inline internal_cmp_mag(a, b), nil;
}
/*
Check if remainders are possible squares - fast exclude non-squares.
Returns `true` if `a` is a square, `false` if not.
Assumes `a` not to be `nil` and to have been initialized.
*/
int_is_square :: proc(a: ^Int, allocator := context.allocator) -> (square: bool, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
internal_clear_if_uninitialized(a) or_return;
return #force_inline internal_int_is_square(a);
}
+480
View File
@@ -0,0 +1,480 @@
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
An arbitrary precision mathematics implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
This file contains radix conversions, `string_to_int` (atoi) and `int_to_string` (itoa).
TODO:
- Use Barrett reduction for non-powers-of-two.
- Also look at extracting and splatting several digits at once.
*/
import "core:intrinsics"
import "core:mem"
/*
This version of `itoa` allocates one behalf of the caller. The caller must free the string.
*/
int_itoa_string :: proc(a: ^Int, radix := i8(-1), zero_terminate := false, allocator := context.allocator) -> (res: string, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
a := a; radix := radix;
clear_if_uninitialized(a) or_return;
/*
Radix defaults to 10.
*/
radix = radix if radix > 0 else 10;
/*
TODO: If we want to write a prefix for some of the radixes, we can oversize the buffer.
Then after the digits are written and the string is reversed
*/
/*
Calculate the size of the buffer we need, and
Exit if calculating the size returned an error.
*/
size := radix_size(a, radix, zero_terminate) or_return;
/*
Allocate the buffer we need.
*/
buffer := make([]u8, size);
/*
Write the digits out into the buffer.
*/
written: int;
written, err = int_itoa_raw(a, radix, buffer, size, zero_terminate);
return string(buffer[:written]), err;
}
/*
This version of `itoa` allocates one behalf of the caller. The caller must free the string.
*/
int_itoa_cstring :: proc(a: ^Int, radix := i8(-1), allocator := context.allocator) -> (res: cstring, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
a := a; radix := radix;
clear_if_uninitialized(a) or_return;
/*
Radix defaults to 10.
*/
radix = radix if radix > 0 else 10;
s: string;
s, err = int_itoa_string(a, radix, true);
return cstring(raw_data(s)), err;
}
/*
A low-level `itoa` using a caller-provided buffer. `itoa_string` and `itoa_cstring` use this.
You can use also use it if you want to pre-allocate a buffer and optionally reuse it.
Use `radix_size` or `radix_size_estimate` to determine a buffer size big enough.
You can pass the output of `radix_size` to `size` if you've previously called it to size
the output buffer. If you haven't, this routine will call it. This way it knows if the buffer
is the appropriate size, and we can write directly in place without a reverse step at the end.
=== === === IMPORTANT === === ===
If you determined the buffer size using `radix_size_estimate`, or have a buffer
that you reuse that you know is large enough, don't pass this size unless you know what you are doing,
because we will always write backwards starting at last byte of the buffer.
Keep in mind that if you set `size` yourself and it's smaller than the buffer,
it'll result in buffer overflows, as we use it to avoid reversing at the end
and having to perform a buffer overflow check each character.
*/
int_itoa_raw :: proc(a: ^Int, radix: i8, buffer: []u8, size := int(-1), zero_terminate := false) -> (written: int, err: Error) {
assert_if_nil(a);
a := a; radix := radix; size := size;
clear_if_uninitialized(a) or_return;
/*
Radix defaults to 10.
*/
radix = radix if radix > 0 else 10;
if radix < 2 || radix > 64 {
return 0, .Invalid_Argument;
}
/*
We weren't given a size. Let's compute it.
*/
if size == -1 {
size = radix_size(a, radix, zero_terminate) or_return;
}
/*
Early exit if the buffer we were given is too small.
*/
available := len(buffer);
if available < size {
return 0, .Buffer_Overflow;
}
/*
Fast path for when `Int` == 0 or the entire `Int` fits in a single radix digit.
*/
z, _ := is_zero(a);
if z || (a.used == 1 && a.digit[0] < DIGIT(radix)) {
if zero_terminate {
available -= 1;
buffer[available] = 0;
}
available -= 1;
buffer[available] = RADIX_TABLE[a.digit[0]];
if n, _ := is_neg(a); n {
available -= 1;
buffer[available] = '-';
}
/*
If we overestimated the size, we need to move the buffer left.
*/
written = len(buffer) - available;
if written < size {
diff := size - written;
mem.copy(&buffer[0], &buffer[diff], written);
}
return written, nil;
}
/*
Fast path for when `Int` fits within a `_WORD`.
*/
if a.used == 1 || a.used == 2 {
if zero_terminate {
available -= 1;
buffer[available] = 0;
}
val := _WORD(a.digit[1]) << _DIGIT_BITS + _WORD(a.digit[0]);
for val > 0 {
q := val / _WORD(radix);
available -= 1;
buffer[available] = RADIX_TABLE[val - (q * _WORD(radix))];
val = q;
}
if n, _ := is_neg(a); n {
available -= 1;
buffer[available] = '-';
}
/*
If we overestimated the size, we need to move the buffer left.
*/
written = len(buffer) - available;
if written < size {
diff := size - written;
mem.copy(&buffer[0], &buffer[diff], written);
}
return written, nil;
}
/*
Fast path for radixes that are a power of two.
*/
if is_power_of_two(int(radix)) {
if zero_terminate {
available -= 1;
buffer[available] = 0;
}
shift, count: int;
// mask := _WORD(radix - 1);
shift, err = log(DIGIT(radix), 2);
count, err = count_bits(a);
digit: _WORD;
for offset := 0; offset < count; offset += shift {
bits_to_get := int(min(count - offset, shift));
digit, err = int_bitfield_extract(a, offset, bits_to_get);
if err != nil {
return len(buffer) - available, .Invalid_Argument;
}
available -= 1;
buffer[available] = RADIX_TABLE[digit];
}
if n, _ := is_neg(a); n {
available -= 1;
buffer[available] = '-';
}
/*
If we overestimated the size, we need to move the buffer left.
*/
written = len(buffer) - available;
if written < size {
diff := size - written;
mem.copy(&buffer[0], &buffer[diff], written);
}
return written, nil;
}
return _itoa_raw_full(a, radix, buffer, zero_terminate);
}
itoa :: proc{int_itoa_string, int_itoa_raw};
int_to_string :: int_itoa_string;
int_to_cstring :: int_itoa_cstring;
/*
Read a string [ASCII] in a given radix.
*/
int_atoi :: proc(res: ^Int, input: string, radix := i8(10), allocator := context.allocator) -> (err: Error) {
assert_if_nil(res);
input := input;
context.allocator = allocator;
/*
Make sure the radix is ok.
*/
if radix < 2 || radix > 64 { return .Invalid_Argument; }
/*
Set the integer to the default of zero.
*/
internal_zero(res) or_return;
/*
We'll interpret an empty string as zero.
*/
if len(input) == 0 {
return nil;
}
/*
If the leading digit is a minus set the sign to negative.
Given the above early out, the length should be at least 1.
*/
sign := Sign.Zero_or_Positive;
if input[0] == '-' {
input = input[1:];
sign = .Negative;
}
/*
Process each digit of the string.
*/
ch: rune;
for len(input) > 0 {
/* if the radix <= 36 the conversion is case insensitive
* this allows numbers like 1AB and 1ab to represent the same value
* [e.g. in hex]
*/
ch = rune(input[0]);
if radix <= 36 && ch >= 'a' && ch <= 'z' {
ch -= 32; // 'a' - 'A'
}
pos := ch - '+';
if RADIX_TABLE_REVERSE_SIZE <= pos {
break;
}
y := RADIX_TABLE_REVERSE[pos];
/* if the char was found in the map
* and is less than the given radix add it
* to the number, otherwise exit the loop.
*/
if y >= u8(radix) {
break;
}
internal_mul(res, res, DIGIT(radix)) or_return;
internal_add(res, res, DIGIT(y)) or_return;
input = input[1:];
}
/*
If an illegal character was found, fail.
*/
if len(input) > 0 && ch != 0 && ch != '\r' && ch != '\n' {
return .Invalid_Argument;
}
/*
Set the sign only if res != 0.
*/
if res.used > 0 {
res.sign = sign;
}
return nil;
}
atoi :: proc { int_atoi, };
/*
We size for `string` by default.
*/
radix_size :: proc(a: ^Int, radix: i8, zero_terminate := false, allocator := context.allocator) -> (size: int, err: Error) {
a := a;
assert_if_nil(a);
if radix < 2 || radix > 64 { return -1, .Invalid_Argument; }
clear_if_uninitialized(a) or_return;
if internal_is_zero(a) {
if zero_terminate {
return 2, nil;
}
return 1, nil;
}
if internal_is_power_of_two(a) {
/*
Calculate `log` on a temporary "copy" with its sign set to positive.
*/
t := &Int{
used = a.used,
sign = .Zero_or_Positive,
digit = a.digit,
};
size = internal_log(t, DIGIT(radix)) or_return;
} else {
la, k := &Int{}, &Int{};
defer internal_destroy(la, k);
/* la = floor(log_2(a)) + 1 */
bit_count := internal_count_bits(a);
internal_set(la, bit_count) or_return;
/* k = floor(2^29/log_2(radix)) + 1 */
lb := _log_bases;
internal_set(k, lb[radix]) or_return;
/* n = floor((la * k) / 2^29) + 1 */
internal_mul(k, la, k) or_return;
internal_shr(k, k, _RADIX_SIZE_SCALE) or_return;
/* The "+1" here is the "+1" in "floor((la * k) / 2^29) + 1" */
/* n = n + 1 + EOS + sign */
size_, _ := internal_get(k, u128);
size = int(size_);
}
/*
log truncates to zero, so we need to add one more, and one for `-` if negative.
*/
size += 2 if a.sign == .Negative else 1;
size += 1 if zero_terminate else 0;
return size, nil;
}
/*
Overestimate the size needed for the bigint to string conversion by a very small amount.
The error is about 10^-8; it will overestimate the result by at most 11 elements for
a number of the size 2^(2^31)-1 which is currently the largest possible in this library.
Some short tests gave no results larger than 5 (plus 2 for sign and EOS).
*/
/*
Table of {0, INT(log_2([1..64])*2^p)+1 } where p is the scale
factor defined in MP_RADIX_SIZE_SCALE and INT() extracts the integer part (truncating).
Good for 32 bit "int". Set MP_RADIX_SIZE_SCALE = 61 and recompute values
for 64 bit "int".
*/
_RADIX_SIZE_SCALE :: 29;
_log_bases :: [65]u32{
0, 0, 0x20000001, 0x14309399, 0x10000001,
0xdc81a35, 0xc611924, 0xb660c9e, 0xaaaaaab, 0xa1849cd,
0x9a209a9, 0x94004e1, 0x8ed19c2, 0x8a5ca7d, 0x867a000,
0x830cee3, 0x8000001, 0x7d42d60, 0x7ac8b32, 0x7887847,
0x7677349, 0x749131f, 0x72d0163, 0x712f657, 0x6fab5db,
0x6e40d1b, 0x6ced0d0, 0x6badbde, 0x6a80e3b, 0x6964c19,
0x6857d31, 0x6758c38, 0x6666667, 0x657fb21, 0x64a3b9f,
0x63d1ab4, 0x6308c92, 0x624869e, 0x618ff47, 0x60dedea,
0x6034ab0, 0x5f90e7b, 0x5ef32cb, 0x5e5b1b2, 0x5dc85c3,
0x5d3aa02, 0x5cb19d9, 0x5c2d10f, 0x5bacbbf, 0x5b3064f,
0x5ab7d68, 0x5a42df0, 0x59d1506, 0x5962ffe, 0x58f7c57,
0x588f7bc, 0x582a000, 0x57c7319, 0x5766f1d, 0x5709243,
0x56adad9, 0x565474d, 0x55fd61f, 0x55a85e8, 0x5555556,
};
/*
Characters used in radix conversions.
*/
RADIX_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
RADIX_TABLE_REVERSE := [RADIX_TABLE_REVERSE_SIZE]u8{
0x3e, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x01, 0x02, 0x03, 0x04, /* +,-./01234 */
0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, /* 56789:;<=> */
0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, /* ?@ABCDEFGH */
0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, /* IJKLMNOPQR */
0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0xff, 0xff, /* STUVWXYZ[\ */
0xff, 0xff, 0xff, 0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, /* ]^_`abcdef */
0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, /* ghijklmnop */
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, /* qrstuvwxyz */
};
RADIX_TABLE_REVERSE_SIZE :: 80;
/*
Stores a bignum as a ASCII string in a given radix (2..64)
The buffer must be appropriately sized. This routine doesn't check.
*/
_itoa_raw_full :: proc(a: ^Int, radix: i8, buffer: []u8, zero_terminate := false, allocator := context.allocator) -> (written: int, err: Error) {
assert_if_nil(a);
context.allocator = allocator;
temp, denominator := &Int{}, &Int{};
internal_copy(temp, a) or_return;
internal_set(denominator, radix) or_return;
available := len(buffer);
if zero_terminate {
available -= 1;
buffer[available] = 0;
}
if a.sign == .Negative {
temp.sign = .Zero_or_Positive;
}
remainder: DIGIT;
for {
if remainder, err = #force_inline internal_divmod(temp, temp, DIGIT(radix)); err != nil {
internal_destroy(temp, denominator);
return len(buffer) - available, err;
}
available -= 1;
buffer[available] = RADIX_TABLE[remainder];
if temp.used == 0 {
break;
}
}
if a.sign == .Negative {
available -= 1;
buffer[available] = '-';
}
internal_destroy(temp, denominator);
/*
If we overestimated the size, we need to move the buffer left.
*/
written = len(buffer) - available;
if written < len(buffer) {
diff := len(buffer) - written;
mem.copy(&buffer[0], &buffer[diff], written);
}
return written, nil;
}
+390
View File
@@ -0,0 +1,390 @@
//+ignore
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
An arbitrary precision mathematics implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
This file exports procedures for use with the test.py test suite.
*/
/*
TODO: Write tests for `internal_*` and test reusing parameters with the public implementations.
*/
import "core:runtime"
import "core:strings"
PyRes :: struct {
res: cstring,
err: Error,
}
@export test_initialize_constants :: proc "c" () -> (res: u64) {
context = runtime.default_context();
res = u64(initialize_constants());
//assert(MUL_KARATSUBA_CUTOFF >= 40);
return res;
}
@export test_error_string :: proc "c" (err: Error) -> (res: cstring) {
context = runtime.default_context();
es := Error_String;
return strings.clone_to_cstring(es[err], context.temp_allocator);
}
@export test_add :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
aa, bb, sum := &Int{}, &Int{}, &Int{};
defer internal_destroy(aa, bb, sum);
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":add:atoi(a):", err=err}; }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":add:atoi(b):", err=err}; }
if bb.used == 1 {
if err = #force_inline internal_add(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; }
} else {
if err = #force_inline internal_add(sum, aa, bb); err != nil { return PyRes{res=":add:add(sum,a,b):", err=err}; }
}
r: cstring;
r, err = int_itoa_cstring(sum, 16, context.temp_allocator);
if err != nil { return PyRes{res=":add:itoa(sum):", err=err}; }
return PyRes{res = r, err = nil};
}
@export test_sub :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
aa, bb, sum := &Int{}, &Int{}, &Int{};
defer internal_destroy(aa, bb, sum);
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":sub:atoi(a):", err=err}; }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":sub:atoi(b):", err=err}; }
if bb.used == 1 {
if err = #force_inline internal_sub(sum, aa, bb.digit[0]); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; }
} else {
if err = #force_inline internal_sub(sum, aa, bb); err != nil { return PyRes{res=":sub:sub(sum,a,b):", err=err}; }
}
r: cstring;
r, err = int_itoa_cstring(sum, 16, context.temp_allocator);
if err != nil { return PyRes{res=":sub:itoa(sum):", err=err}; }
return PyRes{res = r, err = nil};
}
@export test_mul :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
aa, bb, product := &Int{}, &Int{}, &Int{};
defer internal_destroy(aa, bb, product);
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":mul:atoi(a):", err=err}; }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":mul:atoi(b):", err=err}; }
if err = #force_inline internal_mul(product, aa, bb); err != nil { return PyRes{res=":mul:mul(product,a,b):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(product, 16, context.temp_allocator);
if err != nil { return PyRes{res=":mul:itoa(product):", err=err}; }
return PyRes{res = r, err = nil};
}
@export test_sqr :: proc "c" (a: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
aa, square := &Int{}, &Int{};
defer internal_destroy(aa, square);
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":sqr:atoi(a):", err=err}; }
if err = #force_inline internal_sqr(square, aa); err != nil { return PyRes{res=":sqr:sqr(square,a):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(square, 16, context.temp_allocator);
if err != nil { return PyRes{res=":sqr:itoa(square):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
NOTE(Jeroen): For simplicity, we don't return the quotient and the remainder, just the quotient.
*/
@export test_div :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
aa, bb, quotient := &Int{}, &Int{}, &Int{};
defer internal_destroy(aa, bb, quotient);
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":div:atoi(a):", err=err}; }
if err = atoi(bb, string(b), 16); err != nil { return PyRes{res=":div:atoi(b):", err=err}; }
if err = #force_inline internal_div(quotient, aa, bb); err != nil { return PyRes{res=":div:div(quotient,a,b):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(quotient, 16, context.temp_allocator);
if err != nil { return PyRes{res=":div:itoa(quotient):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
res = log(a, base)
*/
@export test_log :: proc "c" (a: cstring, base := DIGIT(2)) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
l: int;
aa := &Int{};
defer internal_destroy(aa);
if err = atoi(aa, string(a), 16); err != nil { return PyRes{res=":log:atoi(a):", err=err}; }
if l, err = #force_inline internal_log(aa, base); err != nil { return PyRes{res=":log:log(a, base):", err=err}; }
#force_inline internal_zero(aa);
aa.digit[0] = DIGIT(l) & _MASK;
aa.digit[1] = DIGIT(l) >> _DIGIT_BITS;
aa.used = 2;
clamp(aa);
r: cstring;
r, err = int_itoa_cstring(aa, 16, context.temp_allocator);
if err != nil { return PyRes{res=":log:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = base^power
*/
@export test_pow :: proc "c" (base: cstring, power := int(2)) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
dest, bb := &Int{}, &Int{};
defer internal_destroy(dest, bb);
if err = atoi(bb, string(base), 16); err != nil { return PyRes{res=":pow:atoi(base):", err=err}; }
if err = #force_inline internal_pow(dest, bb, power); err != nil { return PyRes{res=":pow:pow(dest, base, power):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(dest, 16, context.temp_allocator);
if err != nil { return PyRes{res=":log:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = sqrt(src)
*/
@export test_sqrt :: proc "c" (source: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
src := &Int{};
defer internal_destroy(src);
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":sqrt:atoi(src):", err=err}; }
if err = #force_inline internal_sqrt(src, src); err != nil { return PyRes{res=":sqrt:sqrt(src):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":log:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = root_n(src, power)
*/
@export test_root_n :: proc "c" (source: cstring, power: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
src := &Int{};
defer internal_destroy(src);
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":root_n:atoi(src):", err=err}; }
if err = #force_inline internal_root_n(src, src, power); err != nil { return PyRes{res=":root_n:root_n(src):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":root_n:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = shr_digit(src, digits)
*/
@export test_shr_digit :: proc "c" (source: cstring, digits: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
src := &Int{};
defer internal_destroy(src);
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_digit:atoi(src):", err=err}; }
if err = #force_inline internal_shr_digit(src, digits); err != nil { return PyRes{res=":shr_digit:shr_digit(src):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":shr_digit:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = shl_digit(src, digits)
*/
@export test_shl_digit :: proc "c" (source: cstring, digits: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
src := &Int{};
defer internal_destroy(src);
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl_digit:atoi(src):", err=err}; }
if err = #force_inline internal_shl_digit(src, digits); err != nil { return PyRes{res=":shl_digit:shr_digit(src):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":shl_digit:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = shr(src, bits)
*/
@export test_shr :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
src := &Int{};
defer internal_destroy(src);
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr:atoi(src):", err=err}; }
if err = #force_inline internal_shr(src, src, bits); err != nil { return PyRes{res=":shr:shr(src, bits):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":shr:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = shr_signed(src, bits)
*/
@export test_shr_signed :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
src := &Int{};
defer internal_destroy(src);
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shr_signed:atoi(src):", err=err}; }
if err = #force_inline internal_shr_signed(src, src, bits); err != nil { return PyRes{res=":shr_signed:shr_signed(src, bits):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":shr_signed:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = shl(src, bits)
*/
@export test_shl :: proc "c" (source: cstring, bits: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
src := &Int{};
defer internal_destroy(src);
if err = atoi(src, string(source), 16); err != nil { return PyRes{res=":shl:atoi(src):", err=err}; }
if err = #force_inline internal_shl(src, src, bits); err != nil { return PyRes{res=":shl:shl(src, bits):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(src, 16, context.temp_allocator);
if err != nil { return PyRes{res=":shl:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = factorial(n)
*/
@export test_factorial :: proc "c" (n: int) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
dest := &Int{};
defer internal_destroy(dest);
if err = #force_inline internal_int_factorial(dest, n); err != nil { return PyRes{res=":factorial:factorial(n):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(dest, 16, context.temp_allocator);
if err != nil { return PyRes{res=":factorial:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = gcd(a, b)
*/
@export test_gcd :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
ai, bi, dest := &Int{}, &Int{}, &Int{};
defer internal_destroy(ai, bi, dest);
if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":gcd:atoi(a):", err=err}; }
if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":gcd:atoi(b):", err=err}; }
if err = #force_inline internal_int_gcd_lcm(dest, nil, ai, bi); err != nil { return PyRes{res=":gcd:gcd(a, b):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(dest, 16, context.temp_allocator);
if err != nil { return PyRes{res=":gcd:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = lcm(a, b)
*/
@export test_lcm :: proc "c" (a, b: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
ai, bi, dest := &Int{}, &Int{}, &Int{};
defer internal_destroy(ai, bi, dest);
if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":lcm:atoi(a):", err=err}; }
if err = atoi(bi, string(b), 16); err != nil { return PyRes{res=":lcm:atoi(b):", err=err}; }
if err = #force_inline internal_int_gcd_lcm(nil, dest, ai, bi); err != nil { return PyRes{res=":lcm:lcm(a, b):", err=err}; }
r: cstring;
r, err = int_itoa_cstring(dest, 16, context.temp_allocator);
if err != nil { return PyRes{res=":lcm:itoa(res):", err=err}; }
return PyRes{res = r, err = nil};
}
/*
dest = lcm(a, b)
*/
@export test_is_square :: proc "c" (a: cstring) -> (res: PyRes) {
context = runtime.default_context();
err: Error;
square: bool;
ai := &Int{};
defer internal_destroy(ai);
if err = atoi(ai, string(a), 16); err != nil { return PyRes{res=":is_square:atoi(a):", err=err}; }
if square, err = #force_inline internal_int_is_square(ai); err != nil { return PyRes{res=":is_square:is_square(a):", err=err}; }
if square {
return PyRes{"True", nil};
}
return PyRes{"False", nil};
}
+707
View File
@@ -0,0 +1,707 @@
from ctypes import *
from random import *
import math
import os
import platform
import time
import gc
from enum import Enum
import argparse
parser = argparse.ArgumentParser(
description = "Odin core:math/big test suite",
epilog = "By default we run regression and random tests with preset parameters.",
formatter_class = argparse.ArgumentDefaultsHelpFormatter,
)
#
# Normally, we report the number of passes and fails. With this option set, we exit at first fail.
#
parser.add_argument(
"-exit-on-fail",
help = "Exit when a test fails",
action = "store_true",
)
#
# We skip randomized tests altogether if this is set.
#
no_random = parser.add_mutually_exclusive_group()
no_random.add_argument(
"-no-random",
help = "No random tests",
action = "store_true",
)
#
# Normally we run a given number of cycles on each test.
# Timed tests budget 1 second per 20_000 bits instead.
#
# For timed tests we budget a second per `n` bits and iterate until we hit that time.
#
timed_or_fast = no_random.add_mutually_exclusive_group()
timed_or_fast.add_argument(
"-timed",
type = bool,
default = False,
help = "Timed tests instead of a preset number of iterations.",
)
parser.add_argument(
"-timed-bits",
type = int,
metavar = "BITS",
default = 20_000,
help = "Timed tests. Every `BITS` worth of input is given a second of running time.",
)
#
# For normal tests (non-timed), `-fast-tests` cuts down on the number of iterations.
#
timed_or_fast.add_argument(
"-fast-tests",
help = "Cut down on the number of iterations of each test",
action = "store_true",
)
args = parser.parse_args()
EXIT_ON_FAIL = args.exit_on_fail
#
# How many iterations of each random test do we want to run?
#
BITS_AND_ITERATIONS = [
( 120, 10_000),
( 1_200, 1_000),
( 4_096, 100),
(12_000, 10),
]
if args.fast_tests:
for k in range(len(BITS_AND_ITERATIONS)):
b, i = BITS_AND_ITERATIONS[k]
BITS_AND_ITERATIONS[k] = (b, i // 10 if i >= 100 else 5)
if args.no_random:
BITS_AND_ITERATIONS = []
#
# Where is the DLL? If missing, build using: `odin build . -build-mode:shared`
#
if platform.system() == "Windows":
LIB_PATH = os.getcwd() + os.sep + "big.dll"
elif platform.system() == "Linux":
LIB_PATH = os.getcwd() + os.sep + "big.so"
elif platform.system() == "Darwin":
LIB_PATH = os.getcwd() + os.sep + "big.dylib"
else:
print("Platform is unsupported.")
exit(1)
TOTAL_TIME = 0
UNTIL_TIME = 0
UNTIL_ITERS = 0
def we_iterate():
if args.timed:
return TOTAL_TIME < UNTIL_TIME
else:
global UNTIL_ITERS
UNTIL_ITERS -= 1
return UNTIL_ITERS != -1
#
# Error enum values
#
class Error(Enum):
Okay = 0
Out_Of_Memory = 1
Invalid_Pointer = 2
Invalid_Argument = 3
Unknown_Error = 4
Max_Iterations_Reached = 5
Buffer_Overflow = 6
Integer_Overflow = 7
Division_by_Zero = 8
Math_Domain_Error = 9
Unimplemented = 127
#
# Disable garbage collection
#
gc.disable()
#
# Set up exported procedures
#
try:
l = cdll.LoadLibrary(LIB_PATH)
except:
print("Couldn't find or load " + LIB_PATH + ".")
exit(1)
def load(export_name, args, res):
export_name.argtypes = args
export_name.restype = res
return export_name
#
# Result values will be passed in a struct { res: cstring, err: Error }
#
class Res(Structure):
_fields_ = [("res", c_char_p), ("err", c_uint64)]
initialize_constants = load(l.test_initialize_constants, [], c_uint64)
print("initialize_constants: ", initialize_constants())
error_string = load(l.test_error_string, [c_byte], c_char_p)
add = load(l.test_add, [c_char_p, c_char_p ], Res)
sub = load(l.test_sub, [c_char_p, c_char_p ], Res)
mul = load(l.test_mul, [c_char_p, c_char_p ], Res)
sqr = load(l.test_sqr, [c_char_p ], Res)
div = load(l.test_div, [c_char_p, c_char_p ], Res)
# Powers and such
int_log = load(l.test_log, [c_char_p, c_longlong], Res)
int_pow = load(l.test_pow, [c_char_p, c_longlong], Res)
int_sqrt = load(l.test_sqrt, [c_char_p ], Res)
int_root_n = load(l.test_root_n, [c_char_p, c_longlong], Res)
# Logical operations
int_shl_digit = load(l.test_shl_digit, [c_char_p, c_longlong], Res)
int_shr_digit = load(l.test_shr_digit, [c_char_p, c_longlong], Res)
int_shl = load(l.test_shl, [c_char_p, c_longlong], Res)
int_shr = load(l.test_shr, [c_char_p, c_longlong], Res)
int_shr_signed = load(l.test_shr_signed, [c_char_p, c_longlong], Res)
int_factorial = load(l.test_factorial, [c_uint64 ], Res)
int_gcd = load(l.test_gcd, [c_char_p, c_char_p ], Res)
int_lcm = load(l.test_lcm, [c_char_p, c_char_p ], Res)
is_square = load(l.test_is_square, [c_char_p ], Res)
def test(test_name: "", res: Res, param=[], expected_error = Error.Okay, expected_result = "", radix=16):
passed = True
r = None
err = Error(res.err)
if err != expected_error:
error_loc = res.res.decode('utf-8')
error = "{}: {} in '{}'".format(test_name, err, error_loc)
if len(param):
error += " with params {}".format(param)
print(error, flush=True)
passed = False
elif err == Error.Okay:
r = None
try:
r = res.res.decode('utf-8')
r = int(res.res, radix)
except:
pass
if r != expected_result:
error = "{}: Result was '{}', expected '{}'".format(test_name, r, expected_result)
if len(param):
error += " with params {}".format(param)
print(error, flush=True)
passed = False
if EXIT_ON_FAIL and not passed: exit(res.err)
return passed
def arg_to_odin(a):
if a >= 0:
s = hex(a)[2:]
else:
s = '-' + hex(a)[3:]
return s.encode('utf-8')
def test_add(a = 0, b = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), arg_to_odin(b)]
res = add(*args)
expected_result = None
if expected_error == Error.Okay:
expected_result = a + b
return test("test_add", res, [a, b], expected_error, expected_result)
def test_sub(a = 0, b = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), arg_to_odin(b)]
res = sub(*args)
expected_result = None
if expected_error == Error.Okay:
expected_result = a - b
return test("test_sub", res, [a, b], expected_error, expected_result)
def test_mul(a = 0, b = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), arg_to_odin(b)]
try:
res = mul(*args)
except OSError as e:
print("{} while trying to multiply {} x {}.".format(e, a, b))
if EXIT_ON_FAIL: exit(3)
return False
expected_result = None
if expected_error == Error.Okay:
expected_result = a * b
return test("test_mul", res, [a, b], expected_error, expected_result)
def test_sqr(a = 0, b = 0, expected_error = Error.Okay):
args = [arg_to_odin(a)]
try:
res = sqr(*args)
except OSError as e:
print("{} while trying to square {}.".format(e, a))
if EXIT_ON_FAIL: exit(3)
return False
expected_result = None
if expected_error == Error.Okay:
expected_result = a * a
return test("test_sqr", res, [a], expected_error, expected_result)
def test_div(a = 0, b = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), arg_to_odin(b)]
try:
res = div(*args)
except OSError as e:
print("{} while trying divide to {} / {}.".format(e, a, b))
if EXIT_ON_FAIL: exit(3)
return False
expected_result = None
if expected_error == Error.Okay:
#
# We don't round the division results, so if one component is negative, we're off by one.
#
if a < 0 and b > 0:
expected_result = int(-(abs(a) // b))
elif b < 0 and a > 0:
expected_result = int(-(a // abs((b))))
else:
expected_result = a // b if b != 0 else None
return test("test_div", res, [a, b], expected_error, expected_result)
def test_log(a = 0, base = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), base]
res = int_log(*args)
expected_result = None
if expected_error == Error.Okay:
expected_result = int(math.log(a, base))
return test("test_log", res, [a, base], expected_error, expected_result)
def test_pow(base = 0, power = 0, expected_error = Error.Okay):
args = [arg_to_odin(base), power]
res = int_pow(*args)
expected_result = None
if expected_error == Error.Okay:
if power < 0:
expected_result = 0
else:
# NOTE(Jeroen): Don't use `math.pow`, it's a floating point approximation.
# Use built-in `pow` or `a**b` instead.
expected_result = pow(base, power)
return test("test_pow", res, [base, power], expected_error, expected_result)
def test_sqrt(number = 0, expected_error = Error.Okay):
args = [arg_to_odin(number)]
try:
res = int_sqrt(*args)
except OSError as e:
print("{} while trying to sqrt {}.".format(e, number))
if EXIT_ON_FAIL: exit(3)
return False
expected_result = None
if expected_error == Error.Okay:
if number < 0:
expected_result = 0
else:
expected_result = int(math.isqrt(number))
return test("test_sqrt", res, [number], expected_error, expected_result)
def root_n(number, root):
u, s = number, number + 1
while u < s:
s = u
t = (root-1) * s + number // pow(s, root - 1)
u = t // root
return s
def test_root_n(number = 0, root = 0, expected_error = Error.Okay):
args = [arg_to_odin(number), root]
res = int_root_n(*args)
expected_result = None
if expected_error == Error.Okay:
if number < 0:
expected_result = 0
else:
expected_result = root_n(number, root)
return test("test_root_n", res, [number, root], expected_error, expected_result)
def test_shl_digit(a = 0, digits = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), digits]
res = int_shl_digit(*args)
expected_result = None
if expected_error == Error.Okay:
expected_result = a << (digits * 60)
return test("test_shl_digit", res, [a, digits], expected_error, expected_result)
def test_shr_digit(a = 0, digits = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), digits]
res = int_shr_digit(*args)
expected_result = None
if expected_error == Error.Okay:
if a < 0:
# Don't pass negative numbers. We have a shr_signed.
return False
else:
expected_result = a >> (digits * 60)
return test("test_shr_digit", res, [a, digits], expected_error, expected_result)
def test_shl(a = 0, bits = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), bits]
res = int_shl(*args)
expected_result = None
if expected_error == Error.Okay:
expected_result = a << bits
return test("test_shl", res, [a, bits], expected_error, expected_result)
def test_shr(a = 0, bits = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), bits]
res = int_shr(*args)
expected_result = None
if expected_error == Error.Okay:
if a < 0:
# Don't pass negative numbers. We have a shr_signed.
return False
else:
expected_result = a >> bits
return test("test_shr", res, [a, bits], expected_error, expected_result)
def test_shr_signed(a = 0, bits = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), bits]
res = int_shr_signed(*args)
expected_result = None
if expected_error == Error.Okay:
expected_result = a >> bits
return test("test_shr_signed", res, [a, bits], expected_error, expected_result)
def test_factorial(number = 0, expected_error = Error.Okay):
print("Factorial:", number)
args = [number]
try:
res = int_factorial(*args)
except OSError as e:
print("{} while trying to factorial {}.".format(e, number))
if EXIT_ON_FAIL: exit(3)
return False
expected_result = None
if expected_error == Error.Okay:
expected_result = math.factorial(number)
return test("test_factorial", res, [number], expected_error, expected_result)
def test_gcd(a = 0, b = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), arg_to_odin(b)]
res = int_gcd(*args)
expected_result = None
if expected_error == Error.Okay:
expected_result = math.gcd(a, b)
return test("test_gcd", res, [a, b], expected_error, expected_result)
def test_lcm(a = 0, b = 0, expected_error = Error.Okay):
args = [arg_to_odin(a), arg_to_odin(b)]
res = int_lcm(*args)
expected_result = None
if expected_error == Error.Okay:
expected_result = math.lcm(a, b)
return test("test_lcm", res, [a, b], expected_error, expected_result)
def test_is_square(a = 0, b = 0, expected_error = Error.Okay):
args = [arg_to_odin(a)]
res = is_square(*args)
expected_result = None
if expected_error == Error.Okay:
expected_result = str(math.isqrt(a) ** 2 == a) if a > 0 else "False"
return test("test_is_square", res, [a], expected_error, expected_result)
# TODO(Jeroen): Make sure tests cover edge cases, fast paths, and so on.
#
# The last two arguments in tests are the expected error and expected result.
#
# The expected error defaults to None.
# By default the Odin implementation will be tested against the Python one.
# You can override that by supplying an expected result as the last argument instead.
TESTS = {
test_add: [
[ 1234, 5432],
],
test_sub: [
[ 1234, 5432],
],
test_mul: [
[ 1234, 5432],
[ 0xd3b4e926aaba3040e1c12b5ea553b5, 0x1a821e41257ed9281bee5bc7789ea7 ],
[ 1 << 21_105, 1 << 21_501 ],
],
test_sqr: [
[ 5432],
[ 0xd3b4e926aaba3040e1c12b5ea553b5 ],
],
test_div: [
[ 54321, 12345],
[ 55431, 0, Error.Division_by_Zero],
[ 12980742146337069150589594264770969721, 4611686018427387904 ],
[ 831956404029821402159719858789932422, 243087903122332132 ],
],
test_log: [
[ 3192, 1, Error.Invalid_Argument],
[ -1234, 2, Error.Math_Domain_Error],
[ 0, 2, Error.Math_Domain_Error],
[ 1024, 2],
],
test_pow: [
[ 0, -1, Error.Math_Domain_Error ], # Math
[ 0, 0 ], # 1
[ 0, 2 ], # 0
[ 42, -1,], # 0
[ 42, 1 ], # 1
[ 42, 0 ], # 42
[ 42, 2 ], # 42*42
],
test_sqrt: [
[ -1, Error.Invalid_Argument, ],
[ 42, Error.Okay, ],
[ 12345678901234567890, Error.Okay, ],
[ 1298074214633706907132624082305024, Error.Okay, ],
[ 686885735734829009541949746871140768343076607029752932751182108475420900392874228486622313727012705619148037570309621219533087263900443932890792804879473795673302686046941536636874184361869252299636701671980034458333859202703255467709267777184095435235980845369829397344182319113372092844648570818726316581751114346501124871729572474923695509057166373026411194094493240101036672016770945150422252961487398124677567028263059046193391737576836378376192651849283925197438927999526058932679219572030021792914065825542626400207956134072247020690107136531852625253942429167557531123651471221455967386267137846791963149859804549891438562641323068751514370656287452006867713758971418043865298618635213551059471668293725548570452377976322899027050925842868079489675596835389444833567439058609775325447891875359487104691935576723532407937236505941186660707032433807075470656782452889754501872408562496805517394619388777930253411467941214807849472083814447498068636264021405175653742244368865090604940094889189800007448083930490871954101880815781177612910234741529950538835837693870921008635195545246771593130784786737543736434086434015200264933536294884482218945403958647118802574342840790536176272341586020230110889699633073513016344826709214, Error.Okay, ],
],
test_root_n: [
[ 1298074214633706907132624082305024, 2, Error.Okay, ],
],
test_shl_digit: [
[ 3192, 1 ],
[ 1298074214633706907132624082305024, 2 ],
[ 1024, 3 ],
],
test_shr_digit: [
[ 3680125442705055547392, 1 ],
[ 1725436586697640946858688965569256363112777243042596638790631055949824, 2 ],
[ 219504133884436710204395031992179571, 2 ],
],
test_shl: [
[ 3192, 1 ],
[ 1298074214633706907132624082305024, 2 ],
[ 1024, 3 ],
],
test_shr: [
[ 3680125442705055547392, 1 ],
[ 1725436586697640946858688965569256363112777243042596638790631055949824, 2 ],
[ 219504133884436710204395031992179571, 2 ],
],
test_shr_signed: [
[ -611105530635358368578155082258244262, 12 ],
[ -149195686190273039203651143129455, 12 ],
[ 611105530635358368578155082258244262, 12 ],
[ 149195686190273039203651143129455, 12 ],
],
test_factorial: [
[ 6_000 ], # Regular factorial, see cutoff in common.odin.
[ 12_345 ], # Binary split factorial
],
test_gcd: [
[ 23, 25, ],
[ 125, 25, ],
[ 125, 0, ],
[ 0, 0, ],
[ 0, 125,],
],
test_lcm: [
[ 23, 25,],
[ 125, 25, ],
[ 125, 0, ],
[ 0, 0, ],
[ 0, 125,],
],
test_is_square: [
[ 12, ],
[ 92232459121502451677697058974826760244863271517919321608054113675118660929276431348516553336313179167211015633639725554914519355444316239500734169769447134357534241879421978647995614218985202290368055757891124109355450669008628757662409138767505519391883751112010824030579849970582074544353971308266211776494228299586414907715854328360867232691292422194412634523666770452490676515117702116926803826546868467146319938818238521874072436856528051486567230096290549225463582766830777324099589751817442141036031904145041055454639783559905920619197290800070679733841430619962318433709503256637256772215111521321630777950145713049902839937043785039344243357384899099910837463164007565230287809026956254332260375327814271845678201, ]
],
}
if not args.fast_tests:
TESTS[test_factorial].append(
# This one on its own takes around 800ms, so we exclude it for FAST_TESTS
[ 10_000 ],
)
total_passes = 0
total_failures = 0
#
# test_shr_signed also tests shr, so we're not going to test shr randomly.
#
RANDOM_TESTS = [
test_add, test_sub, test_mul, test_sqr, test_div,
test_log, test_pow, test_sqrt, test_root_n,
test_shl_digit, test_shr_digit, test_shl, test_shr_signed,
test_gcd, test_lcm, test_is_square,
]
SKIP_LARGE = [
test_pow, test_root_n, # test_gcd,
]
SKIP_LARGEST = []
# Untimed warmup.
for test_proc in TESTS:
for t in TESTS[test_proc]:
res = test_proc(*t)
if __name__ == '__main__':
print("\n---- math/big tests ----")
print()
max_name = 0
for test_proc in TESTS:
max_name = max(max_name, len(test_proc.__name__))
fmt_string = "{name:>{max_name}}: {count_pass:7,} passes and {count_fail:7,} failures in {timing:9.3f} ms."
fmt_string = fmt_string.replace("{max_name}", str(max_name))
for test_proc in TESTS:
count_pass = 0
count_fail = 0
TIMINGS = {}
for t in TESTS[test_proc]:
start = time.perf_counter()
res = test_proc(*t)
diff = time.perf_counter() - start
TOTAL_TIME += diff
if test_proc not in TIMINGS:
TIMINGS[test_proc] = diff
else:
TIMINGS[test_proc] += diff
if res:
count_pass += 1
total_passes += 1
else:
count_fail += 1
total_failures += 1
print(fmt_string.format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail, timing=TIMINGS[test_proc] * 1_000))
for BITS, ITERATIONS in BITS_AND_ITERATIONS:
print()
print("---- math/big with two random {bits:,} bit numbers ----".format(bits=BITS))
print()
#
# We've already tested up to the 10th root.
#
TEST_ROOT_N_PARAMS = [2, 3, 4, 5, 6]
for test_proc in RANDOM_TESTS:
if BITS > 1_200 and test_proc in SKIP_LARGE: continue
if BITS > 4_096 and test_proc in SKIP_LARGEST: continue
count_pass = 0
count_fail = 0
TIMINGS = {}
UNTIL_ITERS = ITERATIONS
if test_proc == test_root_n and BITS == 1_200:
UNTIL_ITERS /= 10
UNTIL_TIME = TOTAL_TIME + BITS / args.timed_bits
# We run each test for a second per 20k bits
index = 0
while we_iterate():
a = randint(-(1 << BITS), 1 << BITS)
b = randint(-(1 << BITS), 1 << BITS)
if test_proc == test_div:
# We've already tested division by zero above.
bits = int(BITS * 0.6)
b = randint(-(1 << bits), 1 << bits)
if b == 0:
b == 42
elif test_proc == test_log:
# We've already tested log's domain errors.
a = randint(1, 1 << BITS)
b = randint(2, 1 << 60)
elif test_proc == test_pow:
b = randint(1, 10)
elif test_proc == test_sqrt:
a = randint(1, 1 << BITS)
b = Error.Okay
elif test_proc == test_root_n:
a = randint(1, 1 << BITS)
b = TEST_ROOT_N_PARAMS[index]
index = (index + 1) % len(TEST_ROOT_N_PARAMS)
elif test_proc == test_shl_digit:
b = randint(0, 10);
elif test_proc == test_shr_digit:
a = abs(a)
b = randint(0, 10);
elif test_proc == test_shl:
b = randint(0, min(BITS, 120))
elif test_proc == test_shr_signed:
b = randint(0, min(BITS, 120))
elif test_proc == test_is_square:
a = randint(0, 1 << BITS)
else:
b = randint(0, 1 << BITS)
res = None
start = time.perf_counter()
res = test_proc(a, b)
diff = time.perf_counter() - start
TOTAL_TIME += diff
if test_proc not in TIMINGS:
TIMINGS[test_proc] = diff
else:
TIMINGS[test_proc] += diff
if res:
count_pass += 1; total_passes += 1
else:
count_fail += 1; total_failures += 1
print(fmt_string.format(name=test_proc.__name__, count_pass=count_pass, count_fail=count_fail, timing=TIMINGS[test_proc] * 1_000))
print()
print("---- THE END ----")
print()
print(fmt_string.format(name="total", count_pass=total_passes, count_fail=total_failures, timing=TOTAL_TIME * 1_000))
if total_failures:
exit(1)
+81
View File
@@ -0,0 +1,81 @@
//+ignore
package math_big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
import "core:fmt"
import "core:time"
Category :: enum {
itoa,
atoi,
factorial,
factorial_bin,
choose,
lsb,
ctz,
sqr,
bitfield_extract,
rm_trials,
};
Event :: struct {
ticks: time.Duration,
count: int,
cycles: u64,
}
Timings := [Category]Event{};
print_timings :: proc() {
duration :: proc(d: time.Duration) -> (res: string) {
switch {
case d < time.Microsecond:
return fmt.tprintf("%v ns", time.duration_nanoseconds(d));
case d < time.Millisecond:
return fmt.tprintf("%v µs", time.duration_microseconds(d));
case:
return fmt.tprintf("%v ms", time.duration_milliseconds(d));
}
}
for v in Timings {
if v.count > 0 {
fmt.println("\nTimings:");
break;
}
}
for v, i in Timings {
if v.count > 0 {
avg_ticks := time.Duration(f64(v.ticks) / f64(v.count));
avg_cycles := f64(v.cycles) / f64(v.count);
fmt.printf("\t%v: %s / %v cycles (avg), %s / %v cycles (total, %v calls)\n", i, duration(avg_ticks), avg_cycles, duration(v.ticks), v.cycles, v.count);
}
}
}
@(deferred_in_out=_SCOPE_END)
SCOPED_TIMING :: #force_inline proc(c: Category) -> (ticks: time.Tick, cycles: u64) {
cycles = time.read_cycle_counter();
ticks = time.tick_now();
return;
}
_SCOPE_END :: #force_inline proc(c: Category, ticks: time.Tick, cycles: u64) {
cycles_now := time.read_cycle_counter();
ticks_now := time.tick_now();
Timings[c].ticks = time.tick_diff(ticks, ticks_now);
Timings[c].cycles = cycles_now - cycles;
Timings[c].count += 1;
}
SCOPED_COUNT_ADD :: #force_inline proc(c: Category, count: int) {
Timings[c].count += count;
}
+1 -1
View File
@@ -1,6 +1,6 @@
package math_bits
import "intrinsics"
import "core:intrinsics"
U8_MIN :: 0;
U16_MIN :: 0;
+1 -1
View File
@@ -3,7 +3,7 @@ package math_fixed
import "core:math"
import "core:strconv"
import "intrinsics"
import "core:intrinsics"
_ :: intrinsics;
Fixed :: struct($Backing: typeid, $Fraction_Width: uint)
+1 -1
View File
@@ -1,6 +1,6 @@
package linalg
import "builtin"
import "core:builtin"
import "core:math"
radians :: proc(degrees: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
+1 -1
View File
@@ -1,7 +1,7 @@
package linalg
import "core:math"
import "intrinsics"
import "core:intrinsics"
// Generic
+1 -1
View File
@@ -1,6 +1,6 @@
package math
import "intrinsics"
import "core:intrinsics"
_ :: intrinsics;
Float_Class :: enum {
+1 -1
View File
@@ -95,7 +95,7 @@ int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 {
int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 {
if n <= 0 {
panic("Invalid argument to int63_max");
panic("Invalid argument to int127_max");
}
if n&(n-1) == 0 {
return int127(r) & (n-1);
+65 -34
View File
@@ -31,10 +31,11 @@ Allocator_Query_Info :: struct {
Allocator_Error :: runtime.Allocator_Error;
/*
Allocator_Error :: enum byte {
None = 0,
Out_Of_Memory = 1,
Invalid_Pointer = 2,
Invalid_Argument = 3,
None = 0,
Out_Of_Memory = 1,
Invalid_Pointer = 2,
Invalid_Argument = 3,
Mode_Not_Implemented = 4,
}
*/
Allocator_Proc :: runtime.Allocator_Proc;
@@ -121,7 +122,15 @@ resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_AL
return nil;
}
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
_ = err;
if err == .Mode_Not_Implemented {
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
if err != nil {
return nil;
}
runtime.copy(data, byte_slice(ptr, old_size));
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
return raw_data(data);
}
return raw_data(data);
}
@@ -140,7 +149,16 @@ resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_A
} else if ptr == nil {
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
}
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc);
if err == .Mode_Not_Implemented {
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc);
if err != nil {
return data, err;
}
runtime.copy(data, old_data);
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc);
}
return data, err;
}
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
@@ -189,51 +207,54 @@ delete :: proc{
};
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) {
return new_aligned(T, align_of(T), allocator, loc);
}
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> ^T {
ptr := (^T)(alloc(size_of(T), alignment, allocator, loc));
if ptr != nil { ptr^ = T{}; }
return ptr;
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
data := alloc_bytes(size_of(T), alignment, allocator, loc) or_return;
t = (^T)(raw_data(data));
return;
}
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
ptr := (^T)(alloc(size_of(T), align_of(T), allocator, loc));
if ptr != nil { ptr^ = data; }
return ptr;
data := alloc_bytes(size_of(T), alignment, allocator, loc) or_return;
t = (^T)(raw_data(data));
if t != nil {
t^ = data;
}
return;
}
DEFAULT_RESERVE_CAPACITY :: 16;
make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) {
runtime.make_slice_error_loc(loc, len);
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return;
if data == nil && size_of(E) != 0 {
return;
}
slice = transmute(T)Raw_Slice{raw_data(data), len};
return;
}
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_aligned(T, len, align_of(E), allocator, loc);
}
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
runtime.make_slice_error_loc(loc, len);
data := alloc(size_of(E)*len, alignment, allocator, loc);
if data == nil && size_of(E) != 0 {
return nil;
}
zero(data, size_of(E)*len);
s := Raw_Slice{data, len};
return transmute(T)s;
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc);
}
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
}
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_dynamic_array_len_cap(T, len, len, allocator, loc);
}
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) {
runtime.make_dynamic_array_error_loc(loc, len, cap);
data := alloc(size_of(E)*cap, align_of(E), allocator, loc);
s := Raw_Dynamic_Array{data, len, cap, allocator};
data := alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return;
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator};
if data == nil && size_of(E) != 0 {
s.len, s.cap = 0, 0;
}
zero(data, size_of(E)*len);
return transmute(T)s;
array = transmute(T)s;
return;
}
make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
runtime.make_map_expr_error_loc(loc, cap);
context.allocator = allocator;
@@ -241,6 +262,15 @@ make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := con
reserve_map(&m, cap);
return m;
}
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
runtime.make_slice_error_loc(loc, len);
data := alloc_bytes(size_of(E)*len, align_of(E), allocator, loc) or_return;
if data == nil && size_of(E) != 0 {
return;
}
mp = cast(T)raw_data(data);
return;
}
make :: proc{
make_slice,
@@ -248,6 +278,7 @@ make :: proc{
make_dynamic_array_len,
make_dynamic_array_len_cap,
make_map,
make_multi_pointer,
};
+13 -110
View File
@@ -1,6 +1,6 @@
package mem
import "intrinsics"
import "core:intrinsics"
import "core:runtime"
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
@@ -67,8 +67,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return byte_slice(ptr, size), nil;
case .Free:
// NOTE(bill): Free all at once
// Use Arena_Temp_Memory if you want to free a block
return nil, .Mode_Not_Implemented;
case .Free_All:
arena.offset = 0;
@@ -84,7 +83,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, nil;
case .Query_Info:
return nil, nil;
return nil, .Mode_Not_Implemented;
}
return nil, nil;
@@ -115,12 +114,13 @@ Scratch_Allocator :: struct {
leaked_allocations: [dynamic][]byte,
}
scratch_allocator_init :: proc(s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) {
s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator);
scratch_allocator_init :: proc(s: ^Scratch_Allocator, size: int, backup_allocator := context.allocator) -> Allocator_Error {
s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator) or_return;
s.curr_offset = 0;
s.prev_allocation = nil;
s.backup_allocator = backup_allocator;
s.leaked_allocations.allocator = backup_allocator;
return nil;
}
scratch_allocator_destroy :: proc(s: ^Scratch_Allocator) {
@@ -189,7 +189,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return ptr, err;
}
if s.leaked_allocations == nil {
s.leaked_allocations = make([dynamic][]byte, a);
s.leaked_allocations, err = make([dynamic][]byte, a);
}
append(&s.leaked_allocations, ptr);
@@ -262,7 +262,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, nil;
case .Query_Info:
return nil, nil;
return nil, .Mode_Not_Implemented;
}
return nil, nil;
@@ -426,7 +426,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
}
return nil, nil;
case .Query_Info:
return nil, nil;
return nil, .Mode_Not_Implemented;
}
return nil, nil;
@@ -561,7 +561,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, nil;
case .Query_Info:
return nil, nil;
return nil, .Mode_Not_Implemented;
}
return nil, nil;
@@ -602,7 +602,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
case .Alloc:
return dynamic_pool_alloc_bytes(pool, size);
case .Free:
return nil, nil;
return nil, .Mode_Not_Implemented;
case .Free_All:
dynamic_pool_free_all(pool);
return nil, nil;
@@ -786,7 +786,7 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, nil;
case .Query_Info:
return nil, nil;
panic("mem: panic allocator, .Query_Info called");
}
return nil, nil;
@@ -908,106 +908,9 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, nil;
case .Query_Info:
return nil, nil;
unreachable();
}
return result, err;
}
// Small_Allocator primary allocates memory from its local buffer of size BUFFER_SIZE
// If that buffer's memory is exhausted, it will use the backing allocator (a scratch allocator is recommended)
// Memory allocated with Small_Allocator cannot be freed individually using 'free' and must be freed using 'free_all'
Small_Allocator :: struct($BUFFER_SIZE: int)
where
BUFFER_SIZE >= 2*size_of(uintptr),
BUFFER_SIZE & (BUFFER_SIZE-1) == 0 {
buffer: [BUFFER_SIZE]byte,
backing: Allocator,
start: uintptr,
curr: uintptr,
end: uintptr,
chunk_size: int,
}
small_allocator :: proc(s: ^$S/Small_Allocator, backing := context.allocator) -> (a: Allocator) {
if s.backing.procedure == nil {
s.backing = backing;
}
a.data = s;
a.procedure = proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
s := (^S)(allocator_data);
if s.chunk_size <= 0 {
s.chunk_size = 4*1024;
}
if s.start == 0 {
s.start = uintptr(&s.buffer[0]);
s.curr = s.start;
s.end = s.start + uintptr(S.BUFFER_SIZE);
(^rawptr)(s.start)^ = nil;
s.curr += size_of(rawptr);
}
switch mode {
case .Alloc:
s.curr = align_forward_uintptr(s.curr, uintptr(alignment));
if size > int(s.end - s.curr) {
to_allocate := size_of(rawptr) + size + alignment;
if to_allocate < s.chunk_size {
to_allocate = s.chunk_size;
}
s.chunk_size *= 2;
p := alloc(to_allocate, 16, s.backing, loc);
(^rawptr)(s.start)^ = p;
s.start = uintptr(p);
s.curr = s.start;
s.end = s.start + uintptr(to_allocate);
(^rawptr)(s.start)^ = nil;
s.curr += size_of(rawptr);
s.curr = align_forward_uintptr(s.curr, uintptr(alignment));
}
p := rawptr(s.curr);
s.curr += uintptr(size);
return mem_zero(p, size);
case .Free:
// NOP
return nil;
case .Resize:
// No need copying the code
return default_resize_align(old_memory, old_size, size, alignment, small_allocator(s, s.backing), loc);
case .Free_All:
p := (^rawptr)(&s.buffer[0])^;
for p != nil {
next := (^rawptr)(p)^;
free(next, s.backing, loc);
p = next;
}
// Reset to default
s.start = uintptr(&s.buffer[0]);
s.curr = s.start;
s.end = s.start + uintptr(S.BUFFER_SIZE);
(^rawptr)(s.start)^ = nil;
s.curr += size_of(rawptr);
case .Query_Features:
return nil, nil;
case .Query_Info:
return nil, nil;
}
return nil, nil;
};
return a;
}
+8 -10
View File
@@ -135,18 +135,12 @@ ptr_sub :: proc(a, b: $P/^$T) -> int {
}
slice_ptr :: proc(ptr: ^$T, len: int) -> []T {
assert(len >= 0);
return transmute([]T)Raw_Slice{data = ptr, len = len};
return ([^]T)(ptr)[:len];
}
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
return ([^]u8)(data)[:max(len, 0)];
}
@(deprecated="use byte_slice")
slice_ptr_to_bytes :: proc(data: rawptr, len: int) -> []byte {
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
}
slice_to_bytes :: proc(slice: $E/[]$T) -> []byte {
s := transmute(Raw_Slice)slice;
@@ -245,6 +239,10 @@ context_from_allocator :: proc(a: Allocator) -> type_of(context) {
return context;
}
reinterpret_copy :: proc($T: typeid, ptr: rawptr) -> (value: T) {
copy(&value, ptr, size_of(T));
return;
}
Fixed_Byte_Buffer :: distinct [dynamic]byte;
@@ -291,8 +289,8 @@ calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int)
clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> T {
new_slice := make(T, len(slice), allocator, loc);
clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> (new_slice: T) {
new_slice, _ = make(T, len(slice), allocator, loc);
runtime.copy(new_slice, slice);
return new_slice;
}
+11
View File
@@ -58,3 +58,14 @@ raw_dynamic_array_data :: proc(a: $T/[dynamic]$E) -> ^E {
raw_data :: proc{raw_array_data, raw_string_data, raw_slice_data, raw_dynamic_array_data};
Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {
hash: uintptr,
next: int,
key: Key,
value: Value,
}
Poly_Raw_Map :: struct($Key, $Value: typeid) {
hashes: []int,
entries: [dynamic]Poly_Raw_Map_Entry(Key, Value),
}
+23 -11
View File
@@ -241,15 +241,6 @@ Field_Value :: struct {
value: ^Expr,
}
Ternary_Expr :: struct {
using node: Expr,
cond: ^Expr,
op1: tokenizer.Token,
x: ^Expr,
op2: tokenizer.Token,
y: ^Expr,
}
Ternary_If_Expr :: struct {
using node: Expr,
x: ^Expr,
@@ -261,13 +252,26 @@ Ternary_If_Expr :: struct {
Ternary_When_Expr :: struct {
using node: Expr,
x: ^Expr,
x: ^Expr,
op1: tokenizer.Token,
cond: ^Expr,
op2: tokenizer.Token,
y: ^Expr,
}
Or_Else_Expr :: struct {
using node: Expr,
x: ^Expr,
token: tokenizer.Token,
y: ^Expr,
}
Or_Return_Expr :: struct {
using node: Expr,
expr: ^Expr,
token: tokenizer.Token,
}
Type_Assertion :: struct {
using node: Expr,
expr: ^Expr,
@@ -542,7 +546,7 @@ Field_Flag :: enum {
No_Alias,
C_Vararg,
Auto_Cast,
In,
Any_Int,
Results,
Tags,
@@ -652,6 +656,14 @@ Pointer_Type :: struct {
elem: ^Expr,
}
Multi_Pointer_Type :: struct {
using node: Expr,
open: tokenizer.Pos,
pointer: tokenizer.Pos,
close: tokenizer.Pos,
elem: ^Expr,
}
Array_Type :: struct {
using node: Expr,
open: tokenizer.Pos,
+8 -5
View File
@@ -5,7 +5,7 @@ import "core:fmt"
import "core:odin/tokenizer"
new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
n := mem.new(T);
n, _ := mem.new(T);
n.pos = pos;
n.end = end;
n.derived = n^;
@@ -129,10 +129,6 @@ clone_node :: proc(node: ^Node) -> ^Node {
case Field_Value:
r.field = clone(r.field);
r.value = clone(r.value);
case Ternary_Expr:
r.cond = clone(r.cond);
r.x = clone(r.x);
r.y = clone(r.y);
case Ternary_If_Expr:
r.x = clone(r.x);
r.cond = clone(r.cond);
@@ -141,6 +137,11 @@ clone_node :: proc(node: ^Node) -> ^Node {
r.x = clone(r.x);
r.cond = clone(r.cond);
r.y = clone(r.y);
case Or_Else_Expr:
r.x = clone(r.x);
r.y = clone(r.y);
case Or_Return_Expr:
r.expr = clone(r.expr);
case Type_Assertion:
r.expr = clone(r.expr);
r.type = clone(r.type);
@@ -250,6 +251,8 @@ clone_node :: proc(node: ^Node) -> ^Node {
r.results = auto_cast clone(r.results);
case Pointer_Type:
r.elem = clone(r.elem);
case Multi_Pointer_Type:
r.elem = clone(r.elem);
case Array_Type:
r.len = clone(r.len);
r.elem = clone(r.elem);
+7 -4
View File
@@ -126,10 +126,6 @@ walk :: proc(v: ^Visitor, node: ^Node) {
case Field_Value:
walk(v, n.field);
walk(v, n.value);
case Ternary_Expr:
walk(v, n.cond);
walk(v, n.x);
walk(v, n.y);
case Ternary_If_Expr:
walk(v, n.x);
walk(v, n.cond);
@@ -138,6 +134,11 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.x);
walk(v, n.cond);
walk(v, n.y);
case Or_Else_Expr:
walk(v, n.x);
walk(v, n.y);
case Or_Return_Expr:
walk(v, n.expr);
case Type_Assertion:
walk(v, n.expr);
if n.type != nil {
@@ -348,6 +349,8 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.results);
case Pointer_Type:
walk(v, n.elem);
case Multi_Pointer_Type:
walk(v, n.elem);
case Array_Type:
if n.tag != nil {
walk(v, n.tag);
+2
View File
@@ -166,6 +166,7 @@ Type_Kind :: enum u32le {
SOA_Struct_Dynamic = 19,
Relative_Pointer = 20,
Relative_Slice = 21,
Multi_Pointer = 22,
}
Type_Elems_Cap :: 4;
@@ -222,6 +223,7 @@ Type :: struct {
// .Simd_Vector - 1 type: 0=element
// .Relative_Pointer - 2 types: 0=pointer type, 1=base integer
// .Relative_Slice - 2 types: 0=slice type, 1=base integer
// .Multi_Pointer - 1 type: 0=element
types: Array(Type_Index),
// Used by:
+264 -83
View File
@@ -44,8 +44,13 @@ Parser :: struct {
curr_proc: ^ast.Node,
error_count: int,
fix_count: int,
fix_prev_pos: tokenizer.Pos,
}
MAX_FIX_COUNT :: 10;
Stmt_Allow_Flag :: enum {
In,
Label,
@@ -140,9 +145,7 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
p.line_comment = nil;
}
if .Optional_Semicolons in p.flags {
p.tok.flags += {.Insert_Semicolon};
}
p.tok.flags += {.Insert_Semicolon};
p.file = file;
tokenizer.init(&p.tok, file.src, file.fullpath, p.err);
@@ -229,7 +232,7 @@ peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
return;
}
skip_possible_newline :: proc(p: ^Parser) -> bool {
if .Insert_Semicolon not_in p.tok.flags {
if .Optional_Semicolons not_in p.flags {
return false;
}
@@ -242,7 +245,7 @@ skip_possible_newline :: proc(p: ^Parser) -> bool {
}
skip_possible_newline_for_literal :: proc(p: ^Parser) -> bool {
if .Insert_Semicolon not_in p.tok.flags {
if .Optional_Semicolons not_in p.flags {
return false;
}
@@ -371,11 +374,14 @@ expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string)
expect_operator :: proc(p: ^Parser) -> tokenizer.Token {
prev := p.curr_tok;
if prev.kind == .If || prev.kind == .When {
#partial switch prev.kind {
case .If, .When, .Or_Else:
// okay
} else if !tokenizer.is_operator(prev.kind) {
g := tokenizer.token_to_string(prev);
error(p, prev.pos, "expected an operator, got '%s'", g);
case:
if !tokenizer.is_operator(prev.kind) {
g := tokenizer.token_to_string(prev);
error(p, prev.pos, "expected an operator, got '%s'", g);
}
}
advance_token(p);
return prev;
@@ -389,6 +395,30 @@ allow_token :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> bool {
return false;
}
end_of_line_pos :: proc(p: ^Parser, tok: tokenizer.Token) -> tokenizer.Pos {
offset := clamp(tok.pos.offset, 0, len(p.tok.src)-1);
s := p.tok.src[offset:];
pos := tok.pos;
pos.column -= 1;
for len(s) != 0 && s[0] != 0 && s[0] != '\n' {
s = s[1:];
pos.column += 1;
}
return pos;
}
expect_closing_brace_of_field_list :: proc(p: ^Parser) -> tokenizer.Token {
token := p.curr_tok;
if allow_token(p, .Close_Brace) {
return token;
}
if allow_token(p, .Semicolon) {
str := tokenizer.token_to_string(token);
error(p, end_of_line_pos(p, p.prev_tok), "expected a comma, got %s", p);
}
return expect_token(p, .Close_Brace);
}
is_blank_ident :: proc{
is_blank_ident_string,
@@ -411,6 +441,33 @@ is_blank_ident_node :: proc(node: ^ast.Node) -> bool {
return true;
}
fix_advance_to_next_stmt :: proc(p: ^Parser) {
for {
#partial switch t := p.curr_tok; t.kind {
case .EOF, .Semicolon:
return;
case .Package, .Foreign, .Import,
.If, .For, .When, .Return, .Switch,
.Defer, .Using,
.Break, .Continue, .Fallthrough,
.Hash:
if t.pos == p.fix_prev_pos && p.fix_count < MAX_FIX_COUNT {
p.fix_count += 1;
return;
}
if t.pos.offset < p.fix_prev_pos.offset {
p.fix_prev_pos = t.pos;
p.fix_count = 0;
return;
}
}
advance_token(p);
}
}
is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
if node == nil {
@@ -526,6 +583,7 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
}
error(p, prev.pos, "expected ';', got %s", tokenizer.token_to_string(p.curr_tok));
fix_advance_to_next_stmt(p);
return false;
}
@@ -597,12 +655,16 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
}
if allow_token(p, .Do) {
body = convert_stmt_to_body(p, parse_stmt(p));
if cond.pos.line != body.pos.line {
error(p, body.pos, "the body of a 'do' must be on the same line as when statement");
}
} else {
body = parse_block_stmt(p, true);
}
skip_possible_newline_for_literal(p);
if allow_token(p, .Else) {
if p.curr_tok.kind == .Else {
else_tok := expect_token(p, .Else);
#partial switch p.curr_tok.kind {
case .When:
else_stmt = parse_when_stmt(p);
@@ -611,6 +673,9 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
case .Do:
expect_token(p, .Do);
else_stmt = convert_stmt_to_body(p, parse_stmt(p));
if else_tok.pos.line != else_stmt.pos.line {
error(p, else_stmt.pos, "the body of a 'do' must be on the same line as 'else'");
}
case:
error(p, p.curr_tok.pos, "expected when statement block statement");
else_stmt = ast.new(ast.Bad_Stmt, p.curr_tok.pos, end_pos(p.curr_tok));
@@ -673,6 +738,9 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
}
if allow_token(p, .Do) {
body = convert_stmt_to_body(p, parse_stmt(p));
if cond.pos.line != body.pos.line {
error(p, body.pos, "the body of a 'do' must be on the same line as the if condition");
}
} else {
body = parse_block_stmt(p, false);
}
@@ -680,7 +748,8 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
else_tok := p.curr_tok.pos;
skip_possible_newline_for_literal(p);
if allow_token(p, .Else) {
if p.curr_tok.kind == .Else {
else_tok := expect_token(p, .Else);
#partial switch p.curr_tok.kind {
case .If:
else_stmt = parse_if_stmt(p);
@@ -689,6 +758,9 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
case .Do:
expect_token(p, .Do);
else_stmt = convert_stmt_to_body(p, parse_stmt(p));
if else_tok.pos.line != else_stmt.pos.line {
error(p, body.pos, "the body of a 'do' must be on the same line as 'else'");
}
case:
error(p, p.curr_tok.pos, "expected if statement block statement");
else_stmt = ast.new(ast.Bad_Stmt, p.curr_tok.pos, end_pos(p.curr_tok));
@@ -750,6 +822,10 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
if allow_token(p, .Do) {
body = convert_stmt_to_body(p, parse_stmt(p));
if tok.pos.line != body.pos.line {
error(p, body.pos, "the body of a 'do' must be on the same line as 'else'");
}
} else {
body = parse_body(p);
}
@@ -772,18 +848,33 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
if !is_range && parse_control_statement_semicolon_separator(p) {
init = cond;
cond = nil;
if p.curr_tok.kind != .Semicolon {
cond = parse_simple_stmt(p, nil);
}
expect_semicolon(p, cond);
if p.curr_tok.kind != .Open_Brace && p.curr_tok.kind != .Do {
post = parse_simple_stmt(p, nil);
if p.curr_tok.kind == .Open_Brace || p.curr_tok.kind == .Do {
error(p, p.curr_tok.pos, "Expected ';', followed by a condition expression and post statement, got %s", tokenizer.tokens[p.curr_tok.kind]);
} else {
if p.curr_tok.kind != .Semicolon {
cond = parse_simple_stmt(p, nil);
}
if p.curr_tok.text != ";" {
error(p, p.curr_tok.pos, "Expected ';', got %s", tokenizer.token_to_string(p.curr_tok));
} else {
expect_semicolon(p, nil);
}
if p.curr_tok.kind != .Open_Brace && p.curr_tok.kind != .Do {
post = parse_simple_stmt(p, nil);
}
}
}
}
if allow_token(p, .Do) {
body = convert_stmt_to_body(p, parse_stmt(p));
if tok.pos.line != body.pos.line {
error(p, body.pos, "the body of a 'do' must be on the same line as the 'for' token");
}
} else {
body = parse_body(p);
}
@@ -1129,6 +1220,9 @@ parse_unrolled_for_loop :: proc(p: ^Parser, inline_tok: tokenizer.Token) -> ^ast
if allow_token(p, .Do) {
body = convert_stmt_to_body(p, parse_stmt(p));
if for_tok.pos.line != body.pos.line {
error(p, body.pos, "the body of a 'do' must be on the same line as the 'for' token");
}
} else {
body = parse_block_stmt(p, false);
}
@@ -1150,7 +1244,6 @@ parse_unrolled_for_loop :: proc(p: ^Parser, inline_tok: tokenizer.Token) -> ^ast
parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
#partial switch p.curr_tok.kind {
case .Inline:
if peek_token_kind(p, .For) {
inline_tok := expect_token(p, .Inline);
@@ -1158,15 +1251,15 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
}
fallthrough;
// Operands
case .Context, // Also allows for 'context = '
case .No_Inline,
.Context, // Also allows for 'context = '
.Proc,
.No_Inline,
.Asm, // Inline assembly
.Ident,
.Integer, .Float, .Imag,
.Rune, .String,
.Open_Paren,
.Pointer,
.Asm, // Inline assembly
// Unary Expressions
.Add, .Sub, .Xor, .Not, .And:
@@ -1175,8 +1268,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
return s;
case .Import: return parse_import_decl(p);
case .Foreign: return parse_foreign_decl(p);
case .Import: return parse_import_decl(p);
case .If: return parse_if_stmt(p);
case .When: return parse_when_stmt(p);
case .For: return parse_for_stmt(p);
@@ -1288,9 +1381,9 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
stmt := parse_stmt(p);
switch name {
case "bounds_check":
stmt.state_flags |= {.Bounds_Check};
stmt.state_flags += {.Bounds_Check};
case "no_bounds_check":
stmt.state_flags |= {.No_Bounds_Check};
stmt.state_flags += {.No_Bounds_Check};
}
return stmt;
case "partial":
@@ -1309,6 +1402,12 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
es := ast.new(ast.Expr_Stmt, ce.pos, ce.end);
es.expr = ce;
return es;
case "force_inline", "force_no_inline":
expr := parse_inlining_operand(p, true, tok);
es := ast.new(ast.Expr_Stmt, expr.pos, expr.end);
es.expr = expr;
return es;
case "unroll":
return parse_unrolled_for_loop(p, tag);
case "include":
@@ -1320,6 +1419,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
te.op = tok;
te.name = name;
te.stmt = stmt;
fix_advance_to_next_stmt(p);
return te;
}
case .Open_Brace:
@@ -1331,8 +1432,31 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
return s;
}
#partial switch p.curr_tok.kind {
case .Else:
token := expect_token(p, .Else);
error(p, token.pos, "'else' unattached to an 'if' statement");
#partial switch p.curr_tok.kind {
case .If:
return parse_if_stmt(p);
case .When:
return parse_when_stmt(p);
case .Open_Brace:
return parse_block_stmt(p, true);
case .Do:
expect_token(p, .Do);
return convert_stmt_to_body(p, parse_stmt(p));
case:
fix_advance_to_next_stmt(p);
return ast.new(ast.Bad_Stmt, token.pos, end_pos(p.curr_tok));
}
}
tok := advance_token(p);
error(p, tok.pos, "expected a statement, got %s", tokenizer.token_to_string(tok));
fix_advance_to_next_stmt(p);
s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok));
return s;
}
@@ -1340,7 +1464,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
token_precedence :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> int {
#partial switch kind {
case .Question, .If, .When:
case .Question, .If, .When, .Or_Else:
return 1;
case .Ellipsis, .Range_Half, .Range_Full:
if !p.allow_range {
@@ -1469,8 +1593,8 @@ Field_Prefix :: enum {
Using,
No_Alias,
C_Vararg,
In,
Auto_Cast,
Any_Int,
}
Field_Prefixes :: distinct bit_set[Field_Prefix];
@@ -1517,19 +1641,15 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
}
is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
using Field_Prefix;
#partial switch p.curr_tok.kind {
case .EOF:
return Invalid;
return .Invalid;
case .Using:
advance_token(p);
return Using;
case .In:
advance_token(p);
return In;
return .Using;
case .Auto_Cast:
advance_token(p);
return Auto_Cast;
return .Auto_Cast;
case .Hash:
advance_token(p);
defer advance_token(p);
@@ -1537,14 +1657,16 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
case .Ident:
switch p.curr_tok.text {
case "no_alias":
return No_Alias;
return .No_Alias;
case "c_vararg":
return C_Vararg;
return .C_Vararg;
case "any_int":
return .Any_Int;
}
}
return Unknown;
return .Unknown;
}
return Invalid;
return .Invalid;
}
parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
@@ -1568,24 +1690,23 @@ parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
for kind in Field_Prefix {
count := counts[kind];
using Field_Prefix;
switch kind {
case Invalid, Unknown: // Ignore
case Using:
case .Invalid, .Unknown: // Ignore
case .Using:
if count > 1 { error(p, p.curr_tok.pos, "multiple 'using' in this field list"); }
if count > 0 { flags |= {.Using}; }
case No_Alias:
if count > 0 { flags += {.Using}; }
case .No_Alias:
if count > 1 { error(p, p.curr_tok.pos, "multiple '#no_alias' in this field list"); }
if count > 0 { flags |= {.No_Alias}; }
case C_Vararg:
if count > 0 { flags += {.No_Alias}; }
case .C_Vararg:
if count > 1 { error(p, p.curr_tok.pos, "multiple '#c_vararg' in this field list"); }
if count > 0 { flags |= {.C_Vararg}; }
case In:
if count > 1 { error(p, p.curr_tok.pos, "multiple 'in' in this field list"); }
if count > 0 { flags |= {.In}; }
case Auto_Cast:
if count > 0 { flags += {.C_Vararg}; }
case .Auto_Cast:
if count > 1 { error(p, p.curr_tok.pos, "multiple 'auto_cast' in this field list"); }
if count > 0 { flags |= {.Auto_Cast}; }
if count > 0 { flags += {.Auto_Cast}; }
case .Any_Int:
if count > 1 { error(p, p.curr_tok.pos, "multiple '#any_int' in this field list"); }
if count > 0 { flags += {.Any_Int}; }
}
}
@@ -1596,7 +1717,7 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se
flags = set_flags;
if name_count > 1 && .Using in flags {
error(p, p.curr_tok.pos, "cannot apply 'using' to more than one of the same type");
flags &~= {.Using};
flags -= {.Using};
}
for flag in ast.Field_Flag {
@@ -1610,12 +1731,12 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se
error(p, p.curr_tok.pos, "'#c_vararg' is not allowed within this field list");
case .Auto_Cast:
error(p, p.curr_tok.pos, "'auto_cast' is not allowed within this field list");
case .In:
error(p, p.curr_tok.pos, "'in' is not allowed within this field list");
case .Any_Int:
error(p, p.curr_tok.pos, "'#any_int' is not allowed within this field list");
case .Tags, .Ellipsis, .Results, .Default_Parameters, .Typeid_Token:
panic("Impossible prefixes");
}
flags &~= {flag};
flags -= {flag};
}
}
@@ -1953,10 +2074,10 @@ parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
ident := expect_token(p, .Ident);
switch ident.text {
case "bounds_check": tags |= {.Bounds_Check};
case "no_bounds_check": tags |= {.No_Bounds_Check};
case "optional_ok": tags |= {.Optional_Ok};
case "optional_second": tags |= {.Optional_Second};
case "bounds_check": tags += {.Bounds_Check};
case "no_bounds_check": tags += {.No_Bounds_Check};
case "optional_ok": tags += {.Optional_Ok};
case "optional_second": tags += {.Optional_Second};
case:
}
}
@@ -2158,12 +2279,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
switch name.text {
case "bounds_check":
operand.state_flags |= {.Bounds_Check};
operand.state_flags += {.Bounds_Check};
if .No_Bounds_Check in operand.state_flags {
error(p, name.pos, "#bounds_check and #no_bounds_check cannot be applied together");
}
case "no_bounds_check":
operand.state_flags |= {.No_Bounds_Check};
operand.state_flags += {.No_Bounds_Check};
if .Bounds_Check in operand.state_flags {
error(p, name.pos, "#bounds_check and #no_bounds_check cannot be applied together");
}
@@ -2232,9 +2353,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
}
type := parse_proc_type(p, tok);
tags := parse_proc_tags(p);
type.tags = tags;
tags: ast.Proc_Tags;
where_token: tokenizer.Token;
where_clauses: []^ast.Expr;
@@ -2246,8 +2365,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
p.expr_level = -1;
where_clauses = parse_rhs_expr_list(p);
p.expr_level = prev_level;
tags = parse_proc_tags(p);
}
tags = parse_proc_tags(p);
type.tags = tags;
if p.allow_type && p.expr_level < 0 {
if where_token.kind != .Invalid {
error(p, where_token.pos, "'where' clauses are not allowed on procedure types");
@@ -2273,6 +2394,9 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
p.curr_proc = type;
body = convert_stmt_to_body(p, parse_stmt(p));
p.curr_proc = prev_proc;
if type.pos.line != body.pos.line {
error(p, body.pos, "the body of a 'do' must be on the same line as the signature");
}
} else {
return type;
}
@@ -2316,18 +2440,26 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
tok := expect_token(p, .Pointer);
elem := parse_type(p);
ptr := ast.new(ast.Pointer_Type, tok.pos, elem.end);
ptr.pointer = tok.pos;
ptr.elem = elem;
return ptr;
case .Open_Bracket:
open := expect_token(p, .Open_Bracket);
count: ^ast.Expr;
if p.curr_tok.kind == .Question {
tok := expect_token(p, .Question);
q := ast.new(ast.Unary_Expr, tok.pos, end_pos(tok));
q.op = tok;
count = q;
} else if p.curr_tok.kind == .Dynamic {
#partial switch p.curr_tok.kind {
case .Pointer:
tok := expect_token(p, .Pointer);
close := expect_token(p, .Close_Bracket);
elem := parse_type(p);
t := ast.new(ast.Multi_Pointer_Type, open.pos, elem.end);
t.open = open.pos;
t.pointer = tok.pos;
t.close = close.pos;
t.elem = elem;
return t;
case .Dynamic:
tok := expect_token(p, .Dynamic);
close := expect_token(p, .Close_Bracket);
elem := parse_type(p);
@@ -2336,12 +2468,18 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
da.dynamic_pos = tok.pos;
da.close = close.pos;
da.elem = elem;
return da;
} else if p.curr_tok.kind != .Close_Bracket {
case .Question:
tok := expect_token(p, .Question);
q := ast.new(ast.Unary_Expr, tok.pos, end_pos(tok));
q.op = tok;
count = q;
case:
p.expr_level += 1;
count = parse_expr(p, false);
p.expr_level -= 1;
case .Close_Bracket:
// handle below
}
close := expect_token(p, .Close_Bracket);
elem := parse_type(p);
@@ -2432,7 +2570,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
skip_possible_newline_for_literal(p);
expect_token(p, .Open_Brace);
fields, name_count = parse_field_list(p, .Close_Brace, ast.Field_Flags_Struct);
close := expect_token(p, .Close_Brace);
close := expect_closing_brace_of_field_list(p);
st := ast.new(ast.Struct_Type, tok.pos, end_pos(close));
st.poly_params = poly_params;
@@ -2495,11 +2633,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
p.expr_level = where_prev_level;
}
variants: [dynamic]^ast.Expr;
skip_possible_newline_for_literal(p);
expect_token_after(p, .Open_Brace, "union");
variants: [dynamic]^ast.Expr;
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
type := parse_type(p);
if _, ok := type.derived.(ast.Bad_Expr); !ok {
@@ -2510,7 +2648,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
}
}
close := expect_token(p, .Close_Brace);
close := expect_closing_brace_of_field_list(p);
ut := ast.new(ast.Union_Type, tok.pos, end_pos(close));
ut.poly_params = poly_params;
@@ -2532,7 +2670,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
skip_possible_newline_for_literal(p);
open := expect_token(p, .Open_Brace);
fields := parse_elem_list(p);
close := expect_token(p, .Close_Brace);
close := expect_closing_brace_of_field_list(p);
et := ast.new(ast.Enum_Type, tok.pos, end_pos(close));
et.base_type = base_type;
@@ -2634,7 +2772,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
expect_token(p, .Comma);
constraints_string := parse_expr(p, false);
allow_token(p, .Comma);
close := expect_token(p, .Close_Brace);
close := expect_closing_brace_of_field_list(p);
e := ast.new(ast.Inline_Asm_Expr, tok.pos, end_pos(close));
e.tok = tok;
@@ -2811,8 +2949,8 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
return nil;
}
error(p, p.curr_tok.pos, "expected an operand");
fix_advance_to_next_stmt(p);
be := ast.new(ast.Bad_Expr, p.curr_tok.pos, end_pos(p.curr_tok));
advance_token(p);
operand = be;
}
@@ -2952,6 +3090,14 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
operand = deref;
case .Or_Return:
token := expect_token(p, .Or_Return);
oe := ast.new(ast.Or_Return_Expr, operand.pos, end_pos(token));
oe.expr = operand;
oe.token = token;
operand = oe;
case .Open_Brace:
if !is_lhs && is_literal_type(operand) && p.expr_level >= 0 {
operand = parse_literal_value(p, operand);
@@ -2959,6 +3105,13 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
loop = false;
}
case .Increment, .Decrement:
if !lhs {
tok := advance_token(p);
error(p, tok.pos, "postfix '%s' operator is not supported", tok.text);
} else {
loop = false;
}
}
is_lhs = false;
@@ -3008,6 +3161,16 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
ue.expr = expr;
return ue;
case .Increment, .Decrement:
op := advance_token(p);
error(p, op.pos, "unary '%s' operator is not supported", op.text);
expr := parse_unary_expr(p, lhs);
ue := ast.new(ast.Unary_Expr, op.pos, expr.end);
ue.op = op;
ue.expr = expr;
return ue;
case .Period:
op := advance_token(p);
field := parse_ident(p);
@@ -3027,16 +3190,18 @@ parse_binary_expr :: proc(p: ^Parser, lhs: bool, prec_in: int) -> ^ast.Expr {
}
for prec := token_precedence(p, p.curr_tok.kind); prec >= prec_in; prec -= 1 {
for {
loop: for {
op := p.curr_tok;
op_prec := token_precedence(p, op.kind);
if op_prec != prec {
break;
break loop;
}
if op.kind == .If || op.kind == .When {
#partial switch op.kind {
case .If, .When:
if p.prev_tok.pos.line < op.pos.line {
// NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition
break;
break loop;
}
}
@@ -3049,7 +3214,7 @@ parse_binary_expr :: proc(p: ^Parser, lhs: bool, prec_in: int) -> ^ast.Expr {
x := parse_expr(p, lhs);
colon := expect_token(p, .Colon);
y := parse_expr(p, lhs);
te := ast.new(ast.Ternary_Expr, expr.pos, end_pos(p.prev_tok));
te := ast.new(ast.Ternary_If_Expr, expr.pos, end_pos(p.prev_tok));
te.cond = cond;
te.op1 = op;
te.x = x;
@@ -3083,6 +3248,16 @@ parse_binary_expr :: proc(p: ^Parser, lhs: bool, prec_in: int) -> ^ast.Expr {
te.y = y;
expr = te;
case .Or_Else:
x := expr;
y := parse_expr(p, lhs);
oe := ast.new(ast.Or_Else_Expr, expr.pos, end_pos(p.prev_tok));
oe.x = x;
oe.token = op;
oe.y = y;
expr = oe;
case:
right := parse_binary_expr(p, false, prec+1);
if right == nil {
@@ -3193,6 +3368,12 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
return ast.new(ast.Bad_Stmt, start_tok.pos, end_pos(p.curr_tok));
}
#partial switch op.kind {
case .Increment, .Decrement:
advance_token(p);
error(p, op.pos, "postfix '%s' statement is not supported", op.text);
}
es := ast.new(ast.Expr_Stmt, lhs[0].pos, lhs[0].end);
es.expr = lhs[0];
return es;
+7 -6
View File
@@ -944,12 +944,6 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
case Auto_Cast:
push_generic_token(p, v.op.kind, 1);
visit_expr(p, v.expr);
case Ternary_Expr:
visit_expr(p, v.cond);
push_generic_token(p, v.op1.kind, 1);
visit_expr(p, v.x);
push_generic_token(p, v.op2.kind, 1);
visit_expr(p, v.y);
case Ternary_If_Expr:
visit_expr(p, v.x);
push_generic_token(p, v.op1.kind, 1);
@@ -962,6 +956,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_expr(p, v.cond);
push_generic_token(p, v.op2.kind, 1);
visit_expr(p, v.y);
case Or_Else_Expr:
visit_expr(p, v.x);
push_generic_token(p, v.token.kind, 1);
visit_expr(p, v.y);
case Or_Return_Expr:
visit_expr(p, v.expr);
push_generic_token(p, v.token.kind, 1);
case Selector_Call_Expr:
visit_expr(p, v.call.expr);
push_generic_token(p, .Open_Paren, 1);
+12 -6
View File
@@ -83,6 +83,8 @@ Token_Kind :: enum u32 {
Cmp_Or_Eq, // ||=
B_Assign_Op_End,
Increment, // ++
Decrement, // --
Arrow_Right, // ->
Undef, // ---
@@ -108,7 +110,6 @@ Token_Kind :: enum u32 {
Ellipsis, // ..
Range_Half, // ..<
Range_Full, // ..=
Back_Slash, // \
B_Operator_End,
B_Keyword_Begin,
@@ -143,10 +144,12 @@ Token_Kind :: enum u32 {
Transmute, // transmute
Distinct, // distinct
Using, // using
Context, // context
Or_Else, // or_else
Or_Return, // or_return
Asm, // asm
Inline, // inline
No_Inline, // no_inline
Context, // context
Asm, // asm
B_Keyword_End,
COUNT,
@@ -210,6 +213,8 @@ tokens := [Token_Kind.COUNT]string {
"||=",
"",
"++",
"--",
"->",
"---",
@@ -235,7 +240,6 @@ tokens := [Token_Kind.COUNT]string {
"..",
"..<",
"..=",
"\\",
"",
"",
@@ -270,10 +274,12 @@ tokens := [Token_Kind.COUNT]string {
"transmute",
"distinct",
"using",
"context",
"or_else",
"or_return",
"asm",
"inline",
"no_inline",
"context",
"asm",
"",
};
+210 -137
View File
@@ -114,17 +114,23 @@ peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
}
skip_whitespace :: proc(t: ^Tokenizer) {
for {
switch t.ch {
case ' ', '\t', '\r':
advance_rune(t);
case '\n':
if t.insert_semicolon {
if t.insert_semicolon {
for {
switch t.ch {
case ' ', '\t', '\r':
advance_rune(t);
case:
return;
}
}
} else {
for {
switch t.ch {
case ' ', '\t', '\r', '\n':
advance_rune(t);
case:
return;
}
advance_rune(t);
case:
return;
}
}
}
@@ -134,7 +140,7 @@ is_letter :: proc(r: rune) -> bool {
switch r {
case '_':
return true;
case 'A'..'Z', 'a'..'z':
case 'A'..='Z', 'a'..='z':
return true;
}
}
@@ -250,11 +256,11 @@ scan_raw_string :: proc(t: ^Tokenizer) -> string {
digit_val :: proc(r: rune) -> int {
switch r {
case '0'..'9':
case '0'..='9':
return int(r-'0');
case 'A'..'F':
case 'A'..='F':
return int(r-'A' + 10);
case 'a'..'f':
case 'a'..='f':
return int(r-'a' + 10);
}
return 16;
@@ -270,7 +276,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
advance_rune(t);
return true;
case '0'..'7':
case '0'..='7':
n, base, max = 3, 8, 255;
case 'x':
advance_rune(t);
@@ -465,58 +471,21 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, str
scan :: proc(t: ^Tokenizer) -> Token {
switch2 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind) -> Token_Kind {
if t.ch == '=' {
advance_rune(t);
return tok1;
}
return tok0;
}
switch3 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2: Token_Kind) -> Token_Kind {
if t.ch == '=' {
advance_rune(t);
return tok1;
}
if t.ch == ch2 {
advance_rune(t);
return tok2;
}
return tok0;
}
switch4 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2, tok3: Token_Kind) -> Token_Kind {
if t.ch == '=' {
advance_rune(t);
return tok1;
}
if t.ch == ch2 {
advance_rune(t);
if t.ch == '=' {
advance_rune(t);
return tok3;
}
return tok2;
}
return tok0;
}
skip_whitespace(t);
offset := t.offset;
kind: Token_Kind;
lit: string;
lit: string;
pos := offset_to_pos(t, offset);
insert_semicolon := false;
switch ch := t.ch; true {
case is_letter(ch):
lit = scan_identifier(t);
kind = .Ident;
check_keyword: if len(lit) > 1 {
// TODO(bill): Maybe have a hash table lookup rather than this linear search
for i in Token_Kind.B_Keyword_Begin .. Token_Kind.B_Keyword_End {
for i in Token_Kind.B_Keyword_Begin ..= Token_Kind.B_Keyword_End {
if lit == tokens[i] {
kind = Token_Kind(i);
break check_keyword;
@@ -528,14 +497,9 @@ scan :: proc(t: ^Tokenizer) -> Token {
break check_keyword;
}
}
#partial switch kind {
case .Ident, .Context, .Typeid, .Break, .Continue, .Fallthrough, .Return:
insert_semicolon = true;
}
break check_keyword;
}
case '0' <= ch && ch <= '9':
insert_semicolon = true;
kind, lit = scan_number(t, false);
case:
advance_rune(t);
@@ -546,118 +510,227 @@ scan :: proc(t: ^Tokenizer) -> Token {
t.insert_semicolon = false;
kind = .Semicolon;
lit = "\n";
return Token{kind, lit, pos};
}
case '\n':
t.insert_semicolon = false;
kind = .Semicolon;
lit = "\n";
case '"':
insert_semicolon = true;
kind = .String;
lit = scan_string(t);
case '\\':
if .Insert_Semicolon in t.flags {
t.insert_semicolon = false;
}
token := scan(t);
if token.pos.line == pos.line {
error(t, token.pos.offset, "expected a newline after \\");
}
return token;
case '\'':
insert_semicolon = true;
kind = .Rune;
lit = scan_rune(t);
case '"':
kind = .String;
lit = scan_string(t);
case '`':
insert_semicolon = true;
kind = .String;
lit = scan_raw_string(t);
case '=': kind = switch2(t, .Eq, .Cmp_Eq);
case '!': kind = switch2(t, .Not, .Not_Eq);
case '.':
kind = .Period;
switch t.ch {
case '0'..='9':
kind, lit = scan_number(t, true);
case '.':
advance_rune(t);
kind = .Ellipsis;
switch t.ch {
case '<':
advance_rune(t);
kind = .Range_Half;
case '=':
advance_rune(t);
kind = .Range_Full;
}
}
case '@': kind = .At;
case '$': kind = .Dollar;
case '?': kind = .Question;
case '^': kind = .Pointer;
case ';': kind = .Semicolon;
case ',': kind = .Comma;
case ':': kind = .Colon;
case '(': kind = .Open_Paren;
case ')': kind = .Close_Paren;
case '[': kind = .Open_Bracket;
case ']': kind = .Close_Bracket;
case '{': kind = .Open_Brace;
case '}': kind = .Close_Brace;
case '%':
kind = .Mod;
switch t.ch {
case '=':
advance_rune(t);
kind = .Mod_Eq;
case '%':
advance_rune(t);
kind = .Mod_Mod;
if t.ch == '=' {
advance_rune(t);
kind = .Mod_Mod_Eq;
}
}
case '*':
kind = .Mul;
if t.ch == '=' {
advance_rune(t);
kind = .Mul_Eq;
}
case '=':
kind = .Eq;
if t.ch == '=' {
advance_rune(t);
kind = .Cmp_Eq;
}
case '~':
kind = .Xor;
if t.ch == '=' {
advance_rune(t);
kind = .Xor_Eq;
}
case '!':
kind = .Not;
if t.ch == '=' {
advance_rune(t);
kind = .Not_Eq;
}
case '+':
kind = .Add;
switch t.ch {
case '=':
advance_rune(t);
kind = .Add_Eq;
case '+':
advance_rune(t);
kind = .Increment;
}
case '-':
kind = .Sub;
switch t.ch {
case '-':
advance_rune(t);
kind = .Decrement;
if t.ch == '-' {
advance_rune(t);
kind = .Undef;
}
case '>':
advance_rune(t);
kind = .Arrow_Right;
case '=':
advance_rune(t);
kind = .Sub_Eq;
}
case '#':
kind = .Hash;
if t.ch == '!' {
insert_semicolon = t.insert_semicolon;
kind = .Comment;
lit = scan_comment(t);
}
case '?':
insert_semicolon = true;
kind = .Question;
case '@': kind = .At;
case '$': kind = .Dollar;
case '^': kind = .Pointer;
case '+': kind = switch2(t, .Add, .Add_Eq);
case '-':
if t.ch == '>' {
advance_rune(t);
kind = .Arrow_Right;
} else if t.ch == '-' && peek_byte(t) == '-' {
advance_rune(t);
advance_rune(t);
kind = .Undef;
} else {
kind = switch2(t, .Sub, .Sub_Eq);
}
case '*': kind = switch2(t, .Mul, .Mul_Eq);
case '/':
if t.ch == '/' || t.ch == '*' {
insert_semicolon = t.insert_semicolon;
kind = .Quo;
switch t.ch {
case '/', '*':
kind = .Comment;
lit = scan_comment(t);
} else {
kind = switch2(t, .Quo, .Quo_Eq);
}
case '%': kind = switch4(t, .Mod, .Mod_Eq, '%', .Mod_Mod, .Mod_Mod_Eq);
case '&':
if t.ch == '~' {
case '=':
advance_rune(t);
kind = switch2(t, .And_Not, .And_Not_Eq);
} else {
kind = switch3(t, .And, .And_Eq, '&', .Cmp_And);
kind = .Quo_Eq;
}
case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or);
case '~': kind = switch2(t, .Xor, .Xor_Eq);
case '<': kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq);
case '.':
if '0' <= t.ch && t.ch <= '9' {
kind, lit = scan_number(t, true);
} else {
kind = .Period;
if t.ch == '.' {
case '<':
kind = .Lt;
switch t.ch {
case '=':
advance_rune(t);
kind = .Lt_Eq;
case '<':
advance_rune(t);
kind = .Shl;
if t.ch == '=' {
advance_rune(t);
kind = .Ellipsis;
if t.ch == '<' {
advance_rune(t);
kind = .Range_Half;
} else if t.ch == '=' {
advance_rune(t);
kind = .Range_Full;
}
kind = .Shl_Eq;
}
}
case '>':
kind = .Gt;
switch t.ch {
case '=':
advance_rune(t);
kind = .Gt_Eq;
case '<':
advance_rune(t);
kind = .Shr;
if t.ch == '=' {
advance_rune(t);
kind = .Shr_Eq;
}
}
case '&':
kind = .And;
switch t.ch {
case '~':
advance_rune(t);
kind = .And_Not;
if t.ch == '=' {
advance_rune(t);
kind = .And_Not_Eq;
}
case '=':
advance_rune(t);
kind = .And_Eq;
case '&':
advance_rune(t);
kind = .Cmp_And;
if t.ch == '=' {
advance_rune(t);
kind = .Cmp_And_Eq;
}
}
case '|':
kind = .Or;
switch t.ch {
case '=':
advance_rune(t);
kind = .Or_Eq;
case '|':
advance_rune(t);
kind = .Cmp_Or;
if t.ch == '=' {
advance_rune(t);
kind = .Cmp_Or_Eq;
}
}
case ':': kind = .Colon;
case ',': kind = .Comma;
case ';': kind = .Semicolon;
case '(': kind = .Open_Paren;
case ')':
insert_semicolon = true;
kind = .Close_Paren;
case '[': kind = .Open_Bracket;
case ']':
insert_semicolon = true;
kind = .Close_Bracket;
case '{': kind = .Open_Brace;
case '}':
insert_semicolon = true;
kind = .Close_Brace;
case '\\': kind = .Back_Slash;
case:
if ch != utf8.RUNE_BOM {
error(t, t.offset, "illegal character '%r': %d", ch, ch);
}
insert_semicolon = t.insert_semicolon; // preserve insert_semicolon info
kind = .Invalid;
}
}
if .Insert_Semicolon in t.flags {
t.insert_semicolon = insert_semicolon;
#partial switch kind {
case .Invalid, .Comment:
// Preserve insert_semicolon info
case .Ident, .Context, .Typeid, .Break, .Continue, .Fallthrough, .Return,
.Integer, .Float, .Imag, .Rune, .String, .Undef,
.Question, .Pointer, .Close_Paren, .Close_Bracket, .Close_Brace,
.Increment, .Decrement, .Or_Return:
/*fallthrough*/
t.insert_semicolon = true;
case:
t.insert_semicolon = false;
break;
}
}
if lit == "" {
+4 -8
View File
@@ -1,7 +1,6 @@
package os
import win32 "core:sys/windows"
import "core:mem"
// lookup_env gets the value of the environment variable named by the key
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
@@ -65,22 +64,19 @@ unset_env :: proc(key: string) -> Errno {
// environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
environ :: proc(allocator := context.allocator) -> []string {
envs := win32.GetEnvironmentStringsW();
envs := cast([^]win32.WCHAR)(win32.GetEnvironmentStringsW());
if envs == nil {
return nil;
}
defer win32.FreeEnvironmentStringsW(envs);
r := make([dynamic]string, 0, 50, allocator);
for from, i, p := 0, 0, envs; true; i += 1 {
c := (^u16)(uintptr(p) + uintptr(i*2))^;
if c == 0 {
for from, i := 0, 0; true; i += 1 {
if c := envs[i]; c == 0 {
if i <= from {
break;
}
w := mem.slice_ptr(mem.ptr_offset(p, from), i-from);
append(&r, win32.utf16_to_utf8(w, allocator));
append(&r, win32.utf16_to_utf8(envs[from:i], allocator));
from = i + 1;
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
package os
import win32 "core:sys/windows"
import "intrinsics"
import "core:intrinsics"
is_path_separator :: proc(c: byte) -> bool {
return c == '/' || c == '\\';
+2 -2
View File
@@ -188,7 +188,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
aligned_free(old_memory);
case .Free_All:
// NOTE(tetra): Do nothing.
return nil, .Mode_Not_Implemented;
case .Resize:
if old_memory == nil {
@@ -204,7 +204,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
return nil, nil;
case .Query_Info:
return nil, nil;
return nil, .Mode_Not_Implemented;
}
return nil, nil;
+1 -1
View File
@@ -57,7 +57,7 @@ link_error_delete :: proc(lerr: Maybe(Link_Error)) {
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
v := or_else(ferr.(Platform_Error), {});
v := ferr.(Platform_Error) or_else {};
return v.err, v.err != 0;
}
+1 -1
View File
@@ -13,7 +13,7 @@ error_to_io_error :: proc(ferr: Error) -> io.Error {
if ferr == nil {
return .None;
}
return or_else(ferr.(io.Error), .Unknown);
return ferr.(io.Error) or_else .Unknown;
}
+1 -1
View File
@@ -84,7 +84,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
aligned_free(old_memory);
case .Free_All:
// NOTE(tetra): Do nothing.
return nil, .Mode_Not_Implemented;
case .Resize:
if old_memory == nil {
+4 -5
View File
@@ -7,11 +7,11 @@ import "core:runtime"
import "core:strings"
import "core:c"
import "core:strconv"
import "core:intrinsics"
Handle :: distinct i32;
File_Time :: distinct u64;
Errno :: distinct i32;
Syscall :: distinct i32;
INVALID_HANDLE :: ~Handle(0);
@@ -265,11 +265,10 @@ X_OK :: 1; // Test for execute permission
W_OK :: 2; // Test for write permission
R_OK :: 4; // Test for read permission
SYS_GETTID: Syscall : 186;
SYS_GETTID :: 186;
foreign libc {
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> i32 ---;
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---;
@@ -569,7 +568,7 @@ get_current_directory :: proc() -> string {
page_size := get_page_size();
buf := make([dynamic]u8, page_size);
for {
cwd := _unix_getcwd(cstring(#no_bounds_check &buf[0]), c.size_t(len(buf)));
#no_bounds_check cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf)));
if cwd != nil {
return string(cwd);
}
@@ -595,7 +594,7 @@ exit :: proc "contextless" (code: int) -> ! {
}
current_thread_id :: proc "contextless" () -> int {
return cast(int)syscall(SYS_GETTID);
return cast(int)intrinsics.syscall(SYS_GETTID);
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
+18 -1
View File
@@ -160,6 +160,18 @@ _alloc_command_line_arguments :: proc() -> []string {
return arg_list;
}
/*
Windows 11 (preview) has the same major and minor version numbers
as Windows 10: 10 and 0 respectively.
To determine if you're on Windows 10 or 11, we need to look at
the build number. As far as we can tell right now, the cutoff is build 22_000.
TODO: Narrow down this range once Win 11 is published and the last Win 10 builds
become available.
*/
WINDOWS_11_BUILD_CUTOFF :: 22_000;
get_windows_version_w :: proc() -> win32.OSVERSIONINFOEXW {
osvi : win32.OSVERSIONINFOEXW;
osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW);
@@ -194,5 +206,10 @@ is_windows_8_1 :: proc() -> bool {
is_windows_10 :: proc() -> bool {
osvi := get_windows_version_w();
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0);
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber < WINDOWS_11_BUILD_CUTOFF);
}
is_windows_11 :: proc() -> bool {
osvi := get_windows_version_w();
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF);
}
+1 -4
View File
@@ -298,10 +298,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s
for fi in fis {
n := fi.name;
matched, err := match(pattern, n);
if err != nil {
return m, err;
}
matched := match(pattern, n) or_return;
if matched {
append(&m, join(dir, n));
}
+12 -1
View File
@@ -2,7 +2,7 @@ package reflect
import "core:runtime"
import "core:mem"
import "intrinsics"
import "core:intrinsics"
_ :: intrinsics;
Type_Info :: runtime.Type_Info;
@@ -18,6 +18,7 @@ Type_Info_Boolean :: runtime.Type_Info_Boolean;
Type_Info_Any :: runtime.Type_Info_Any;
Type_Info_Type_Id :: runtime.Type_Info_Type_Id;
Type_Info_Pointer :: runtime.Type_Info_Pointer;
Type_Info_Multi_Pointer :: runtime.Type_Info_Multi_Pointer;
Type_Info_Procedure :: runtime.Type_Info_Procedure;
Type_Info_Array :: runtime.Type_Info_Array;
Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array;
@@ -50,6 +51,7 @@ Type_Kind :: enum {
Any,
Type_Id,
Pointer,
Multi_Pointer,
Procedure,
Array,
Enumerated_Array,
@@ -82,6 +84,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
case Type_Info_Any: return .Any;
case Type_Info_Type_Id: return .Type_Id;
case Type_Info_Pointer: return .Pointer;
case Type_Info_Multi_Pointer: return .Multi_Pointer;
case Type_Info_Procedure: return .Procedure;
case Type_Info_Array: return .Array;
case Type_Info_Enumerated_Array: return .Enumerated_Array;
@@ -172,6 +175,7 @@ typeid_elem :: proc(id: typeid) -> typeid {
case 256: return f64;
}
case Type_Info_Pointer: return v.elem.id;
case Type_Info_Multi_Pointer: return v.elem.id;
case Type_Info_Array: return v.elem.id;
case Type_Info_Enumerated_Array: return v.elem.id;
case Type_Info_Slice: return v.elem.id;
@@ -298,6 +302,13 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
}
return index({ptr, a.elem.id}, i, loc);
case Type_Info_Multi_Pointer:
ptr := (^rawptr)(val.data)^;
if ptr == nil {
return nil;
}
return index({ptr, a.elem.id}, i, loc);
case Type_Info_Array:
runtime.bounds_check_error_loc(loc, i, a.count);
offset := uintptr(a.elem.size * i);
+28 -32
View File
@@ -21,13 +21,11 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
switch x in a.variant {
case Type_Info_Named:
y, ok := b.variant.(Type_Info_Named);
if !ok { return false; }
y := b.variant.(Type_Info_Named) or_return;
return x.base == y.base;
case Type_Info_Integer:
y, ok := b.variant.(Type_Info_Integer);
if !ok { return false; }
y := b.variant.(Type_Info_Integer) or_return;
return x.signed == y.signed && x.endianness == y.endianness;
case Type_Info_Rune:
@@ -63,13 +61,15 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
return ok;
case Type_Info_Pointer:
y, ok := b.variant.(Type_Info_Pointer);
if !ok { return false; }
y := b.variant.(Type_Info_Pointer) or_return;
return are_types_identical(x.elem, y.elem);
case Type_Info_Multi_Pointer:
y := b.variant.(Type_Info_Multi_Pointer) or_return;
return are_types_identical(x.elem, y.elem);
case Type_Info_Procedure:
y, ok := b.variant.(Type_Info_Procedure);
if !ok { return false; }
y := b.variant.(Type_Info_Procedure) or_return;
switch {
case x.variadic != y.variadic,
x.convention != y.convention:
@@ -79,31 +79,26 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
return are_types_identical(x.params, y.params) && are_types_identical(x.results, y.results);
case Type_Info_Array:
y, ok := b.variant.(Type_Info_Array);
if !ok { return false; }
y := b.variant.(Type_Info_Array) or_return;
if x.count != y.count { return false; }
return are_types_identical(x.elem, y.elem);
case Type_Info_Enumerated_Array:
y, ok := b.variant.(Type_Info_Enumerated_Array);
if !ok { return false; }
y := b.variant.(Type_Info_Enumerated_Array) or_return;
if x.count != y.count { return false; }
return are_types_identical(x.index, y.index) &&
are_types_identical(x.elem, y.elem);
case Type_Info_Dynamic_Array:
y, ok := b.variant.(Type_Info_Dynamic_Array);
if !ok { return false; }
y := b.variant.(Type_Info_Dynamic_Array) or_return;
return are_types_identical(x.elem, y.elem);
case Type_Info_Slice:
y, ok := b.variant.(Type_Info_Slice);
if !ok { return false; }
y := b.variant.(Type_Info_Slice) or_return;
return are_types_identical(x.elem, y.elem);
case Type_Info_Tuple:
y, ok := b.variant.(Type_Info_Tuple);
if !ok { return false; }
y := b.variant.(Type_Info_Tuple) or_return;
if len(x.types) != len(y.types) { return false; }
for _, i in x.types {
xt, yt := x.types[i], y.types[i];
@@ -114,8 +109,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
return true;
case Type_Info_Struct:
y, ok := b.variant.(Type_Info_Struct);
if !ok { return false; }
y := b.variant.(Type_Info_Struct) or_return;
switch {
case len(x.types) != len(y.types),
x.is_packed != y.is_packed,
@@ -138,8 +132,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
return true;
case Type_Info_Union:
y, ok := b.variant.(Type_Info_Union);
if !ok { return false; }
y := b.variant.(Type_Info_Union) or_return;
if len(x.variants) != len(y.variants) { return false; }
for _, i in x.variants {
@@ -153,28 +146,23 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
return false;
case Type_Info_Map:
y, ok := b.variant.(Type_Info_Map);
if !ok { return false; }
y := b.variant.(Type_Info_Map) or_return;
return are_types_identical(x.key, y.key) && are_types_identical(x.value, y.value);
case Type_Info_Bit_Set:
y, ok := b.variant.(Type_Info_Bit_Set);
if !ok { return false; }
y := b.variant.(Type_Info_Bit_Set) or_return;
return x.elem == y.elem && x.lower == y.lower && x.upper == y.upper;
case Type_Info_Simd_Vector:
y, ok := b.variant.(Type_Info_Simd_Vector);
if !ok { return false; }
y := b.variant.(Type_Info_Simd_Vector) or_return;
return x.count == y.count && x.elem == y.elem;
case Type_Info_Relative_Pointer:
y, ok := b.variant.(Type_Info_Relative_Pointer);
if !ok { return false; }
y := b.variant.(Type_Info_Relative_Pointer) or_return;
return x.base_integer == y.base_integer && x.pointer == y.pointer;
case Type_Info_Relative_Slice:
y, ok := b.variant.(Type_Info_Relative_Slice);
if !ok { return false; }
y := b.variant.(Type_Info_Relative_Slice) or_return;
return x.base_integer == y.base_integer && x.slice == y.slice;
}
@@ -257,6 +245,11 @@ is_pointer :: proc(info: ^Type_Info) -> bool {
_, ok := type_info_base(info).variant.(Type_Info_Pointer);
return ok;
}
is_multi_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer);
return ok;
}
is_procedure :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
_, ok := type_info_base(info).variant.(Type_Info_Procedure);
@@ -424,6 +417,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info) -> (n: int) {
write_string(w, "^");
write_type(w, info.elem);
}
case Type_Info_Multi_Pointer:
write_string(w, "[^]");
write_type(w, info.elem);
case Type_Info_Procedure:
n += write_string(w, "proc");
if info.params == nil {
+12 -6
View File
@@ -20,7 +20,7 @@
//
package runtime
import "intrinsics"
import "core:intrinsics"
// NOTE(bill): This must match the compiler's
Calling_Convention :: enum u8 {
@@ -74,6 +74,9 @@ Type_Info_Type_Id :: struct {};
Type_Info_Pointer :: struct {
elem: ^Type_Info, // nil -> rawptr
};
Type_Info_Multi_Pointer :: struct {
elem: ^Type_Info,
};
Type_Info_Procedure :: struct {
params: ^Type_Info, // Type_Info_Tuple
results: ^Type_Info, // Type_Info_Tuple
@@ -184,6 +187,7 @@ Type_Info :: struct {
Type_Info_Any,
Type_Info_Type_Id,
Type_Info_Pointer,
Type_Info_Multi_Pointer,
Type_Info_Procedure,
Type_Info_Array,
Type_Info_Enumerated_Array,
@@ -214,6 +218,7 @@ Typeid_Kind :: enum u8 {
Any,
Type_Id,
Pointer,
Multi_Pointer,
Procedure,
Array,
Enumerated_Array,
@@ -276,10 +281,11 @@ Allocator_Query_Info :: struct {
}
Allocator_Error :: enum byte {
None = 0,
Out_Of_Memory = 1,
Invalid_Pointer = 2,
Invalid_Argument = 3,
None = 0,
Out_Of_Memory = 1,
Invalid_Pointer = 2,
Invalid_Argument = 3,
Mode_Not_Implemented = 4,
}
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
@@ -339,7 +345,7 @@ Context :: struct {
Raw_String :: struct {
data: ^byte,
data: [^]byte,
len: int,
}
+52 -30
View File
@@ -1,6 +1,6 @@
package runtime
import "intrinsics"
import "core:intrinsics"
@builtin
Maybe :: union($T: typeid) #maybe {T};
@@ -170,23 +170,27 @@ delete :: proc{
// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
@builtin
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_second {
ptr, err := mem_alloc(size_of(T), align_of(T), allocator, loc);
return (^T)(ptr), err;
return new_aligned(T, align_of(T), allocator, loc);
}
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
data := mem_alloc_bytes(size_of(T), alignment, allocator, loc) or_return;
t = (^T)(raw_data(data));
return;
}
@builtin
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_second {
ptr, err := mem_alloc(size_of(T), align_of(T), allocator, loc);
res := (^T)(ptr);
if ptr != nil && err != .Out_Of_Memory {
res^ = data;
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_second {
t_data := mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return;
t = (^T)(raw_data(t_data));
if t != nil {
t^ = data;
}
return res, err;
return;
}
DEFAULT_RESERVE_CAPACITY :: 16;
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
make_slice_error_loc(loc, len);
data, err := mem_alloc_bytes(size_of(E)*len, alignment, allocator, loc);
if data == nil && size_of(E) != 0 {
@@ -196,34 +200,31 @@ make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, alloca
return transmute(T)s, err;
}
@builtin
make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
@(builtin)
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
return make_aligned(T, len, align_of(E), allocator, loc);
}
@builtin
@(builtin)
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc);
}
@builtin
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
@(builtin)
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
return make_dynamic_array_len_cap(T, len, len, allocator, loc);
}
@builtin
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_second {
@(builtin)
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_second {
make_dynamic_array_error_loc(loc, len, cap);
data, err := mem_alloc(size_of(E)*cap, align_of(E), allocator, loc);
s := Raw_Dynamic_Array{data, len, cap, allocator};
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return;
s := Raw_Dynamic_Array{raw_data(data), len, cap, allocator};
if data == nil && size_of(E) != 0 {
s.len, s.cap = 0, 0;
}
return transmute(T)s, err;
array = transmute(T)s;
return;
}
@builtin
make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
@(builtin)
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
make_map_expr_error_loc(loc, cap);
context.allocator = allocator;
@@ -231,6 +232,17 @@ make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = DEFAULT_RESERVE_CAPA
reserve_map(&m, cap);
return m;
}
@(builtin)
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) #optional_second {
make_slice_error_loc(loc, len);
data := mem_alloc_bytes(size_of(E)*len, align_of(E), allocator, loc) or_return;
if data == nil && size_of(E) != 0 {
return;
}
mp = cast(T)raw_data(data);
return;
}
// The make built-in procedure allocates and initializes a value of type slice, dynamic array, or map (only)
// Similar to new, the first argument is a type, not a value. Unlike new, make's return type is the same as the
@@ -243,6 +255,7 @@ make :: proc{
make_dynamic_array_len,
make_dynamic_array_len_cap,
make_map,
make_multi_pointer,
};
@@ -303,9 +316,9 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
if cap(array)-len(array) > 0 {
a := (^Raw_Dynamic_Array)(array);
when size_of(E) != 0 {
data := (^E)(a.data);
data := ([^]E)(a.data);
assert(condition=data != nil, loc=loc);
intrinsics.ptr_offset(data, a.len)^ = arg;
data[a.len] = arg;
}
a.len += 1;
}
@@ -331,9 +344,9 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
if arg_len > 0 {
a := (^Raw_Dynamic_Array)(array);
when size_of(E) != 0 {
data := (^E)(a.data);
data := ([^]E)(a.data);
assert(condition=data != nil, loc=loc);
intrinsics.mem_copy(intrinsics.ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len);
}
a.len += arg_len;
}
@@ -510,6 +523,15 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
return true;
}
@builtin
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
key, value := key, value;
h := __get_map_header(m);
hash := __get_map_hash(&key);
data := uintptr(__dynamic_map_set(h, hash, &value, loc));
return (^V)(data + h.value_offset);
}
@builtin
+1 -1
View File
@@ -1,6 +1,6 @@
package runtime
import "intrinsics"
import "core:intrinsics"
_ :: intrinsics;
/*
+16 -11
View File
@@ -1,6 +1,6 @@
package runtime
import "intrinsics"
import "core:intrinsics"
_ :: intrinsics;
INITIAL_MAP_CAP :: 16;
@@ -193,26 +193,28 @@ __dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller
}
__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check {
new_header: Map_Header = header;
nm := Raw_Map{};
nm.entries.allocator = m.entries.allocator;
new_header.m = &nm;
c := context;
if m.entries.allocator.procedure != nil {
c.allocator = m.entries.allocator;
}
context = c;
nm := Raw_Map{};
nm.entries.allocator = m.entries.allocator;
nm.hashes = m.hashes;
new_header: Map_Header = header;
new_header.m = &nm;
new_count := new_count;
new_count = max(new_count, 2*m.entries.len);
__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc);
__slice_resize(&nm.hashes, new_count, m.entries.allocator, loc);
for i in 0 ..< new_count {
nm.hashes[i] = -1;
}
__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc);
for i in 0 ..< m.entries.len {
if len(nm.hashes) == 0 {
__dynamic_map_grow(new_header, loc);
@@ -239,7 +241,6 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c
}
}
delete(m.hashes, m.entries.allocator, loc);
free(m.entries.data, m.entries.allocator, loc);
header.m^ = nm;
}
@@ -253,7 +254,7 @@ __dynamic_map_get :: proc(h: Map_Header, hash: Map_Hash) -> rawptr {
return nil;
}
__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) #no_bounds_check {
__dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #caller_location) -> ^Map_Entry_Header #no_bounds_check {
index: int;
assert(value != nil);
@@ -277,7 +278,7 @@ __dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #
e := __dynamic_map_get_entry(h, index);
e.hash = hash.hash;
key := rawptr(uintptr(e) + h.key_offset);
mem_copy(key, hash.key_ptr, h.key_size);
@@ -286,7 +287,11 @@ __dynamic_map_set :: proc(h: Map_Header, hash: Map_Hash, value: rawptr, loc := #
if __dynamic_map_full(h) {
__dynamic_map_grow(h, loc);
index = __dynamic_map_find(h, hash).entry_index;
assert(index >= 0);
}
return __dynamic_map_get_entry(h, index);
}
@@ -297,7 +302,7 @@ __dynamic_map_grow :: proc(using h: Map_Header, loc := #caller_location) {
}
__dynamic_map_full :: #force_inline proc "contextless" (using h: Map_Header) -> bool {
return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
return int(0.75 * f64(len(m.hashes))) <= m.entries.len;
}
+18
View File
@@ -45,6 +45,24 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h
bounds_trap();
}
multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! {
print_caller_location(Source_Code_Location{file, line, column, ""});
print_string(" Invalid slice indices: ");
print_i64(i64(lo));
print_string(":");
print_i64(i64(hi));
print_byte('\n');
bounds_trap();
}
multi_pointer_slice_expr_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) {
if lo <= hi {
return;
}
multi_pointer_slice_handle_error(file, line, column, lo, hi);
}
slice_expr_error_hi :: proc "contextless" (file: string, line, column: i32, hi: int, len: int) {
if 0 <= hi && hi <= len {
return;
+1 -1
View File
@@ -1,6 +1,6 @@
package runtime
import "intrinsics"
import "core:intrinsics"
bswap_16 :: proc "none" (x: u16) -> u16 {
return x>>8 | x<<8;
+1 -1
View File
@@ -1,6 +1,6 @@
package runtime
import "intrinsics"
import "core:intrinsics"
@(link_name="__umodti3")
umodti3 :: proc "c" (a, b: u128) -> u128 {
+1 -1
View File
@@ -1,6 +1,6 @@
package runtime
import "intrinsics"
import "core:intrinsics"
@(link_name="__umodti3")
umodti3 :: proc "c" (a, b: u128) -> u128 {
+1 -1
View File
@@ -2,7 +2,7 @@
//+build windows
package runtime
import "intrinsics"
import "core:intrinsics"
foreign import kernel32 "system:Kernel32.lib"
+3
View File
@@ -203,6 +203,9 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
print_string("^");
print_type(info.elem);
}
case Type_Info_Multi_Pointer:
print_string("[^]");
print_type(info.elem);
case Type_Info_Procedure:
print_string("proc");
if info.params == nil {

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