Compare commits

..

445 Commits

Author SHA1 Message Date
gingerBill 2d71ab6f29 Improve error message on undefined operators 2023-03-02 14:54:27 +00:00
gingerBill 99e2f0c91e Merge pull request #2357 from JeppeSS/sys-windows-additions
Added missing Windows functions for console manipulation
2023-03-02 13:21:38 +00:00
gingerBill 553f338f6f Merge pull request #2360 from Lperlind/documentation/dynlib
Document core:dynlib
2023-03-02 12:07:32 +00:00
Lucas Perlind bb72c804fb Document core:dynlib 2023-03-02 19:20:45 +11:00
Jeppe Skov ffc592c7cf Added missing Windows functions for console manipulation
This commit adds several missing types and functions to the Windows implementation to enable manipulation of console windows. The types added include 'SMALL_RECT', 'CONSOLE_SCREEN_BUFFER_INFO', and 'PCONSOLE_SCREEN_BUFFER_INFO'. The functions added include 'GetConsoleScreenBufferInfo', 'SetConsoleScreenBufferSize', and 'SetConsoleWindowInfo'. These functions were necessary to properly manage the console window.
2023-02-28 23:18:10 +01:00
Jeroen van Rijn 3567c006e6 Merge pull request #2356 from flysand7/master
Add option to link to glfw3 dynamically
2023-02-28 18:01:32 +01:00
bumbread b9db450a7d Fix missing underscore 2023-03-01 03:43:59 +11:00
bumbread 0d65c6dcf7 Add option to link to glfw3 dynamically 2023-03-01 03:05:04 +11:00
gingerBill dfee7c103e Document virtual.Arena 2023-02-28 13:07:52 +00:00
gingerBill 025fc2685d Add docs to core:path/filepath 2023-02-28 12:55:13 +00:00
gingerBill 5b5154eda0 Add temp allocator guard; clean up indentation 2023-02-28 12:38:36 +00:00
gingerBill ecf65303cd Make arena_free_all keep the first memory block for a .Growing arena 2023-02-28 12:37:05 +00:00
gingerBill 8ea6972496 Merge pull request #2327 from odin-lang/new-temp-allocator
New default `context.temp_allocator`
2023-02-28 12:22:45 +00:00
gingerBill 9afd9f9bea Merge branch 'master' into new-temp-allocator 2023-02-28 12:15:54 +00:00
gingerBill c8d3a9121b Merge pull request #2354 from elusivePorpoise/master
FindFirstChangeNotification series of calls
2023-02-28 10:49:42 +00:00
gingerBill f0950e2286 Merge pull request #2355 from colrdavidson/os_linux_cleanup
os/linux cleanup
2023-02-28 10:48:09 +00:00
Colin Davidson edd78ae129 cleanup of os/linux 2023-02-28 01:17:43 -08:00
Elusive Porpoise 8738695bd8 FindFirstChangeNotification series of calls 2023-02-27 18:43:43 -08:00
gingerBill 76cb3b7874 Add better fallback for ast_token 2023-02-27 16:15:19 +00:00
gingerBill 02a098eb48 Merge branch 'master' of https://github.com/odin-lang/Odin 2023-02-27 15:58:42 +00:00
gingerBill 1f17a391c6 Improve error line squiggle logic 2023-02-27 15:58:32 +00:00
Jeroen van Rijn a8fb346fd7 Merge pull request #2350 from Kelimion/d3d_license
Add license for dxcompiler libs.
2023-02-26 19:54:53 +01:00
Jeroen van Rijn 96fb5eb0ba Add license for dxcompiler libs. 2023-02-26 19:48:44 +01:00
gingerBill 9c7656d59a Add core:prof/spall 2023-02-26 14:00:39 +00:00
gingerBill a53bff5645 Fix typed #caller_location bug. 2023-02-26 13:52:02 +00:00
gingerBill a9182cfd8c Allow compound literals to access fields through using 2023-02-26 13:26:35 +00:00
gingerBill de6c0f682f Merge pull request #2343 from Hyp-X/pr-heapflags
Fix d3d12 HEAP_FLAG_ALLOW_ONLY_BUFFERS flags
2023-02-24 11:58:30 +00:00
gingerBill 9cfcb43725 Merge pull request #2345 from Hyp-X/pr-dxc
Add vendor:directx/dxc package
2023-02-24 11:57:59 +00:00
gingerBill d9b590c76e Merge pull request #2344 from Hyp-X/pr-more-shader-reflection
Add d3d12shader missing types and UUID's
2023-02-24 11:51:11 +00:00
Hyp-X 450c535e9a Add vendor:directx/dxc package:
DirectX Shader Compiler
2023-02-24 11:34:00 +01:00
Hyp-X 0dc166e594 Add d3d12shader missing types and UUID's 2023-02-24 10:02:12 +01:00
Hyp-X 8ba080a66d Fix d3d12 HEAP_FLAG_ALLOW_ONLY_BUFFERS flags 2023-02-23 17:15:13 +01:00
gingerBill 7801582819 Merge pull request #2341 from Hyp-X/pr-getresourceallocationinfo
Fix d3d12 GetResourceAllocationInfo signature
2023-02-23 15:48:02 +00:00
gingerBill bc882e678d Merge pull request #2340 from Hyp-X/pr-shader-reflection-fix
Fixed d3d12 shader reflection vtables
2023-02-23 15:40:15 +00:00
gingerBill 58e173f279 Merge pull request #2339 from Hyp-X/pr-dxgidebug
Add dxgidebug.h implementation to dxgi package
2023-02-23 15:40:02 +00:00
Hyp-X b7d7b9d6b3 Fix d3d12 GetResourceAllocationInfo signature 2023-02-23 16:30:28 +01:00
Hyp-X cf091a48b4 Fixed d3d12 shader reflection vtables 2023-02-23 14:48:58 +01:00
Hyp-X d9bfe9e5d4 Add dxgidebug.h implementation to dxgi package 2023-02-23 14:39:39 +01:00
gingerBill 245a6697ef Improve truncated verbose line error 2023-02-22 22:57:11 +00:00
gingerBill 6226c2978d Change padding of showing the error in line 2023-02-22 22:04:00 +00:00
gingerBill 3d325e52c6 Merge branch 'master' of https://github.com/odin-lang/Odin 2023-02-22 21:50:51 +00:00
gingerBill 6a6d7701f9 Improve error bounds for check_comparison 2023-02-22 21:50:49 +00:00
Jeroen van Rijn 50c688f0f7 Merge pull request #2338 from Tetralux/fix-here
Remove debug print
2023-02-22 22:49:52 +01:00
Tetralux ef99d03f21 Remove debug print 2023-02-22 21:43:42 +00:00
gingerBill af265250c2 Merge pull request #2336 from colrdavidson/tsc_freq
Add TSC frequency getter
2023-02-22 21:22:10 +00:00
Colin Davidson c6f463b8c9 shuffle tsc around a little 2023-02-22 12:28:24 -08:00
gingerBill b7d75e2f1d Override to have ansi colors if env has ODIN_TERMINAL=ansi 2023-02-22 12:41:53 +00:00
gingerBill 6aa54cbe9a Begin work on adding colours to error messages on Windows Terminals 2023-02-22 12:31:51 +00:00
gingerBill 090e30f07b Make -verbose-errors the default; -terse-errors to disable it 2023-02-22 11:48:10 +00:00
gingerBill f5d507a9b9 Improve errors about conversions of constant integers 2023-02-22 11:30:08 +00:00
Colin Davidson 8e5e43f335 add sleep-fallback and invariant check 2023-02-21 17:48:49 -08:00
gingerBill b9f7b2fdfa Improve error message for typed constants that cannot be represented by a type 2023-02-21 23:16:25 +00:00
gingerBill 59a601f2cf Improve error messages when trying to access a non-existent field on a type 2023-02-21 23:08:02 +00:00
gingerBill b6a5c5f5d2 Improve some error messages when casting a constant value which needs to be truncated/rounded 2023-02-21 17:24:22 +00:00
gingerBill a2f02b8b32 Fix bug with for in statements and pointer intervals 2023-02-21 16:31:22 +00:00
gingerBill ee4ed126e1 Improve error message for accidentally using a type as an expression statement 2023-02-21 16:25:28 +00:00
gingerBill c36dc91849 Minor changes in runtime 2023-02-21 16:24:28 +00:00
Colin Davidson 91dccf8d62 more function name changes 2023-02-21 06:46:36 -08:00
Colin Davidson 1fc3a25f47 block all x86 tsc functions in when block 2023-02-21 06:28:55 -08:00
Colin Davidson 7322b63991 adjust func names 2023-02-21 06:22:19 -08:00
Colin Davidson f860b09065 use the libc call on darwin so sysctlbyname works 2023-02-21 05:38:07 -08:00
Colin Davidson 45b742be23 sort out units to make things happier 2023-02-19 20:50:30 -08:00
Colin Davidson d325ee4b91 more typo. yay. 2023-02-19 20:45:56 -08:00
Colin Davidson 87d6910bb8 intrinsics typo 2023-02-19 20:44:49 -08:00
Colin Davidson 9c9300ed58 derp. raw-syscalls 2023-02-19 20:44:00 -08:00
Colin Davidson e559cf32fe oops, add intrinsics import 2023-02-19 20:39:36 -08:00
Colin Davidson f2202db517 make darwin syscalls contextless 2023-02-19 20:38:46 -08:00
Colin Davidson fb735883be add a tsc frequency get for windows 2023-02-19 20:33:48 -08:00
Colin Davidson 6a2ef1f4f3 add osx support 2023-02-19 20:23:35 -08:00
Colin Davidson 051c9cb564 begin adding tsc frequency getters 2023-02-19 20:08:11 -08:00
gingerBill eb60ec3899 Fix unreachable error 2023-02-19 12:53:22 +00:00
gingerBill 233f47cc99 Fix #2329 2023-02-19 12:47:14 +00:00
gingerBill c386c72d10 Check for procedure literals in $ parameters 2023-02-19 12:11:57 +00:00
gingerBill 20eacc4a84 Fix issue that conflicts with constant parapoly procedures and deferred_* procedures 2023-02-19 12:10:28 +00:00
Jeroen van Rijn a28699b42d Merge pull request #2335 from colrdavidson/add_panel
Add open file dialog panel to foundation
2023-02-19 08:32:01 +01:00
Colin Davidson 4d74d5bc99 Add user-defaults config to enable force-smooth-scrolling for SDL 2023-02-18 19:54:40 -08:00
Colin Davidson ed371f2b0d Add open file dialog panel to foundation 2023-02-18 14:56:51 -08:00
gingerBill 66f2881a78 Allow comparisons between empty struct{} and union{} 2023-02-17 17:02:37 +00:00
gingerBill 7d4e9497eb Reduce stack usage of some type switch cases 2023-02-17 16:51:57 +00:00
gingerBill c08809e29d Improve handling of passing constants to implicit immutable const ref parameters 2023-02-17 14:49:37 +00:00
gingerBill 99460c9e32 Minimize stack wastage with compound literals defining variables 2023-02-17 14:26:22 +00:00
gingerBill d86df8321c Fix #2330 2023-02-17 13:08:20 +00:00
gingerBill 806f56ca38 Remove debug string 2023-02-17 13:04:09 +00:00
gingerBill c40b6c7c2f Add constant data to the identifier directly 2023-02-17 13:02:41 +00:00
gingerBill 896b7145b3 Merge branch 'master' of https://github.com/odin-lang/Odin 2023-02-17 13:01:12 +00:00
gingerBill 8a2a70a3c2 Fix overriding procedure information for literals 2023-02-17 13:00:37 +00:00
gingerBill 97352538ad Merge pull request #2332 from thePHTest/master
Fix #by_ptr argument overrides for Linux
2023-02-16 10:22:25 +00:00
Phil Homan c6c4ad6188 fix #by_ptr argument overrides for Linux 2023-02-15 16:51:00 -08:00
gingerBill 210f47b8ab Merge branch 'master' of https://github.com/odin-lang/Odin 2023-02-15 11:32:02 +00:00
gingerBill 94c1331c07 Implement @(fini) (opposite of @(init)) 2023-02-15 11:31:51 +00:00
gingerBill d6407e9636 Merge pull request #2331 from colrdavidson/platform_file_cleanup
make file access a little more normal across platforms
2023-02-15 11:07:42 +00:00
Colin Davidson df58a00564 fix errno/signatures 2023-02-14 18:43:48 -08:00
Colin Davidson d546677ae7 fix typo 2023-02-14 18:39:09 -08:00
Colin Davidson 04b1023988 make file access a little more normal across platforms 2023-02-14 18:34:03 -08:00
gingerBill 9a81071687 Merge branch 'master' into new-temp-allocator 2023-02-14 23:59:49 +00:00
gingerBill 48685e8bf1 Remove set volatile for store 2023-02-14 23:52:36 +00:00
gingerBill 0f697a0f26 Move in_multi_assignment check tighter 2023-02-14 23:52:23 +00:00
gingerBill 8ddb493b96 Add #optional_allocator_error to make_map 2023-02-14 10:28:04 +00:00
gingerBill 039d9938b9 Fix return value 2023-02-10 17:20:14 +00:00
gingerBill f50ea649f6 Minor fix 2023-02-10 17:15:40 +00:00
gingerBill 6e647a88eb Keep -vet happy 2023-02-10 16:36:50 +00:00
gingerBill 986cba584e Add runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD where appropriate 2023-02-10 16:23:33 +00:00
gingerBill b427a4c8c9 Minor change to arena_temp_end 2023-02-10 14:44:46 +00:00
gingerBill 133ee70a5b Add default_temp_allocator_temp_begin and default_temp_allocator_temp_end 2023-02-10 14:36:57 +00:00
gingerBill 494612827a Add Arena_Temp 2023-02-10 14:30:41 +00:00
gingerBill 1113f23475 Remove unused variable 2023-02-10 14:10:06 +00:00
gingerBill 8626f58773 Replace current default context.temp_allocator to use a growing arena rather than a ring buffer 2023-02-10 13:18:33 +00:00
gingerBill 7032867421 Pass #caller_location down correctly 2023-02-10 13:18:03 +00:00
gingerBill e6239ca3c2 Warn on 'expand_to_tuple' has been replaced with 'expand_values' 2023-02-10 13:17:04 +00:00
gingerBill 162628000f Calculate the size needed before allocating 2023-02-10 11:55:08 +00:00
gingerBill 55b79c078c Remove := context.allocator usage in package os2 2023-02-10 11:46:29 +00:00
gingerBill 570b127869 Fix crash when a variable declaration must be an identifier 2023-02-08 11:46:33 +00:00
gingerBill 6179d4feb1 Rename to Type_Info_Parameters 2023-02-08 11:23:21 +00:00
gingerBill 2ff5d016d5 Merge pull request #2326 from ftphikari/master
Updated documentation to reflect changes from commit 8a16fd7
2023-02-08 11:09:23 +00:00
hikari 854a95327a Updated documentation to reflect changes from commit 8a16fd7 2023-02-08 12:24:10 +02:00
gingerBill 8a16fd7699 Rename built-in procedure to expand_values 2023-02-07 15:39:39 +00:00
gingerBill 7bbcf22deb Remove dead code (sort/map.odin) 2023-02-05 18:33:53 +00:00
gingerBill 0324281634 Enforce dynamic map calls for the time being 2023-02-03 15:17:30 +00:00
gingerBill de0a3e0ab9 Minor change to byval for readonly parameters 2023-02-03 15:07:44 +00:00
gingerBill d26110da7f Change attributes for the static map get 2023-02-03 14:25:30 +00:00
gingerBill 60e73d91f6 Remove internal readonly attribute 2023-02-03 13:42:23 +00:00
gingerBill 5eeb436626 Temporarily make all map get calls dynamic 2023-02-03 12:43:21 +00:00
gingerBill 802333e454 Fix arena.free_all 2023-02-03 12:40:52 +00:00
gingerBill eb457d688d Make static map calls the default; add -dynamic-map-calls 2023-02-03 12:16:58 +00:00
gingerBill fcc920ed39 Fix typo 2023-02-02 00:24:36 +00:00
gingerBill 4e70256109 Fix when within foreign block (again) 2023-02-02 00:22:54 +00:00
gingerBill 2e4d6d2577 Fix when within foreign blocks at the file scope 2023-02-01 23:41:13 +00:00
gingerBill 51ae21a029 Separate check_stmt code into separate procedures 2023-02-01 23:40:42 +00:00
gingerBill f59846377d Improve ternary logic for untyped nil stuff 2023-01-30 15:29:59 +00:00
gingerBill 8e8eb9e5cd Improve ternary if expression type inference rues
Allow for expression like this

    `x: union{f32} = f32(123) if cond else nil`
2023-01-30 12:54:11 +00:00
gingerBill 88b578ca11 Add for C++ for loop uses 2023-01-30 12:53:36 +00:00
gingerBill 4cb16db4e9 Remove @(require_results) from one procedure 2023-01-30 12:51:56 +00:00
gingerBill 338d483682 Merge branch 'master' of https://github.com/odin-lang/Odin 2023-01-30 11:58:39 +00:00
gingerBill 0ce59a9e2b Use C++ for rather than for_array macro 2023-01-29 11:28:36 +00:00
gingerBill 8d43cc840a Add @(require_results) to package reflect 2023-01-29 11:28:05 +00:00
gingerBill 9ae1bfb69d Merge pull request #2317 from ftphikari/master
vendor/d3d11: fixed function definition
2023-01-28 23:45:23 +00:00
hikari 6ec7284467 vendor/d3d11: fixed function definition 2023-01-29 01:38:28 +02:00
gingerBill 0ccc570ef2 Merge pull request #2316 from Hyp-X/master
Fix decimal_to_float_bits for floats >= 1
2023-01-28 23:31:57 +00:00
Hyp-X a3bb7d3028 Fix decimal_to_float_bits for floats >= 1 2023-01-28 14:40:44 +01:00
gingerBill c45ca1bfcc Correct arena_temp_end usage when no allocation ever happens for that arena 2023-01-28 12:09:24 +00:00
gingerBill 94edf89b20 Use a separate arena for heap_allocator 2023-01-28 11:53:05 +00:00
gingerBill 8d6ce0b693 Add mutex to virtual.Arena; add virtual.arena_temp_ignore 2023-01-28 11:51:58 +00:00
gingerBill ccf4b48865 Add extra checks for multiple assignments when emitting stores 2023-01-27 11:47:00 +00:00
gingerBill 96eae94103 Merge branch 'master' of https://github.com/odin-lang/Odin 2023-01-27 11:12:17 +00:00
gingerBill db8b2e69dd Fix slice.reverse 2023-01-27 11:12:10 +00:00
Jeroen van Rijn 82821580c7 Merge pull request #2315 from SentientCoffee/pr/os_read_file_location
Add caller location info to `os.read_entire_file()`
2023-01-27 12:00:56 +01:00
Daniel d23d7cf0f2 Add caller location info to os.read_entire_file()
This helps people using the mem.Tracking_Allocator to more easily
pinpoint where they leaked memory in their own code, rather than
having the leaks be reported as if they were in the core library
itself.
2023-01-27 02:42:16 -05:00
gingerBill 450a602230 Fix json.marshal for map[string]string 2023-01-24 12:05:43 +00:00
gingerBill 36764779cf Add extra add_type_info_type calls 2023-01-23 14:09:55 +00:00
gingerBill 97595c4b50 Use a LUT for shift_left 2023-01-23 14:00:02 +00:00
gingerBill ea9fe397e5 Fix typo in decimal_to_float_bits 2023-01-23 12:46:03 +00:00
gingerBill c482432966 Disable arena guards 2023-01-23 12:29:59 +00:00
gingerBill 55176e52fc Use heap_allocator() with -debug; Reinstate the arena guards 2023-01-23 11:38:18 +00:00
gingerBill 4eb08bb096 Change current requirements for valgrind targets 2023-01-23 10:20:04 +00:00
gingerBill 881ef69063 Use ~{} syntax 2023-01-23 10:14:21 +00:00
gingerBill 761a19689d Escape $ in asm 2023-01-23 10:09:22 +00:00
gingerBill f438153b81 Change to use ODIN_VALGRIND_SUPPORT 2023-01-23 09:29:39 +00:00
gingerBill 117c0cceb1 Add helgrind markers to package sync 2023-01-23 09:24:21 +00:00
gingerBill c949e404c3 Fix bug when . is passed to remove_extension_from_path 2023-01-20 13:01:00 +00:00
gingerBill 3d2a6c5895 Fix #2282 caused by a typo 2023-01-20 11:37:40 +00:00
gingerBill 8f4ffbe1da Fix #2299 by handling very large value cases correctly 2023-01-20 11:23:15 +00:00
gingerBill 8f3b6738ff Merge pull request #2273 from ap29600/core_slice_rotate_fix
Fix `core:slice.rotate_left`
2023-01-19 11:13:40 +00:00
gingerBill d50c6d72db Merge pull request #2310 from Lperlind/staging/ns-application-delegate-fix
Fix a few bindings in NSApplicationDelegate
2023-01-19 11:03:21 +00:00
gingerBill 15c5e92d63 Merge pull request #2309 from Lperlind/staging/ns-window-delegate
Fully implement NSWindowDelegate
2023-01-19 11:03:02 +00:00
gingerBill 041c7c8284 Remove tools directory 2023-01-19 10:33:46 +00:00
Lucas Perlind f040ef41cb Fix a few bindings in NSApplicationDelegate 2023-01-19 21:18:30 +11:00
Lucas Perlind 91ab184175 Fully implement NSWindowDelegate 2023-01-19 19:12:14 +11:00
gingerBill 48a64a2c88 Minor fix to string_compare 2023-01-18 16:48:11 +00:00
gingerBill 7f3795a231 Improve odin doc string printing (Fixes #2246) 2023-01-18 16:17:02 +00:00
gingerBill eb1d00ced6 Fix #2264 2023-01-18 16:05:30 +00:00
gingerBill f41c91d36b Fix #2274 2023-01-18 15:41:49 +00:00
gingerBill 6909e0d774 Minor changes to Semaphore to make it trivially copyable 2023-01-18 15:41:39 +00:00
gingerBill d52921edd4 Merge pull request #2308 from Lperlind/staging/ns-application-delegate
(almost) Fully implement NSApplicationDelegate
2023-01-18 12:21:39 +00:00
gingerBill dcca40033e Merge pull request #2307 from Sanian-Creations/update_vendor-sdl2
Removed multiple "SDL_" prefixes that were missed
2023-01-18 12:21:31 +00:00
Lucas Perlind fed65742df (almost) Fully implement NSApplicationDelegate 2023-01-17 18:53:17 +11:00
Sanian b918acd871 Removed some more SDL_'s in sdl_render.odin 2023-01-16 23:45:03 +01:00
Sanian a046c41c7c Removed another missed SDL_ in sdl_mouse.odin
`sdl2.SDL_MouseWheelDirection` is now `sdl2.MouseWheelDirection`
2023-01-16 23:41:11 +01:00
Sanian 2513403014 Removed an SDL_ prefix that was missed 2023-01-16 22:46:46 +01:00
gingerBill 4a8564aff7 Update threading.cpp to have helgrind annotations 2023-01-16 19:23:13 +00:00
gingerBill edb23db2ae Fix potential race condition when determining the package name 2023-01-16 18:31:40 +00:00
gingerBill 0b01cfd853 Fix minor possible race condition 2023-01-16 18:18:08 +00:00
gingerBill 0d059aa797 Replace BlockingMutex with RwMutex 2023-01-16 18:08:28 +00:00
gingerBill 65c0255e7e Replace RecursiveMutex with a BlockingMutex 2023-01-16 18:05:58 +00:00
gingerBill b289a27c4e Move the mutex up a little 2023-01-16 17:04:37 +00:00
gingerBill d085283f20 Fix cnd_timedwait 2023-01-16 15:32:23 +00:00
gingerBill b6ca10cd5e Fix memory leak in os.get_current_directory on failure on *nix systems 2023-01-16 15:29:45 +00:00
gingerBill 7416f72565 Make static value atomic too 2023-01-16 15:12:34 +00:00
gingerBill b51be71a6f Remove initialization on static atomic 2023-01-16 15:11:24 +00:00
gingerBill e488cf4601 Enforce atomic on hasher id 2023-01-16 15:09:29 +00:00
gingerBill 5d397804f7 Fix #2286 by printing an error correctly 2023-01-16 13:22:37 +00:00
gingerBill a5a7226885 Remove auto_cast 2023-01-16 12:09:09 +00:00
gingerBill 2dca39b557 Remove auto_cast procedure field flag
Fixes #2285
2023-01-16 12:06:03 +00:00
gingerBill b55fa268bf Fix #2267 by making it an error 2023-01-16 12:03:46 +00:00
gingerBill c819c350d6 Add error message for atomic intrinsics to prevent arbitrary types 2023-01-16 11:58:14 +00:00
gingerBill d55248ab0f Fix #2301 2023-01-16 11:45:15 +00:00
gingerBill 68b2d4b9e2 Fix #2305 2023-01-16 11:41:58 +00:00
gingerBill 54f02f59db Fix compiler error on clang 2023-01-16 11:03:24 +00:00
gingerBill 64047cbf05 Fix #2304 2023-01-16 11:02:42 +00:00
gingerBill b0619980b2 Add /NOIMPLIB /NOEXP on MSVC linker by default when building an executable 2023-01-14 13:42:29 +00:00
gingerBill 9aa9429135 Update debugf usage 2023-01-14 13:42:04 +00:00
gingerBill 518f30e523 Bring PtrMap inline with StringMap 2023-01-14 13:23:17 +00:00
gingerBill 868aa4c14a Minor changes to StringMap allocation 2023-01-14 12:58:45 +00:00
gingerBill 1ab90de493 Minimize StringMap structure usage 2023-01-14 12:33:42 +00:00
gingerBill 1064bcd060 Clean up use of StringMap 2023-01-13 12:13:26 +00:00
gingerBill 1e21125527 Split out object generation more 2023-01-12 21:49:53 +00:00
gingerBill 4a8c37dd52 Prepare for arbitrary separate modules 2023-01-12 21:45:02 +00:00
gingerBill 3b22c6620c Begin to generalize modules away from AstPackage * in -use-separate-modules 2023-01-12 17:13:25 +00:00
gingerBill 402a165b60 Correct missing procedures in other build modules which cause a linkage problem 2023-01-12 16:59:16 +00:00
gingerBill 34f9170189 Fix race condition with polymorphic record generation 2023-01-12 16:06:09 +00:00
gingerBill 38136e15fc add_deps_from_child_to_parent always 2023-01-12 15:44:55 +00:00
gingerBill e97bf2ef35 Minimize contention on the deps for decls 2023-01-12 15:38:23 +00:00
gingerBill d6c54148d9 Minor clean up 2023-01-12 15:23:59 +00:00
gingerBill cbe3791b42 Replace all queues with MPSCQueue where possible 2023-01-12 13:11:17 +00:00
gingerBill b470ceb470 Correct mpsc_dequeue 2023-01-12 12:59:09 +00:00
gingerBill c15db05199 Implement MPSCQueue 2023-01-12 12:41:53 +00:00
gingerBill 9428f792ed Comment out allocator guards for the time being 2023-01-12 01:09:36 +00:00
gingerBill 520ff731de Add ArenaTemp to the compiler 2023-01-12 00:47:20 +00:00
gingerBill e9cfe698ba Make the heap_allocator just be the permanent_allocator
This improves the speed of the compiler with very little memory increase (which surprised me, Ginger Bill)
2023-01-12 00:20:25 +00:00
gingerBill 5fa66ac6a8 Fix random race condition for poly records 2023-01-12 00:18:58 +00:00
gingerBill 320062157f Merge pull request #2288 from odin-lang/compiler-improvements-2023-01
Multithreading Compiler Improvements 2023-01
2023-01-11 22:14:53 +00:00
gingerBill d7d6608142 Remove unneeded CI stage 2023-01-11 22:08:25 +00:00
gingerBill 7f2ef2ac67 Move check for type info above 2023-01-11 21:52:04 +00:00
gingerBill 7124d541a1 General optimizations 2023-01-11 18:10:27 +00:00
gingerBill 3c7e45a46f Remove possible race condition in type_size_of/type_align_of 2023-01-11 17:45:18 +00:00
gingerBill 6ec014e980 Make -threaded-checker the default not (opt out with -no-threaded-checker) 2023-01-11 17:27:06 +00:00
gingerBill 9b47a5eddb Fix macro issue 2023-01-11 00:49:04 +00:00
gingerBill 3e8c63ad31 Add Odin check -threaded-checker test for windows 2023-01-10 20:46:00 +00:00
gingerBill 15469758de Merge branch 'master' into compiler-improvements-2023-01 2023-01-10 16:25:38 +00:00
gingerBill 86511d44e4 Merge pull request #2300 from MarenFayre/float-format
Clean up float_fmt logic
2023-01-10 15:46:54 +00:00
MarenFayre fd4633eb25 Clean up float_fmt logic 2023-01-10 15:03:53 +01:00
gingerBill b0756f9e29 Merge pull request #2297 from MarenFayre/d-parsing
Fix off by one error in %d parsing
2023-01-10 12:24:13 +00:00
gingerBill c3ff1e9591 Merge pull request #2298 from MarenFayre/left-pad
Fix left padding format specifier and float formatting
2023-01-10 12:24:07 +00:00
gingerBill dd3fac7523 Merge pull request #2292 from colrdavidson/get_core_count
add get core count
2023-01-10 11:42:02 +00:00
MarenFayre 13029d06b2 Removed unneeded semicolon. 2023-01-09 10:39:46 +01:00
MarenFayre 68173f4bc7 Remove unused formatting flag 2023-01-08 20:24:08 +01:00
MarenFayre c979c2fafa Fix left padding format specifier and float formatting 2023-01-08 20:00:42 +01:00
MarenFayre 658435f1b9 Fix off by one error in %d parsing 2023-01-08 19:59:48 +01:00
Colin Davidson 3935957979 remove unused c import 2023-01-06 13:53:32 -08:00
Colin Davidson a36640bcfc more windows fixes 2023-01-06 13:51:25 -08:00
Colin Davidson 171d5b4012 more windows kerfuffle 2023-01-06 13:45:21 -08:00
Colin Davidson 1cc893f21c Merge branch 'master' into get_core_count 2023-01-06 13:34:16 -08:00
Colin Davidson 6ff2db47b4 shuffle to private/public wrapper 2023-01-06 13:33:47 -08:00
gingerBill a11b6a9e5f Merge pull request #2265 from JooperGH/more_dwmapi_bindings
More dwmapi bindings
2023-01-06 12:18:10 +00:00
gingerBill 978568684c Merge pull request #2295 from matias-eduardo/patch-1
Add GetKeyboardState to user32
2023-01-06 12:16:51 +00:00
gingerBill e8e7d3ea31 Merge pull request #2293 from colrdavidson/fix_futexes
fix futex error handling
2023-01-06 12:09:44 +00:00
matias c03cc21908 Add GetKeyboardState to user32 2023-01-06 07:04:38 -04:00
gingerBill 8ef406324b Multi thread more of the backend where possible 2023-01-05 17:26:51 +00:00
gingerBill 23d0c52bf4 Refactor llvm backend code into separate procedures to make it simpler to profile 2023-01-05 16:42:02 +00:00
gingerBill 5eee8077dd enum-ifiy function pass managers for lbModule 2023-01-05 15:56:45 +00:00
gingerBill 029cb6581b Unify function pass managers for auxiliary procedures (e.g. startup type info, runtime, objc names) 2023-01-05 12:54:53 +00:00
gingerBill 025e87d974 Multithread LLVM procedure generation 2023-01-05 12:39:57 +00:00
gingerBill 213a0499a1 Begin multithreading the llvm backend when -use-separate-modules is enabled 2023-01-05 12:29:16 +00:00
gingerBill 1517f1d779 Add uncomment add_type_info_type calls for type assertions 2023-01-05 11:54:21 +00:00
Colin Davidson 50a2493fd3 add get thread count to openbsd 2023-01-05 01:48:00 -08:00
Colin Davidson b455ccd261 fix more things? 2023-01-05 01:37:50 -08:00
Colin Davidson a58650728e fix futex error handling 2023-01-05 01:27:37 -08:00
Colin Davidson b22ddb1453 fix windows structs 2023-01-05 01:25:18 -08:00
Colin Davidson cb7dd12222 name raw union 2023-01-05 01:18:44 -08:00
Colin Davidson 0484bdbb7e fix darwin/freebsd 2023-01-05 01:14:51 -08:00
Colin Davidson 8f39c45e9b use raw_union? 2023-01-05 01:11:46 -08:00
Colin Davidson 944396128b add get core count 2023-01-05 01:06:55 -08:00
gingerBill bbb2164e38 Inline map gets; cast explicitly on TOMBSTONE checking 2023-01-05 01:25:37 +00:00
gingerBill be23d83fc8 Remove unnecessary check is align_formula* et al 2023-01-05 00:47:09 +00:00
gingerBill 291ea33939 Initialize TypePath constructor like to keep the Futex constructor happy 2023-01-04 22:34:59 +00:00
gingerBill 9455918eec Fix min dep type info problem caused by const ref of map_set 2023-01-04 22:20:18 +00:00
gingerBill 8a99b8af3e Narrow mutex usage 2023-01-04 15:55:10 +00:00
gingerBill 12e42d92d3 Localize GenProcsData to the entity itself 2023-01-04 15:35:24 +00:00
gingerBill faa735d0c7 Localize gen_types mutexes 2023-01-04 15:15:12 +00:00
gingerBill d4e18109da Move walking of dependencies for procedures to just before calculating the min dep set 2023-01-04 13:52:38 +00:00
gingerBill d06a0e7093 Improve the PtrSet to be as simple and small as possible 2023-01-04 13:30:27 +00:00
gingerBill b3a55b8b6f Remove unused procedures 2023-01-03 18:42:13 +00:00
gingerBill ec69101101 Convert minimum_dependency_type_info_set to use a PtrMap 2023-01-03 18:39:37 +00:00
gingerBill 17fa8cb6ef Add extra mutex to TypePth just in case 2023-01-03 18:21:42 +00:00
gingerBill 855ebceadc Minimize add_type_info_type usage 2023-01-03 17:26:05 +00:00
gingerBill 2720e98127 Add +ignore along with +build ignore 2023-01-03 17:25:51 +00:00
gingerBill bb80c1b059 Add type_and_value_mutex to DeclInfo 2023-01-03 17:07:53 +00:00
gingerBill 85e390deba Minimize calling of Ast::thread_safe_file() when cloning 2023-01-03 15:57:09 +00:00
gingerBill dc317c8cd8 Make BlockingMutex 2023-01-03 15:50:31 +00:00
gingerBill 774fea1e63 Use RwMutex for gen_procs 2023-01-03 15:47:25 +00:00
gingerBill 485c606672 Clarify RwLocks for add_dependenies_from_unpacking 2023-01-03 15:37:35 +00:00
gingerBill 3dee3205b2 Use RwMutex for DeclInfo `deps 2023-01-03 15:34:52 +00:00
gingerBill c7a704d345 Use RwMutex for the Scope 2023-01-03 15:26:47 +00:00
gingerBill 0fb3032b73 General improves to alloc_ast_node and other unnecessary checks 2023-01-03 14:45:09 +00:00
gingerBill 69934c3b0b More for_array(i, y) to for (x : y) translations 2023-01-03 13:04:09 +00:00
gingerBill 7380b7e61b Add more uses of C++ style for loops over for_array macro 2023-01-03 12:37:41 +00:00
gingerBill 747a11a954 Allow all set entry types to be implicitly cast to their key/value type to allow for easier iteration 2023-01-03 12:18:35 +00:00
gingerBill 252be0fb41 Make all maps use heap allocator implicitly 2023-01-03 11:59:52 +00:00
gingerBill 600f2b7284 Use heap_allocator for all hash set types 2023-01-03 11:53:59 +00:00
gingerBill 670274ad8f More explicit uses of mutexes 2023-01-02 23:56:37 +00:00
gingerBill e10fe91eba Narrow global gen_procs_mutex further 2023-01-02 23:50:48 +00:00
gingerBill fd62ee14cd Code moving around 2023-01-02 23:31:38 +00:00
gingerBill 8ece92f1f6 Minimize the parapoly mutex usage a bit 2023-01-02 23:21:16 +00:00
gingerBill 69b075782b Use a package local mutex for add_type_and_value 2023-01-02 22:40:28 +00:00
gingerBill 6bd3a9d422 Be very explicit where the gen_procs_mutex can be unlock 2023-01-02 22:23:49 +00:00
gingerBill bc9ee8e1a4 Remove loops within futex signals on Linux 2023-01-02 22:13:49 +00:00
gingerBill d36c3c2590 Re enable type_and_value_mutex 2023-01-02 22:06:05 +00:00
gingerBill 52b319dbfd Fix darwin's futex implementation in the compiler 2023-01-02 21:53:41 +00:00
gingerBill 318d92f9a8 Comment out type_and_value_mutex usage 2023-01-02 21:37:21 +00:00
gingerBill 7ffffeeccc Comment out many mutex guards in type_(size|align)_of_internal 2023-01-02 21:35:40 +00:00
gingerBill f16d8e77b3 Narrow fullpath_mutex usage 2023-01-02 20:55:49 +00:00
gingerBill 5b335bb88c Narrow g_type_mutex usage 2023-01-02 20:48:24 +00:00
gingerBill df2767311f Use mutex_try_lock in check_proc_info 2023-01-02 20:42:22 +00:00
gingerBill 09c26e6be0 Narrow type info mutex usage 2023-01-02 20:38:37 +00:00
gingerBill d2ec2d1606 Remove another use of a global mutex 2023-01-02 19:46:55 +00:00
gingerBill 0d87b2e8db Use local mutexes rather than a global one for the dependency insertion 2023-01-02 19:39:35 +00:00
gingerBill 1568971732 Fix pool running 2023-01-02 18:04:16 +00:00
gingerBill 0e040be941 Add define for darwin 2023-01-02 17:49:16 +00:00
gingerBill 9737b65d9c Explicitly call store for futex 2023-01-02 17:18:59 +00:00
gingerBill ad52003077 Remove some unneeded checks 2023-01-02 17:15:29 +00:00
gingerBill c386509112 Minor clean up of thread pool code 2023-01-02 17:06:29 +00:00
gingerBill c293f5b7eb Remove unneeded mutex 2023-01-02 16:56:05 +00:00
gingerBill fa562ec5d6 Remove unneeded local_entity_map 2023-01-02 15:40:25 +00:00
gingerBill 529383f5b1 Correct a race condition when checking the procedure body 2023-01-02 15:30:04 +00:00
gingerBill f01cff7ff0 Multithread checker 2023-01-02 12:31:00 +00:00
gingerBill 015fe924b8 Remove use of queues for procedure checking. 2023-01-02 12:28:38 +00:00
gingerBill a5ce8a8c0b Multi thread check_export_entities 2023-01-02 01:31:14 +00:00
gingerBill bfdcf900ef Remove global_ prefix from global_thread_pool_* procedures 2023-01-02 00:56:06 +00:00
gingerBill 54f89dd84b Multithread check_collect_entities_all using new thread pool 2023-01-02 00:53:11 +00:00
gingerBill da479c7628 Minor style change 2023-01-02 00:35:12 +00:00
gingerBill 3c90a05957 Replace condition+mutex with futex 2023-01-02 00:26:17 +00:00
gingerBill d16ddf7926 Use C++ style for loop over for_array macro in parser.cpp where posible 2023-01-01 16:32:51 +00:00
gingerBill 5c519f0e8d Remove the synchronization primitive init/destroy calls 2023-01-01 16:19:21 +00:00
gingerBill 74e6d9144e Get around the std::atomic issue 2023-01-01 16:15:35 +00:00
gingerBill 20d451396d Begin work on futex-ifying the threading primitives 2023-01-01 15:06:57 +00:00
gingerBill 60d0390ef8 Unify compiler Futex interface 2023-01-01 14:48:31 +00:00
gingerBill 782f1b4718 Merge pull request #2278 from wjlroe/stb-darwin-universal-libraries
Universal stb libraries for macOS (Intel & Apple Silicon)
2023-01-01 14:10:35 +00:00
gingerBill 85f0a1067c Merge pull request #2280 from DragosPopse/master
Fixed empty output_path.name when building a folder with no subfolders
2023-01-01 14:09:51 +00:00
gingerBill c08ff891ad Merge pull request #2287 from odin-lang/compiler-improvements-2022-12
Compiler improvements 2022 12
2023-01-01 13:29:20 +00:00
gingerBill 168cec1e9d Merge pull request #2283 from colrdavidson/threadpool-swap
move to work-stealing threadpool
2023-01-01 13:28:36 +00:00
gingerBill 28fb35f2f7 Merge pull request #2263 from odin-lang/compiler-improvements-2022-12
Compiler Improvements for 2022-12
2023-01-01 13:26:43 +00:00
gingerBill c1384afe2f Merge branch 'master' into compiler-improvements-2022-12 2023-01-01 13:10:49 +00:00
gingerBill 547c7bce1b Merge pull request #2284 from thePHTest/master
fixup are_types_identical for comparing procs and checking if parameter names differ
2022-12-30 11:19:31 +00:00
Phil 0bb93d40d3 fixup are_types_identical for comparing procs and checking if parameter names differ 2022-12-29 16:10:13 -08:00
Colin Davidson 27ba1d596c rework openbsd futexes a little 2022-12-29 12:00:16 -08:00
Colin Davidson 98e5523f2f cover openbsd too 2022-12-29 11:46:43 -08:00
Colin Davidson 223b66f422 oops if->elif 2022-12-29 11:06:35 -08:00
Colin Davidson 04a4dbcdaf add freebsd support 2022-12-29 11:05:31 -08:00
Colin Davidson ef9e31cb31 fix ulock/uwait imports 2022-12-28 22:08:39 -08:00
Colin Davidson e019673a18 fix build 2022-12-28 21:52:41 -08:00
Colin Davidson 5f27f2dd7f move to work-stealing threadpool 2022-12-28 21:44:17 -08:00
Dragos Popescu cfccf73cdd Merge branch 'odin-lang:master' into master 2022-12-26 19:22:47 +02:00
Dragos 465d003b1e Patched empty output_path.name when building a folder 2022-12-26 19:21:24 +02:00
Mikkel Hjortshøj 1d6f7680a1 Update stale.yml
Update stale action to *not* delete issues/PRs anymore and only mark them as stale, also update the version
2022-12-24 15:44:32 +01:00
Jeroen van Rijn 5d0f9c428a Merge pull request #2279 from ftphikari/master
Replaced opaque bit-shifts with readable constants for memory units
2022-12-24 07:32:29 +01:00
hikari d904ae5191 Replaced opaque bit-shifts with readable constants for memory units 2022-12-24 08:27:15 +02:00
William Roe 8a822bdd9a Update stb macOS libraries to be universal
This updates all the darwin stb libraries to be built as universal
libraries - meaning they contain both Intel and Apple Silicon versions.
This should make these more generally compatible.

Also, add stb_vorbis.a in the same way. Not sure why it was missing
before.
2022-12-23 23:15:14 +00:00
William Roe d1a3842e39 Add Darwin-target for building vendor/stb macos universal libs
This Darwin-specific target builds each stb library with macOS-specific
options so that the results are universal static libraries that should
work on Intel (x86_64) and Apple Silicon (arm64) machines. They also
should work on macOS 10.12 and above (which should match what Odin
compiles for).

The default Makefile target will build the darwin rule if its invoked on
a macOS machine, otherwise it'll invoke the more general unix target.
2022-12-23 23:15:14 +00:00
gingerBill 00823ca88c Remove a few TODOs 2022-12-22 13:03:34 +00:00
gingerBill ffa14c3aad Remove need the MPMC in single threaded case 2022-12-22 12:58:23 +00:00
gingerBill 41b32f0da4 Clean up mutex usage in the parser 2022-12-22 12:45:23 +00:00
gingerBill c53b2198a8 Add minor comment 2022-12-22 12:02:14 +00:00
gingerBill 9b278db993 Revert "Change tav to be a pointer internally"
This reverts commit e98f1a28e6.
2022-12-22 12:01:41 +00:00
gingerBill e98f1a28e6 Change tav to be a pointer internally 2022-12-22 11:53:13 +00:00
gingerBill c8f05b7c0c Merge pull request #2269 from Skytrias/luapattern
Add lua pattern matching to core:text with tests
2022-12-22 11:08:10 +00:00
gingerBill b00c4a6a8f Merge pull request #2272 from sir-w7/fix/darwin_mem_leak
Fixed memory leak in dir_darwin.odin.
2022-12-22 10:58:59 +00:00
gingerBill 84e1fb2cee Merge pull request #2275 from Platin21/fix/dir-opening-macOS
Fix/dir opening mac os
2022-12-22 10:49:41 +00:00
Platin21 b983ac548c Moves check up and sets flag to rdonly if dir is opened.. 2022-12-22 01:36:04 +01:00
Platin21 fb562ea708 Adds error casting from last error if open fails 2022-12-22 01:26:06 +01:00
Platin21 cdeeeafc3f Fixed issues with dir opening on macOS 2022-12-22 01:22:31 +01:00
gingerBill b9a2426e57 Merge branch 'master' into compiler-improvements-2022-12 2022-12-21 23:59:31 +00:00
gingerBill 81037b3091 Change the order of the args and ret for Arm64 ABI 2022-12-21 23:56:34 +00:00
Jooper fc3c76f946 Fixed CI error 2022-12-21 22:18:13 +00:00
Andrea Piseri 3fa971a510 Add the inner for loop back in the logic
This could be easier to predict in cases where one of `left` and `right`
is significantly greater than the other, and as such the same branch is
taken multiple times in a row
2022-12-21 22:10:02 +01:00
skytrias 63a0395a79 refactor SPECIALS_TABLE 2022-12-21 22:08:03 +01:00
Andrea Piseri 191223bb3c Fix non-generic cast in core:slice.rotate_left 2022-12-21 21:58:01 +01:00
gingerBill 6f0bad816e Merge pull request #2248 from tstibor/fix_test_out
Enable -out:<filepath> for build and runs with the attribute @(test)
2022-12-21 20:38:54 +00:00
skytrias 94af3c2887 package name changed 2022-12-21 21:38:21 +01:00
skytrias e5d0417a6c folder name changed 2022-12-21 21:36:50 +01:00
gingerBill c02bda2427 Merge pull request #2256 from Lperlind/staging/small_array_utilities
Add more utility procedures to small array
2022-12-21 20:28:40 +00:00
Andrea Piseri 385d2a143c Fix core:slice.rotate_left
This commit includes two fixes:
- a temporary cast to make the function compile
- a fix to a logic error that caused the function to hang or return
  incorrect results
2022-12-21 21:09:22 +01:00
sir-w7 67c1b364c4 Fixed memory leak in dir_darwin.odin. 2022-12-21 07:25:13 -08:00
Lucas Perlind f029b4beb1 Add more utility procedures to small array 2022-12-21 13:00:33 +11:00
gingerBill 3040361fac Correct type_ptr_set_update and type_ptr_set_exists 2022-12-20 14:59:00 +00:00
gingerBill 44caa96d50 Set the file's filename and directory in init_ast_file 2022-12-20 14:56:44 +00:00
skytrias 1bea0f3772 fix styling issues and use switches in cases its necessary, add comments to helpers 2022-12-20 15:48:10 +01:00
gingerBill eb0775ad53 Move mutex use around in thread pool 2022-12-20 14:45:01 +00:00
gingerBill 8fc9566a83 Use *_set_update where possible 2022-12-20 14:19:55 +00:00
gingerBill 134c7db4d2 Combine join and destroy for threads 2022-12-20 14:08:24 +00:00
gingerBill a0e3a99dd1 Remove need for semaphore in Thread 2022-12-20 14:07:14 +00:00
gingerBill 0edda2bea7 Clarify ThreadPool interface; Move import_mutex guarding to just the string set 2022-12-20 12:46:33 +00:00
skytrias ff7f139fd7 add iter_index and update tests to use easier matcher setup 2022-12-20 12:59:32 +01:00
JooperGH 86a606e716 App bar bindings 2022-12-19 16:31:32 +00:00
JooperGH 1e97588e7b One last binding 2022-12-19 15:29:07 +00:00
JooperGH 3ccc0b5aa6 HRGB and Rect functions 2022-12-19 15:22:05 +00:00
JooperGH 5464a605b1 CreateSolidBrush and FillRect 2022-12-19 13:21:16 +00:00
JooperGH 5519749aa4 Added uxtheme bindings 2022-12-19 11:54:15 +00:00
JooperGH 4a70265bfb Merge branch 'master' of https://github.com/odin-lang/Odin into more_dwmapi_bindings 2022-12-19 11:46:35 +00:00
JooperGH de0d860880 Added more DWMAPI bindings 2022-12-19 11:43:16 +00:00
gingerBill a13e2f4578 Fix minor race condition 2022-12-19 00:29:40 +00:00
gingerBill 01b508f182 Use usize for bounds checking in Array and Slice (compiler) 2022-12-18 23:26:44 +00:00
gingerBill 2a8fa8612d Use fetch_add rather than += 2022-12-18 23:24:34 +00:00
gingerBill e27046098b Add missing gb_internal 2022-12-18 22:58:34 +00:00
gingerBill ca8b148fdc Add gb_internal to path procedures 2022-12-18 22:52:18 +00:00
gingerBill c1f5be24e2 Remove dead code in the compiler 2022-12-18 22:49:10 +00:00
gingerBill 6cdec65ca1 gb_internal LLVM backend 2022-12-18 22:32:05 +00:00
skytrias 967afd8bbb try helper procedures / structs 2022-12-18 23:11:23 +01:00
skytrias 0ae1812f90 small fixes and oob checks, stop infinite loops on empty matches 2022-12-18 23:11:23 +01:00
skytrias eb5523d5d3 case insensitive helper call 2022-12-18 23:11:23 +01:00
skytrias 3f4bbbec29 add proper unicode walking 2022-12-18 23:11:23 +01:00
skytrias 70bd220f34 balanced string, frontier pattern, gsub_with and their tests added 2022-12-18 23:11:23 +01:00
skytrias bd3596f012 create lua strlib text package and tests 2022-12-18 23:11:23 +01:00
gingerBill 66ce990e0b gb_internal to docs and other auxiliary files 2022-12-18 21:51:04 +00:00
gingerBill 690666537c Add gb_internal to checker 2022-12-18 21:46:27 +00:00
gingerBill 056ba1ed13 Even more gb_internal everywhere 2022-12-18 21:24:45 +00:00
gingerBill 93a1f2bf61 Merge remote-tracking branch 'remotes/Odin-GitHub/master' into compiler-improvements-2022-12 2022-12-18 21:17:19 +00:00
gingerBill ac5f5a33e9 gb_internal a lot 2022-12-18 21:17:07 +00:00
gingerBill 0829ac30f7 Merge pull request #2249 from tstibor/fix_odinfmt
Update odinfmt with new filepath.Walk_Proc signature
2022-12-15 10:11:42 +00:00
Thomas Stibor 9d50a04905 Update odinfmt with new filepath.Walk_Proc signature
Commit f9f4551e8d introduced
the additional parameter: `user_data: rawptr` to `filepath.Walk_Proc` callback.
This commit updates odinfmt to meet this new additional parameter.
2022-12-15 09:23:43 +01:00
Thomas Stibor 1ca7da6914 Enable -out:<filepath> for build and runs with the attribute @(test)
According to the odin help command
$ odin help test
...
-out:<filepath>
	Set the file name of the outputted executable
	Example: -out:foo.exe

building and running tests the executable output filepath shall be
specified. However, the -out parameter is disabled, resulting in error message:

Unknown flag for 'odin test': 'out'
'out' is supported with the following commands:
	run, build

Omitting the -out parameter results in default filepath '01.bin' (on Linux).
However, it is desirable for user specifying the output filepath, e.g. by
using this Makefile snippet:

TARGET=main
FLAGS=-warnings-as-errors -verbose-errors

all: run

run:
        @odin run . $(FLAGS) -out:$(TARGET)

test:
        @odin test . $(FLAGS) -out:$(TARGET)

clean:
        @rm -f $(TARGET)

In addition a typo is fixed.
2022-12-14 14:26:32 +01:00
gingerBill 56e050fbc9 Merge pull request #2245 from Said6289/small-typo-in-linalg-any
Fix typo in linalg.any
2022-12-13 11:37:59 +00:00
Said Al Attrach 70e48e39a4 Fix typo in linalg.any 2022-12-13 12:18:58 +01:00
gingerBill 2b0c04f27e Merge pull request #2244 from ftphikari/master
sys/windows: add GetMonitorInfoW
2022-12-13 11:18:35 +00:00
hikari 1ddbe16d28 sys/windows: add GetMonitorInfoW 2022-12-13 10:25:18 +02:00
Jeroen van Rijn 09c1128d9e Merge pull request #2242 from Tetralux/fix-shrink-array
[runtime] Fix typo in shrink_dynamic_array()
2022-12-11 20:14:21 +01:00
Tetralux 588c52a854 [runtime] Fix typo in shrink_dynamic_array() 2022-12-11 09:10:17 +00:00
Jeroen van Rijn 86ec3bcb44 Merge pull request #2238 from awwdev/reflect-procs-aliasing-runtime
Aliasing some procs to avoid code repetition
2022-12-09 19:36:59 +01:00
Jeroen van Rijn 9fc606de48 Merge pull request #2239 from awwdev/patch-3
Fix typo err: runtime.Allocator to Allocator_Error
2022-12-09 19:33:02 +01:00
André (counter) 7fbee88061 Fix typo err: runtime.Allocator to Allocator_Error 2022-12-09 19:20:03 +01:00
André (counter) b3be2cdf9d Aliasing some procs to avoid code repetition
Aliasing some procedures within package reflect so they reference procedures from package runtime.
This avoids redundancy and potential deviation.
Not 100% sure about the ODIN_DISALLOW_RTTI part but I think it should be congruent as well.
2022-12-09 18:14:47 +01:00
gingerBill ff6b76986a Use C++11 loops for some arrays 2022-12-09 12:32:54 +00:00
gingerBill 5c3624eb86 Fix map looping 2022-12-09 12:18:49 +00:00
gingerBill 144e357fd2 Add extra check 2022-12-09 11:37:15 +00:00
gingerBill be22f0d1e1 Fix variable shadow in compiler 2022-12-09 11:32:52 +00:00
gingerBill 34a048f7da Replace compiler for loops for the hash-table types to simplify code usage 2022-12-09 11:29:28 +00:00
gingerBill ffe953b43d Make os.get_last_error contextless 2022-12-08 16:04:03 +00:00
gingerBill b8eacfc7b6 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-12-08 15:58:44 +00:00
gingerBill f8452bf1fc Add different variants for once_do 2022-12-08 15:58:39 +00:00
gingerBill 20943a81c1 Make sync calls contextless where possible 2022-12-08 15:55:53 +00:00
gingerBill 1c4e75e83f Merge pull request #2234 from ftphikari/master
sys/windows: add DescribePixelFormat
2022-12-08 14:59:48 +00:00
gingerBill 9cb9964c2d Remove old code 2022-12-08 00:52:11 +00:00
gingerBill 1f8f94276e Initialize the multiple return value map in lb_create_dummy_procedure 2022-12-07 16:44:26 +00:00
hikari 0d7c89e84a sys/windows: add DescribePixelFormat 2022-12-07 14:33:12 +02:00
gingerBill a5bdb4a8e8 Merge pull request #2208 from odin-lang/multiple-return-abi-experiment
Multiple Return ABI Changes and Improvements
2022-12-07 11:42:23 +00:00
gingerBill d88b052d2d Naïve optimization of named _split_ multiple return valued when defer is never used
This is a naïve optimization but it helps a lot in the general case where callee temporary stack variables
are not allocated to represent the named return values by using that specific memory.

In the future, try to check if a specific named return value is ever used a `defer` within a procedure or not,
or is ever passed to a nested procedure call (e.g. possibly escapes).
2022-11-25 23:57:55 +00:00
gingerBill 615eccb6d1 Correct return ptr semantics for split returns 2022-11-24 14:26:45 +00:00
gingerBill d3c65b6ba5 Make split multiple return logic only work for the native Odin calling conventions 2022-11-24 13:16:02 +00:00
gingerBill 90415e4a6e Add split multiple return to different ABIs 2022-11-24 12:14:19 +00:00
gingerBill 7352c312e0 Fix type for split returns code 2022-11-24 11:20:28 +00:00
gingerBill 0befadde1d Basic copy elision support for multiple return values 2022-11-24 01:27:39 +00:00
gingerBill aef8b25a8e Listen to past Bill's wisdom 2022-11-23 23:54:12 +00:00
gingerBill ae81117f70 Merge branch 'master' into multiple-return-abi-experiment 2022-11-23 23:43:00 +00:00
gingerBill b7b9a016d3 Merge branch 'master' into multiple-return-abi-experiment 2022-11-23 22:48:56 +00:00
gingerBill 708a1b0cd3 Clean up return logic for split multiple return ABI experiment 2022-11-23 16:42:26 +00:00
gingerBill 7ab591667a Basic support for new ABI experiment on Win64 2022-11-23 16:25:09 +00:00
gingerBill 0a0db23b17 Remove copy elision code 2022-11-22 15:49:27 +00:00
203 changed files with 15314 additions and 9825 deletions
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Close Stale Issues
uses: actions/stale@v4.1.0
uses: actions/stale@v7.0.0
with:
# stale-issue-message: |
# Hello!
@@ -36,7 +36,7 @@ jobs:
# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone..
days-before-stale: 120
days-before-close: 30
days-before-close: -1
exempt-draft-pr: true
ascending: true
operations-per-run: 1000
+3 -1
View File
@@ -62,12 +62,14 @@ if %release_mode% EQU 0 ( rem Debug
set compiler_warnings= ^
-W4 -WX ^
-wd4100 -wd4101 -wd4127 -wd4146 ^
-wd4505 ^
-wd4456 -wd4457
set compiler_includes= ^
/Isrc\
set libs= ^
kernel32.lib ^
Synchronization.lib ^
bin\llvm\windows\LLVM-C.lib
set linker_flags= -incremental:no -opt:ref -subsystem:console
@@ -94,4 +96,4 @@ if %release_mode% EQU 0 odin run examples/demo
del *.obj > NUL 2> NUL
:end_of_build
:end_of_build
+1 -1
View File
@@ -50,7 +50,7 @@ config_darwin() {
panic "Requirement: llvm-config must be base version smaller than 15"
fi
LDFLAGS="$LDFLAGS -liconv -ldl"
LDFLAGS="$LDFLAGS -liconv -ldl -framework System"
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS -lLLVM-C"
}
+1 -1
View File
@@ -109,7 +109,7 @@ jmag :: proc(value: Quaternion) -> Float ---
kmag :: proc(value: Quaternion) -> Float ---
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
expand_to_tuple :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
expand_values :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
min :: proc(values: ..T) -> T ---
max :: proc(values: ..T) -> T ---
+1 -1
View File
@@ -108,7 +108,7 @@ when ODIN_OS == .Linux {
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_timedwait :: proc(cond: ^cnd_t, mtx: ^mtx_t, ts: ^timespec) -> int ---
cnd_wait :: proc(cond: ^cnd_t, mtx: ^mtx_t) -> int ---
// 7.26.4 Mutex functions
+2
View File
@@ -212,6 +212,8 @@ read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int
@(optimization_mode="speed")
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
// TODO: REMOVE ALL USE OF context.temp_allocator here
// the is literally no need for it
b := make([]u8, size, context.temp_allocator)
_, e := z.input->impl_read(b[:])
if e == .None {
+46 -1
View File
@@ -1,6 +1,8 @@
package container_small_array
import "core:builtin"
import "core:runtime"
_ :: runtime
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
data: [N]T,
@@ -32,6 +34,20 @@ get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
return &a.data[index]
}
get_safe :: proc(a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check {
if index < 0 || index >= a.len {
return {}, false
}
return a.data[index], true
}
get_ptr_safe :: proc(a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check {
if index < 0 || index >= a.len {
return {}, false
}
return &a.data[index], true
}
set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
a.data[index] = item
}
@@ -93,7 +109,7 @@ pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, o
copy(s[:], s[1:])
a.len -= 1
ok = true
}
}
return
}
@@ -102,6 +118,23 @@ consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_l
a.len -= count
}
ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
runtime.bounds_check_error_loc(loc, index, a.len)
if index+1 < a.len {
copy(a.data[index:], a.data[index+1:])
}
a.len -= 1
}
unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
runtime.bounds_check_error_loc(loc, index, a.len)
n := a.len-1
if index != n {
a.data[index] = a.data[n]
}
a.len -= 1
}
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
resize(a, 0)
}
@@ -111,6 +144,18 @@ push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) {
a.len += n
}
inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {
if a.len < cap(a^) && index >= 0 && index <= len(a^) {
a.len += 1
for i := a.len - 1; i >= index + 1; i -= 1 {
a.data[i] = a.data[i - 1]
}
a.data[index] = item
return true
}
return false
}
append_elem :: push_back
append_elems :: push_back_elems
push :: proc{push_back, push_back_elems}
+1 -1
View File
@@ -12,7 +12,7 @@ _rand_bytes :: proc (dst: []byte) {
for l > 0 {
to_read := min(l, _MAX_PER_CALL_BYTES)
ret := unix.sys_getrandom(raw_data(dst), to_read, 0)
ret := unix.sys_getrandom(raw_data(dst), uint(to_read), 0)
if ret < 0 {
switch os.Errno(-ret) {
case os.EINTR:
+7
View File
@@ -0,0 +1,7 @@
/*
Package core:dynlib implements loading of shared libraries/DLLs and their symbols.
The behaviour of dynamically loaded libraries is specific to the target platform of the program.
For in depth detail on the underlying behaviour please refer to your target platform's documentation.
*/
package dynlib
+81 -2
View File
@@ -1,15 +1,94 @@
package dynlib
/*
A handle to a dynamically loaded library.
*/
Library :: distinct rawptr
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
/*
Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
library available to resolve references in subsequently loaded libraries.
The paramater `global_symbols` is only used for the platforms `linux`, `darwin`, `freebsd` and `openbsd`.
On `windows` this paramater is ignored.
The underlying behaviour is platform specific.
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`.
On `windows` refer to `LoadLibraryW`.
**Implicit Allocators**
`context.temp_allocator`
Example:
import "core:dynlib"
import "core:fmt"
load_my_library :: proc() {
LIBRARY_PATH :: "my_library.dll"
library, ok := dynlib.load_library(LIBRARY_PATH)
if ! ok {
return
}
fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
}
*/
load_library :: proc(path: string, global_symbols := false) -> (library: Library, did_load: bool) {
return _load_library(path, global_symbols)
}
unload_library :: proc(library: Library) -> bool {
/*
Unloads a dynamic library.
The underlying behaviour is platform specific.
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlclose`.
On `windows` refer to `FreeLibrary`.
Example:
import "core:dynlib"
import "core:fmt"
load_then_unload_my_library :: proc() {
LIBRARY_PATH :: "my_library.dll"
library, ok := dynlib.load_library(LIBRARY_PATH)
if ! ok {
return
}
did_unload := dynlib.unload_library(library)
if ! did_unload {
return
}
fmt.println("The library %q was successfully unloaded", LIBRARY_PATH)
}
*/
unload_library :: proc(library: Library) -> (did_unload: bool) {
return _unload_library(library)
}
/*
Loads the address of a procedure/variable from a dynamic library.
The underlying behaviour is platform specific.
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`.
On `windows` refer to `GetProcAddress`.
**Implicit Allocators**
`context.temp_allocator`
Example:
import "core:dynlib"
import "core:fmt"
find_a_in_my_library :: proc() {
LIBRARY_PATH :: "my_library.dll"
library, ok := dynlib.load_library(LIBRARY_PATH)
if ! ok {
return
}
a, found_a := dynlib.symbol_address(library, "a")
if found_a do fmt.printf("The symbol %q was found at the address %v", "a", a)
}
*/
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
return _symbol_address(library, symbol)
}
+3
View File
@@ -4,10 +4,12 @@ package dynlib
import win32 "core:sys/windows"
import "core:strings"
import "core:runtime"
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
handle := cast(Library)win32.LoadLibraryW(wide_path)
return handle, handle != nil
@@ -19,6 +21,7 @@ _unload_library :: proc(library: Library) -> bool {
}
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
found = ptr != nil
+5 -1
View File
@@ -198,7 +198,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Procedure:
return .Unsupported_Type
case runtime.Type_Info_Tuple:
case runtime.Type_Info_Parameters:
return .Unsupported_Type
case runtime.Type_Info_Simd_Vector:
@@ -262,10 +262,14 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
}
map_cap := uintptr(runtime.map_cap(m^))
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
i := 0
for bucket_index in 0..<map_cap {
if !runtime.map_hash_is_valid(hs[bucket_index]) {
continue
}
opt_write_iteration(w, opt, i) or_return
i += 1
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))
+2
View File
@@ -346,6 +346,8 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
fields := reflect.struct_fields_zipped(ti.id)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
field_used := make([]bool, len(fields), context.temp_allocator)
use_field_idx := -1
+3 -1
View File
@@ -33,6 +33,7 @@ import "core:intrinsics"
import "core:mem"
import "core:os"
import "core:strings"
import "core:runtime"
likely :: intrinsics.expect
@@ -408,7 +409,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
next := scan(t)
#partial switch next.kind {
case .Ident:
if len(next.text) == 3 && strings.to_lower(next.text, context.temp_allocator) == "xml" {
if len(next.text) == 3 && strings.equal_fold(next.text, "xml") {
parse_prologue(doc) or_return
} else if len(doc.prologue) > 0 {
/*
@@ -614,6 +615,7 @@ parse_prologue :: proc(doc: ^Document) -> (err: Error) {
}
case "encoding":
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
switch strings.to_lower(attr.val, context.temp_allocator) {
case "utf-8", "utf8":
doc.encoding = .UTF_8
+25 -76
View File
@@ -547,7 +547,7 @@ _parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok:
is_digit :: #force_inline proc(r: byte) -> bool { return '0' <= r && r <= '9' }
new_offset = offset
for new_offset <= len(s) {
for new_offset < len(s) {
c := s[new_offset]
if !is_digit(c) {
break
@@ -678,7 +678,7 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
}
} else if fi.zero && fi.width_set {
prec = fi.width
if neg || fi.plus || fi.space {
if neg || fi.plus {
// There needs to be space for the "sign"
prec -= 1
}
@@ -697,7 +697,6 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
flags: strconv.Int_Flags
if fi.hash && !fi.zero { flags |= {.Prefix} }
if fi.plus { flags |= {.Plus} }
if fi.space { flags |= {.Space} }
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
if fi.hash && fi.zero && fi.indent == 0 {
@@ -744,7 +743,7 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
}
} else if fi.zero && fi.width_set {
prec = fi.width
if neg || fi.plus || fi.space {
if neg || fi.plus {
// There needs to be space for the "sign"
prec -= 1
}
@@ -763,7 +762,6 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
flags: strconv.Int_Flags
if fi.hash && !fi.zero { flags |= {.Prefix} }
if fi.plus { flags |= {.Plus} }
if fi.space { flags |= {.Space} }
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
if fi.hash && fi.zero && fi.indent == 0 {
@@ -867,79 +865,30 @@ _pad :: proc(fi: ^Info, s: string) {
}
}
_fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: byte) {
prec := fi.prec if fi.prec_set else 3
buf: [386]byte
// Can return "NaN", "+Inf", "-Inf", "+<value>", "-<value>".
str := strconv.append_float(buf[:], v, float_fmt, prec, bit_size)
if !fi.plus {
// Strip sign from "+<value>" but not "+Inf".
if str[0] == '+' && str[1] != 'I' {
str = str[1:]
}
}
_pad(fi, str)
}
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
switch verb {
case 'f', 'F', 'g', 'G', 'v':
prec: int = 3
if fi.prec_set {
prec = fi.prec
}
buf: [386]byte
str := strconv.append_float(buf[1:], v, 'f', prec, bit_size)
b := buf[:len(str)+1]
if b[1] == '+' || b[1] == '-' {
b = b[1:]
} else {
b[0] = '+'
}
if fi.space && !fi.plus && b[0] == '+' {
b[0] = ' '
}
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
io.write_string(fi.writer, string(b), &fi.n)
return
}
if fi.plus || b[0] != '+' {
if fi.zero && fi.width_set && fi.width > len(b) {
io.write_byte(fi.writer, b[0], &fi.n)
fmt_write_padding(fi, fi.width - len(b))
io.write_string(fi.writer, string(b[1:]), &fi.n)
} else {
_pad(fi, string(b))
}
} else {
_pad(fi, string(b[1:]))
}
_fmt_float_as(fi, v, bit_size, verb, 'f')
case 'e', 'E':
prec: int = 3
if fi.prec_set {
prec = fi.prec
}
buf: [386]byte
str := strconv.append_float(buf[1:], v, 'e', prec, bit_size)
b := buf[:len(str)+1]
if b[1] == '+' || b[1] == '-' {
b = b[1:]
} else {
b[0] = '+'
}
if fi.space && !fi.plus && b[0] == '+' {
b[0] = ' '
}
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
io.write_string(fi.writer, string(b), &fi.n)
return
}
if fi.plus || str[0] != '+' {
if fi.zero && fi.width_set && fi.width > len(b) {
io.write_byte(fi.writer, b[0], &fi.n)
fmt_write_padding(fi, fi.width - len(b))
io.write_string(fi.writer, string(b[1:]), &fi.n)
} else {
_pad(fi, string(b))
}
} else {
_pad(fi, string(b[1:]))
}
// BUG(): "%.3e" returns "3.000e+00"
_fmt_float_as(fi, v, bit_size, verb, 'e')
case 'h', 'H':
prev_fi := fi^
@@ -1834,8 +1783,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
type_info := type_info_of(v.id)
switch info in type_info.variant {
case runtime.Type_Info_Any: // Ignore
case runtime.Type_Info_Tuple: // Ignore
case runtime.Type_Info_Any: // Ignore
case runtime.Type_Info_Parameters: // Ignore
case runtime.Type_Info_Named:
fmt_named(fi, v, verb, info)
+1 -1
View File
@@ -118,7 +118,7 @@ XXH_mul_64_to_128_fold_64 :: #force_inline proc(lhs, rhs: xxh_u64) -> (res: xxh_
}
@(optimization_mode="speed")
XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, auto_cast shift: uint) -> (res: xxh_u64) {
XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res: xxh_u64) {
return v ~ (v >> shift)
}
+3
View File
@@ -8,6 +8,7 @@ import "core:os"
import "core:strconv"
import "core:strings"
import "core:unicode"
import "core:runtime"
Image :: image.Image
Format :: image.Netpbm_Format
@@ -407,6 +408,8 @@ _parse_header_pam :: proc(data: []byte, allocator := context.allocator) -> (head
}
length = header_end_index + len(HEADER_END)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
// string buffer for the tupltype
tupltype: strings.Builder
strings.builder_init(&tupltype, context.temp_allocator); defer strings.builder_destroy(&tupltype)
+17 -14
View File
@@ -16,6 +16,7 @@ import coretime "core:time"
import "core:strings"
import "core:bytes"
import "core:mem"
import "core:runtime"
/*
Cleanup of image-specific data.
@@ -91,6 +92,8 @@ core_time :: proc(c: image.PNG_Chunk) -> (t: coretime.Time, ok: bool) {
}
text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
assert(len(c.data) == int(c.header.length))
#partial switch c.header.type {
case .tEXt:
@@ -194,18 +197,18 @@ text_destroy :: proc(text: Text) {
}
iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) {
ok = true
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
if len(fields[0]) < 1 || len(fields[0]) > 79 {
// Invalid profile name
ok = false; return
return
}
if len(fields[1]) != 0 {
// Compression method should be a zero, which the split turned into an empty slice.
ok = false; return
return
}
// Set up ZLIB context and decompress iCCP payload
@@ -213,12 +216,12 @@ iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) {
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf)
if zlib_error != nil {
bytes.buffer_destroy(&buf)
ok = false; return
return
}
res.name = strings.clone(string(fields[0]))
res.profile = bytes.buffer_to_bytes(&buf)
ok = true
return
}
@@ -256,18 +259,18 @@ plte :: proc(c: image.PNG_Chunk) -> (res: PLTE, ok: bool) {
splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
if c.header.type != .sPLT {
return {}, false
return
}
ok = true
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator)
if len(fields) != 2 {
return {}, false
return
}
res.depth = fields[1][0]
if res.depth != 8 && res.depth != 16 {
return {}, false
return
}
data := fields[1][1:]
@@ -275,21 +278,21 @@ splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
if res.depth == 8 {
if len(data) % 6 != 0 {
return {}, false
return
}
count = len(data) / 6
if count > 256 {
return {}, false
return
}
res.entries = mem.slice_data_cast([][4]u8, data)
} else { // res.depth == 16
if len(data) % 10 != 0 {
return {}, false
return
}
count = len(data) / 10
if count > 256 {
return {}, false
return
}
res.entries = mem.slice_data_cast([][4]u16, data)
@@ -297,7 +300,7 @@ splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
res.name = strings.clone(string(fields[0]))
res.used = u16(count)
ok = true
return
}
+11 -7
View File
@@ -23,6 +23,7 @@ import "core:bytes"
import "core:io"
import "core:mem"
import "core:intrinsics"
import "core:runtime"
// Limit chunk sizes.
// By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
@@ -1247,6 +1248,8 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) {
// TODO: See about doing a Duff's #unroll where practicable
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Apron so we don't need to special case first rows.
up := make([]u8, row_stride, context.temp_allocator)
ok = true
@@ -1299,10 +1302,9 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) {
}
// @(optimization_mode="speed")
defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_check {
defilter_less_than_8 :: proc(params: ^Filter_Params) -> bool #no_bounds_check {
using params
ok = true
row_stride_in := ((channels * width * depth) + 7) >> 3
row_stride_out := channels * width
@@ -1314,6 +1316,8 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch
// TODO: See about doing a Duff's #unroll where practicable
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Apron so we don't need to special case first rows.
up := make([]u8, row_stride_out, context.temp_allocator)
@@ -1457,18 +1461,18 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch
}
}
return
return true
}
// @(optimization_mode="speed")
defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
defilter_16 :: proc(params: ^Filter_Params) -> bool {
using params
ok = true
stride := channels * 2
row_stride := width * stride
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// TODO: See about doing a Duff's #unroll where practicable
// Apron so we don't need to special case first rows.
up := make([]u8, row_stride, context.temp_allocator)
@@ -1518,7 +1522,7 @@ defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
dest = dest[row_stride:]
}
return
return true
}
defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IHDR, options: Options) -> (err: Error) {
+2 -2
View File
@@ -283,7 +283,7 @@ wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
// x86 Targets (i386, amd64)
x86_cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
x86_cpuid :: proc(ax, cx: u32) -> (eax, ebx, ecx, edx: u32) ---
x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
@@ -305,4 +305,4 @@ valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2,
// Internal compiler use only
__entry_point :: proc() ---
__entry_point :: proc() ---
+1 -1
View File
@@ -531,7 +531,7 @@ not_equal :: proc{not_equal_single, not_equal_array}
any :: proc(x: $A/[$N]bool) -> (out: bool) {
for e in x {
if x {
if e {
return true
}
}
+1 -1
View File
@@ -153,7 +153,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
s := (^Scratch_Allocator)(allocator_data)
if s.data == nil {
DEFAULT_BACKING_SIZE :: 1<<22
DEFAULT_BACKING_SIZE :: 4 * Megabyte
if !(context.allocator.procedure != scratch_allocator_proc &&
context.allocator.data != allocator_data) {
panic("cyclic initialization of the scratch allocator with itself")
+5 -5
View File
@@ -3,11 +3,11 @@ package mem
import "core:runtime"
import "core:intrinsics"
Byte :: 1
Kilobyte :: 1024 * Byte
Megabyte :: 1024 * Kilobyte
Gigabyte :: 1024 * Megabyte
Terabyte :: 1024 * Gigabyte
Byte :: runtime.Byte
Kilobyte :: runtime.Kilobyte
Megabyte :: runtime.Megabyte
Gigabyte :: runtime.Gigabyte
Terabyte :: runtime.Terabyte
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
return runtime.memset(data, i32(value), len)
-13
View File
@@ -23,16 +23,3 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
}
raw_data :: builtin.raw_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),
}
+101 -31
View File
@@ -1,6 +1,7 @@
package mem_virtual
import "core:mem"
import "core:sync"
Arena_Kind :: enum uint {
Growing = 0, // Chained memory blocks (singly linked list).
@@ -8,6 +9,13 @@ Arena_Kind :: enum uint {
Buffer = 2, // Uses a fixed sized buffer.
}
/*
Arena is a generalized arena allocator that supports 3 different variants.
Growing: A linked list of `Memory_Block`s allocated with virtual memory.
Static: A single `Memory_Block` allocated with virtual memory.
Buffer: A single `Memory_Block` created from a user provided []byte.
*/
Arena :: struct {
kind: Arena_Kind,
curr_block: ^Memory_Block,
@@ -15,18 +23,21 @@ Arena :: struct {
total_reserved: uint,
minimum_block_size: uint,
temp_count: uint,
mutex: sync.Mutex,
}
// 1 MiB should be enough to start with
DEFAULT_ARENA_STATIC_COMMIT_SIZE :: 1<<20
DEFAULT_ARENA_STATIC_COMMIT_SIZE :: mem.Megabyte
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: DEFAULT_ARENA_STATIC_COMMIT_SIZE
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
DEFAULT_ARENA_STATIC_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
DEFAULT_ARENA_STATIC_RESERVE_SIZE :: mem.Gigabyte when size_of(uintptr) == 8 else 128 * mem.Megabyte
// Initialization of an `Arena` to be a `.Growing` variant.
// A growing arena is a linked list of `Memory_Block`s allocated with virtual memory.
@(require_results)
arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
arena.kind = .Growing
@@ -37,6 +48,8 @@ arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING
}
// Initialization of an `Arena` to be a `.Static` variant.
// A static arena contains a single `Memory_Block` allocated with virtual memory.
@(require_results)
arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEFAULT_ARENA_STATIC_COMMIT_SIZE) -> (err: Allocator_Error) {
arena.kind = .Static
@@ -46,6 +59,8 @@ arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEF
return
}
// Initialization of an `Arena` to be a `.Buffer` variant.
// A buffer arena contains single `Memory_Block` created from a user provided []byte.
@(require_results)
arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Error) {
if len(buffer) < size_of(Memory_Block) {
@@ -69,6 +84,7 @@ arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Erro
return
}
// Allocates memory from the provided arena.
@(require_results)
arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc)
@@ -78,6 +94,8 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
return nil, nil
}
sync.mutex_guard(&arena.mutex)
switch arena.kind {
case .Growing:
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.reserved {
@@ -115,7 +133,10 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
return
}
// Resets the memory of a Static or Buffer arena to a specific `pos`ition (offset) and zeroes the previously used memory.
arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool {
sync.mutex_guard(&arena.mutex)
if arena.curr_block != nil {
assert(arena.kind != .Growing, "expected a non .Growing arena", loc)
@@ -134,48 +155,72 @@ arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location)
return false
}
// Frees the last memory block of a Growing Arena
arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) {
if free_block := arena.curr_block; free_block != nil {
assert(arena.kind == .Growing, "expected a .Growing arena", loc)
arena.total_used -= free_block.used
arena.total_reserved -= free_block.reserved
arena.curr_block = free_block.prev
memory_block_dealloc(free_block)
}
}
arena_free_all :: proc(arena: ^Arena) {
// Deallocates all but the first memory block of the arena and resets the allocator's usage to 0.
arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
switch arena.kind {
case .Growing:
for arena.curr_block != nil {
arena_growing_free_last_memory_block(arena)
sync.mutex_guard(&arena.mutex)
// NOTE(bill): Free all but the first memory block (if it exists)
for arena.curr_block != nil && arena.curr_block.prev != nil {
arena_growing_free_last_memory_block(arena, loc)
}
arena.total_reserved = 0
// Zero the first block's memory
if arena.curr_block != nil {
mem.zero(arena.curr_block.base, int(arena.curr_block.used))
arena.curr_block.used = 0
}
arena.total_used = 0
case .Static, .Buffer:
arena_static_reset_to(arena, 0)
}
arena.total_used = 0
}
arena_destroy :: proc(arena: ^Arena) {
arena_free_all(arena)
if arena.kind != .Buffer {
// Frees all of the memory allocated by the arena and zeros all of the values of an arena.
// A buffer based arena does not `delete` the provided `[]byte` bufffer.
arena_destroy :: proc(arena: ^Arena, loc := #caller_location) {
sync.mutex_guard(&arena.mutex)
switch arena.kind {
case .Growing:
for arena.curr_block != nil {
arena_growing_free_last_memory_block(arena, loc)
}
case .Static:
memory_block_dealloc(arena.curr_block)
case .Buffer:
// nothing
}
arena.curr_block = nil
arena.curr_block = nil
arena.total_used = 0
arena.total_reserved = 0
arena.temp_count = 0
}
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
arena_growing_bootstrap_new :: proc{
arena_growing_bootstrap_new_by_offset,
arena_growing_bootstrap_new_by_name,
}
// Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy.
arena_static_bootstrap_new :: proc{
arena_static_bootstrap_new_by_offset,
arena_static_bootstrap_new_by_name,
}
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
@(require_results)
arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Arena
@@ -191,11 +236,13 @@ arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintp
return
}
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
@(require_results)
arena_growing_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
}
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
@(require_results)
arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Arena
@@ -211,17 +258,20 @@ arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintpt
return
}
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
@(require_results)
arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
}
// Create an `Allocator` from the provided `Arena`
@(require_results)
arena_allocator :: proc(arena: ^Arena) -> mem.Allocator {
return mem.Allocator{arena_allocator_proc, arena}
}
// The allocator procedured by an `Allocator` produced by `arena_allocator`
arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
@@ -233,17 +283,17 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
return arena_alloc(arena, size, alignment)
return arena_alloc(arena, size, alignment, location)
case .Free:
err = .Mode_Not_Implemented
case .Free_All:
arena_free_all(arena)
arena_free_all(arena, location)
case .Resize:
old_data := ([^]byte)(old_memory)
switch {
case old_data == nil:
return arena_alloc(arena, size, alignment)
return arena_alloc(arena, size, alignment, location)
case size == old_size:
// return old memory
data = old_data[:size]
@@ -257,7 +307,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
return
}
new_memory := arena_alloc(arena, size, alignment) or_return
new_memory := arena_alloc(arena, size, alignment, location) or_return
if new_memory == nil {
return
}
@@ -278,15 +328,20 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
// An `Arena_Temp` is a way to produce temporary watermarks to reset a arena to a previous state.
// All uses of an `Arena_Temp` must be handled by ending them with `arena_temp_end` or ignoring them with `arena_temp_ignore`.
Arena_Temp :: struct {
arena: ^Arena,
block: ^Memory_Block,
used: uint,
}
// Begins the section of temporary arena memory.
@(require_results)
arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
assert(arena != nil, "nil arena", loc)
sync.mutex_guard(&arena.mutex)
temp.arena = arena
temp.block = arena.curr_block
if arena.curr_block != nil {
@@ -296,36 +351,51 @@ arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena
return
}
// Ends the section of temporary arena memory by resetting the memory to the stored position.
arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
sync.mutex_guard(&arena.mutex)
memory_block_found := false
for block := arena.curr_block; block != nil; block = block.prev {
if block == temp.block {
memory_block_found = true
break
if temp.block != nil {
memory_block_found := false
for block := arena.curr_block; block != nil; block = block.prev {
if block == temp.block {
memory_block_found = true
break
}
}
if !memory_block_found {
assert(arena.curr_block == temp.block, "memory block stored within Arena_Temp not owned by Arena", loc)
}
}
if !memory_block_found {
assert(arena.curr_block == temp.block, "memory block stored within Arena_Temp not owned by Arena", loc)
}
for arena.curr_block != temp.block {
arena_growing_free_last_memory_block(arena)
}
for arena.curr_block != temp.block {
arena_growing_free_last_memory_block(arena)
}
if block := arena.curr_block; block != nil {
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
block.used = temp.used
if block := arena.curr_block; block != nil {
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
block.used = temp.used
}
}
assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
arena.temp_count -= 1
}
// Ignore the use of a `arena_temp_begin` entirely by __not__ resetting to the stored position.
arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
sync.mutex_guard(&arena.mutex)
assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
arena.temp_count -= 1
}
// Asserts that all uses of `Arena_Temp` has been used by an `Arena`
arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) {
assert(arena.temp_count == 0, "Arena_Temp not been ended", loc)
}
+5 -1
View File
@@ -7,6 +7,7 @@ DEFAULT_PAGE_SIZE := uint(4096)
Allocator_Error :: mem.Allocator_Error
@(require_results)
reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
return _reserve(size)
}
@@ -15,6 +16,7 @@ commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
return _commit(data, size)
}
@(require_results)
reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
data = reserve(size) or_return
commit(raw_data(data), size) or_return
@@ -57,6 +59,7 @@ Memory_Block_Flag :: enum u32 {
Memory_Block_Flags :: distinct bit_set[Memory_Block_Flag; u32]
@(require_results)
memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags) -> (block: ^Memory_Block, err: Allocator_Error) {
align_formula :: proc "contextless" (size, align: uint) -> uint {
result := size + align-1
@@ -100,6 +103,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
return &pmblock.block, nil
}
@(require_results)
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint) -> (data: []byte, err: Allocator_Error) {
calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
alignment_offset := uint(0)
@@ -160,7 +164,7 @@ memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
@(private)
@(private, require_results)
safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
z, did_overflow := intrinsics.overflow_add(x, y)
return z, !did_overflow
+2 -1
View File
@@ -14,11 +14,12 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
dirpath: string
dirpath, err = absolute_path_from_handle(fd)
if err != ERROR_NONE {
return
}
defer delete(dirpath)
n := n
size := n
if n <= 0 {
+1 -1
View File
@@ -50,7 +50,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
continue
}
fullpath := make([]byte, len(dirpath)+1+len(filename))
fullpath := make([]byte, len(dirpath)+1+len(filename), context.temp_allocator)
copy(fullpath, dirpath)
copy(fullpath[len(dirpath):], "/")
copy(fullpath[len(dirpath)+1:], filename)
+2
View File
@@ -2,6 +2,7 @@ package os
import "core:strings"
import "core:mem"
import "core:runtime"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir
@@ -51,6 +52,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
continue
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
defer delete(fullpath, context.temp_allocator)
+6 -2
View File
@@ -2,6 +2,7 @@ package os
import win32 "core:sys/windows"
import "core:strings"
import "core:runtime"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
@@ -65,13 +66,16 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
n = -1
size = 100
}
dfi := make([dynamic]File_Info, 0, size)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
wpath: []u16
wpath, err = cleanpath_from_handle_u16(fd)
wpath, err = cleanpath_from_handle_u16(fd, context.temp_allocator)
if len(wpath) == 0 || err != ERROR_NONE {
return
}
dfi := make([dynamic]File_Info, 0, size)
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator)
copy(wpath_search, wpath)
wpath_search[len(wpath)+0] = '\\'
+4
View File
@@ -1,6 +1,7 @@
package os
import win32 "core:sys/windows"
import "core:runtime"
// 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
@@ -18,6 +19,8 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return "", false
}
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
b := make([dynamic]u16, n, context.temp_allocator)
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
if n == 0 {
@@ -87,6 +90,7 @@ environ :: proc(allocator := context.allocator) -> []string {
// clear_env deletes all environment variables
clear_env :: proc() {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
envs := environ(context.temp_allocator)
for env in envs {
for j in 1..<len(env) {
+12
View File
@@ -2,6 +2,7 @@ package os
import win32 "core:sys/windows"
import "core:intrinsics"
import "core:runtime"
import "core:unicode/utf16"
is_path_separator :: proc(c: byte) -> bool {
@@ -327,6 +328,7 @@ get_std_handle :: proc "contextless" (h: uint) -> Handle {
exists :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
attribs := win32.GetFileAttributesW(wpath)
@@ -334,6 +336,7 @@ exists :: proc(path: string) -> bool {
}
is_file :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
attribs := win32.GetFileAttributesW(wpath)
@@ -344,6 +347,7 @@ is_file :: proc(path: string) -> bool {
}
is_dir :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
attribs := win32.GetFileAttributesW(wpath)
@@ -359,6 +363,8 @@ is_dir :: proc(path: string) -> bool {
get_current_directory :: proc(allocator := context.allocator) -> string {
win32.AcquireSRWLockExclusive(&cwd_lock)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
@@ -387,6 +393,7 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
change_directory :: proc(path: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.SetCurrentDirectoryW(wpath) {
@@ -396,6 +403,7 @@ change_directory :: proc(path: string) -> (err: Errno) {
}
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Mode is unused on Windows, but is needed on *nix
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
@@ -407,6 +415,7 @@ make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
remove_directory :: proc(path: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.RemoveDirectoryW(wpath) {
@@ -479,12 +488,14 @@ fix_long_path :: proc(path: string) -> string {
link :: proc(old_name, new_name: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
n := win32.utf8_to_wstring(fix_long_path(new_name))
o := win32.utf8_to_wstring(fix_long_path(old_name))
return Errno(win32.CreateHardLinkW(n, o, nil))
}
unlink :: proc(path: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.DeleteFileW(wpath) {
@@ -496,6 +507,7 @@ unlink :: proc(path: string) -> (err: Errno) {
rename :: proc(old_path, new_path: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
+9 -5
View File
@@ -93,7 +93,7 @@ file_size_from_path :: proc(path: string) -> i64 {
return length
}
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator) -> (data: []byte, success: bool) {
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator
fd, err := open(name, O_RDONLY, 0)
@@ -102,10 +102,10 @@ read_entire_file_from_filename :: proc(name: string, allocator := context.alloca
}
defer close(fd)
return read_entire_file_from_handle(fd, allocator)
return read_entire_file_from_handle(fd, allocator, loc)
}
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator) -> (data: []byte, success: bool) {
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator
length: i64
@@ -118,7 +118,7 @@ read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator)
return nil, true
}
data = make([]byte, int(length), allocator)
data = make([]byte, int(length), allocator, loc)
if data == nil {
return nil, false
}
@@ -216,7 +216,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
}
new_memory = aligned_alloc(new_size, new_alignment, p) or_return
// NOTE: heap_resize does not zero the new memory, so we do it
if new_size > old_size {
new_region := mem.raw_data(new_memory[old_size:])
@@ -261,3 +261,7 @@ heap_allocator :: proc() -> mem.Allocator {
data = nil,
}
}
processor_core_count :: proc() -> int {
return _processor_core_count()
}
+5 -3
View File
@@ -1,10 +1,12 @@
package os2
import "core:runtime"
// get_env retrieves the value of the environment variable named by the key
// It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator
get_env :: proc(key: string, allocator := context.allocator) -> string {
get_env :: proc(key: string, allocator: runtime.Allocator) -> string {
value, _ := lookup_env(key, allocator)
return value
}
@@ -13,7 +15,7 @@ get_env :: proc(key: string, allocator := context.allocator) -> string {
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
return _lookup_env(key, allocator)
}
@@ -36,7 +38,7 @@ clear_env :: proc() {
// 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 {
environ :: proc(allocator: runtime.Allocator) -> []string {
return _environ(allocator)
}
+4 -2
View File
@@ -1,7 +1,9 @@
//+private
package os2
_get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
import "core:runtime"
_get_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
//TODO
return
}
@@ -20,7 +22,7 @@ _clear_env :: proc() {
//TODO
}
_environ :: proc(allocator := context.allocator) -> []string {
_environ :: proc(allocator: runtime.Allocator) -> []string {
//TODO
return nil
}
+13 -1
View File
@@ -65,7 +65,19 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
}
defer win32.FreeEnvironmentStringsW(envs)
r := make([dynamic]string, 0, 50, allocator)
n := 0
for from, i, p := 0, 0, envs; true; i += 1 {
c := ([^]u16)(p)[i]
if c == 0 {
if i <= from {
break
}
n += 1
from = i + 1
}
}
r := make([dynamic]string, 0, n, allocator)
for from, i, p := 0, 0, envs; true; i += 1 {
c := ([^]u16)(p)[i]
if c == 0 {
+7 -14
View File
@@ -39,10 +39,8 @@ _file_allocator :: proc() -> runtime.Allocator {
}
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) {
name_cstr, allocated := _name_to_cstring(name)
defer if allocated {
delete(name_cstr)
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
name_cstr := _name_to_cstring(name)
flags_i: int
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
@@ -254,7 +252,7 @@ _symlink :: proc(old_name, new_name: string) -> Error {
return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
}
_read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> (string, Error) {
_read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
bufsz : uint = 256
buf := make([]byte, bufsz, allocator)
for {
@@ -272,7 +270,7 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> (
}
}
_read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) {
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) {
name_cstr, allocated := _name_to_cstring(name)
defer if allocated {
delete(name_cstr)
@@ -411,12 +409,7 @@ _is_dir_fd :: proc(fd: int) -> bool {
// defined as 512, however, it is well known that paths can exceed that limit.
// So, in theory you could have a path larger than the entire temp_allocator's
// buffer. Therefor, any large paths will use context.allocator.
_name_to_cstring :: proc(name: string) -> (cname: cstring, allocated: bool) {
if len(name) > _CSTRING_NAME_HEAP_THRESHOLD {
cname = strings.clone_to_cstring(name)
allocated = true
return
}
cname = strings.clone_to_cstring(name, context.temp_allocator)
return
@(private="file")
_temp_name_to_cstring :: proc(name: string) -> (cname: cstring) {
return strings.clone_to_cstring(name, context.temp_allocator)
}
+2 -1
View File
@@ -1,6 +1,7 @@
package os2
import "core:mem"
import "core:runtime"
import "core:strconv"
import "core:unicode/utf8"
@@ -74,7 +75,7 @@ read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, err: Error) {
read_entire_file :: proc(name: string, allocator: runtime.Allocator) -> (data: []byte, err: Error) {
f, ferr := open(name)
if ferr != nil {
return nil, ferr
+1 -1
View File
@@ -229,7 +229,7 @@ _setwd :: proc(dir: string) -> Error {
return _ok_or_error(unix.sys_chdir(dir_cstr))
}
_get_full_path :: proc(fd: int, allocator := context.allocator) -> string {
_get_full_path :: proc(fd: int, allocator: runtime.Allocator) -> string {
PROC_FD_PATH :: "/proc/self/fd/"
buf: [32]u8
+3 -3
View File
@@ -83,7 +83,7 @@ _Stat :: struct {
}
_fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) {
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
return _fstat_internal(f.impl.fd, allocator)
}
@@ -111,7 +111,7 @@ _fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Er
}
// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath
_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
name_cstr, allocated := _name_to_cstring(name)
defer if allocated {
delete(name_cstr)
@@ -125,7 +125,7 @@ _stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error
return _fstat_internal(fd, allocator)
}
_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
name_cstr, allocated := _name_to_cstring(name)
defer if allocated {
delete(name_cstr)
+88 -40
View File
@@ -277,8 +277,10 @@ foreign libc {
@(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle ---
@(link_name="close") _unix_close :: proc(handle: Handle) -> c.int ---
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
@(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int ---
@(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int ---
@(link_name="pread") _unix_pread :: proc(handle: Handle, buffer: rawptr, count: c.size_t, offset: i64) -> int ---
@(link_name="pwrite") _unix_pwrite :: proc(handle: Handle, buffer: rawptr, count: c.size_t, offset: i64) -> int ---
@(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int ---
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---
@(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---
@@ -314,6 +316,7 @@ foreign libc {
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---
@(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
}
@@ -333,7 +336,7 @@ foreign dl {
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
}
get_last_error :: proc() -> int {
get_last_error :: proc "contextless" () -> int {
return __error()^
}
@@ -342,21 +345,34 @@ get_last_error_string :: proc() -> string {
}
open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno) {
isDir := is_dir_path(path)
flags := flags
if isDir {
/*
@INFO(Platin): To make it impossible to use the wrong flag for dir's
as you can't write to a dir only read which makes it fail to open
*/
flags = O_RDONLY
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, i32(flags), u16(mode))
if handle == -1 {
return INVALID_HANDLE, 1
return INVALID_HANDLE, cast(Errno)get_last_error()
}
when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
if mode != 0 {
/*
@INFO(Platin): this is only done because O_CREATE for some reason fails to apply mode
should not happen if the handle is a directory
*/
if mode != 0 && !isDir {
err := fchmod(handle, cast(u16)mode)
if err != 0 {
_unix_close(handle)
return INVALID_HANDLE, 1
return INVALID_HANDLE, cast(Errno)err
}
}
}
return handle, 0
}
@@ -372,45 +388,51 @@ close :: proc(fd: Handle) -> bool {
@(private)
MAX_RW :: 0x7fffffff // The limit on Darwin is max(i32), trying to read/write more than that fails.
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
assert(fd != -1)
bytes_total := len(data)
bytes_written_total := 0
for bytes_written_total < bytes_total {
bytes_to_write := min(bytes_total - bytes_written_total, MAX_RW)
slice := data[bytes_written_total:bytes_written_total + bytes_to_write]
bytes_written := _unix_write(fd, raw_data(slice), bytes_to_write)
if bytes_written == -1 {
return bytes_written_total, 1
}
bytes_written_total += bytes_written
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE
}
return bytes_written_total, 0
bytes_written := _unix_write(fd, raw_data(data), c.size_t(len(data)))
if bytes_written < 0 {
return -1, Errno(get_last_error())
}
return bytes_written, ERROR_NONE
}
read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
assert(fd != -1)
bytes_total := len(data)
bytes_read_total := 0
for bytes_read_total < bytes_total {
bytes_to_read := min(bytes_total - bytes_read_total, MAX_RW)
slice := data[bytes_read_total:bytes_read_total + bytes_to_read]
bytes_read := _unix_read(fd, raw_data(slice), bytes_to_read)
if bytes_read == -1 {
return bytes_read_total, 1
}
if bytes_read == 0 {
break
}
bytes_read_total += bytes_read
if len(data) == 0 {
return 0, ERROR_NONE
}
return bytes_read_total, 0
bytes_read := _unix_read(fd, raw_data(data), c.size_t(len(data)))
if bytes_read < 0 {
return -1, Errno(get_last_error())
}
return bytes_read, ERROR_NONE
}
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE
}
bytes_read := _unix_pread(fd, raw_data(data), c.size_t(len(data)), offset)
if bytes_read < 0 {
return -1, Errno(get_last_error())
}
return bytes_read, ERROR_NONE
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE
}
bytes_written := _unix_pwrite(fd, raw_data(data), c.size_t(len(data)), offset)
if bytes_written < 0 {
return -1, Errno(get_last_error())
}
return bytes_written, ERROR_NONE
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
@@ -495,24 +517,28 @@ is_file :: proc {is_file_path, is_file_handle}
is_dir :: proc {is_dir_path, is_dir_handle}
exists :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_access(cpath, O_RDONLY)
return res == 0
}
rename :: proc(old: string, new: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_cstr := strings.clone_to_cstring(old, context.temp_allocator)
new_cstr := strings.clone_to_cstring(new, context.temp_allocator)
return _unix_rename(old_cstr, new_cstr) != -1
}
remove :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _unix_remove(path_cstr) != -1
}
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat
@@ -525,6 +551,7 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat
@@ -590,6 +617,7 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
@private
_readlink :: proc(path: string) -> (string, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
bufsz : uint = 256
@@ -627,6 +655,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
rel = "."
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
path_ptr := _unix_realpath(rel_cstr, nil)
@@ -642,6 +671,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
}
access :: proc(path: string, mask: int) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _unix_access(cstr, mask) == 0
}
@@ -666,6 +696,7 @@ heap_free :: proc(ptr: rawptr) {
}
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator)
cstr := _unix_getenv(path_str)
if cstr == nil {
@@ -688,6 +719,7 @@ get_current_directory :: proc() -> string {
return string(cwd)
}
if Errno(get_last_error()) != ERANGE {
delete(buf)
return ""
}
resize(&buf, len(buf)+page_size)
@@ -696,6 +728,7 @@ get_current_directory :: proc() -> string {
}
set_current_directory :: proc(path: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
if res == -1 {
@@ -705,6 +738,7 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
}
make_directory :: proc(path: string, mode: u16 = 0o775) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode)
if res == -1 {
@@ -729,12 +763,14 @@ current_thread_id :: proc "contextless" () -> int {
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, flags)
return handle
}
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
proc_handle := _unix_dlsym(handle, cstr)
return proc_handle
@@ -759,6 +795,18 @@ get_page_size :: proc() -> int {
return page_size
}
@(private)
_processor_core_count :: proc() -> int {
count : int = 0
count_size := size_of(count)
if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 {
if count > 0 {
return count
}
}
return 1
}
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
+35 -2
View File
@@ -287,6 +287,7 @@ foreign libc {
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
}
@@ -303,11 +304,12 @@ is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
get_last_error :: proc() -> int {
get_last_error :: proc "contextless" () -> int {
return __errno_location()^
}
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 {
@@ -360,6 +362,7 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
}
rename :: proc(old_path, new_path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
res := _unix_rename(old_path_cstr, new_path_cstr)
@@ -370,6 +373,7 @@ rename :: proc(old_path, new_path: string) -> Errno {
}
remove :: proc(path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr)
if res == -1 {
@@ -379,6 +383,7 @@ remove :: proc(path: string) -> Errno {
}
make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode)
if res == -1 {
@@ -388,6 +393,7 @@ make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
}
remove_directory :: proc(path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr)
if res == -1 {
@@ -473,6 +479,7 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat = ---
result := _unix_lstat(cstr, &s)
@@ -484,6 +491,7 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
// deliberately uninitialized
@@ -549,6 +557,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
@private
_readlink :: proc(path: string) -> (string, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
bufsz : uint = MAX_PATH
@@ -566,7 +576,8 @@ _readlink :: proc(path: string) -> (string, Errno) {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
}
}
unreachable()
return "", Errno{}
}
// XXX FreeBSD
@@ -579,6 +590,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
if rel == "" {
rel = "."
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
@@ -595,6 +607,8 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
}
access :: proc(path: string, mask: int) -> (bool, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := _unix_access(cstr, c.int(mask))
if result == -1 {
@@ -625,6 +639,8 @@ heap_free :: proc(ptr: rawptr) {
}
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator)
cstr := _unix_getenv(path_str)
if cstr == nil {
@@ -650,6 +666,7 @@ get_current_directory :: proc() -> string {
return string(cwd)
}
if Errno(get_last_error()) != ERANGE {
delete(buf)
return ""
}
resize(&buf, len(buf)+page_size)
@@ -658,6 +675,7 @@ get_current_directory :: proc() -> string {
}
set_current_directory :: proc(path: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
if res == -1 do return Errno(get_last_error())
@@ -674,12 +692,14 @@ current_thread_id :: proc "contextless" () -> int {
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags))
return handle
}
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
proc_handle := _unix_dlsym(handle, cstr)
return proc_handle
@@ -702,6 +722,19 @@ get_page_size :: proc() -> int {
return page_size
}
@(private)
_processor_core_count :: proc() -> int {
count : int = 0
count_size := size_of(count)
if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 {
if count > 0 {
return count
}
}
return 1
}
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
+105 -168
View File
@@ -236,13 +236,13 @@ S_IRUSR :: 0o0400 // R for owner
S_IWUSR :: 0o0200 // W for owner
S_IXUSR :: 0o0100 // X for owner
// Read, write, execute/search by group
// Read, write, execute/search by group
S_IRWXG :: 0o0070 // RWX mask for group
S_IRGRP :: 0o0040 // R for group
S_IWGRP :: 0o0020 // W for group
S_IXGRP :: 0o0010 // X for group
// Read, write, execute/search by others
// Read, write, execute/search by others
S_IRWXO :: 0o0007 // RWX mask for other
S_IROTH :: 0o0004 // R for other
S_IWOTH :: 0o0002 // W for other
@@ -270,140 +270,11 @@ AT_FDCWD :: ~uintptr(99) /* -100 */
AT_REMOVEDIR :: uintptr(0x200)
AT_SYMLINK_NOFOLLOW :: uintptr(0x100)
_unix_personality :: proc(persona: u64) -> int {
return int(intrinsics.syscall(unix.SYS_personality, uintptr(persona)))
}
_unix_fork :: proc() -> Pid {
when ODIN_ARCH != .arm64 {
res := int(intrinsics.syscall(unix.SYS_fork))
} else {
res := int(intrinsics.syscall(unix.SYS_clone, unix.SIGCHLD))
}
return -1 if res < 0 else Pid(res)
}
_unix_open :: proc(path: cstring, flags: int, mode: int = 0o000) -> Handle {
when ODIN_ARCH != .arm64 {
res := int(intrinsics.syscall(unix.SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
} else { // NOTE: arm64 does not have open
res := int(intrinsics.syscall(unix.SYS_openat, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
}
return -1 if res < 0 else Handle(res)
}
_unix_close :: proc(fd: Handle) -> int {
return int(intrinsics.syscall(unix.SYS_close, uintptr(fd)))
}
_unix_read :: proc(fd: Handle, buf: rawptr, size: uint) -> int {
return int(intrinsics.syscall(unix.SYS_read, uintptr(fd), uintptr(buf), uintptr(size)))
}
_unix_write :: proc(fd: Handle, buf: rawptr, size: uint) -> int {
return int(intrinsics.syscall(unix.SYS_write, uintptr(fd), uintptr(buf), uintptr(size)))
}
_unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
return i64(intrinsics.syscall(unix.SYS_lseek, uintptr(fd), uintptr(offset), uintptr(whence)))
} else {
low := uintptr(offset & 0xFFFFFFFF)
high := uintptr(offset >> 32)
result: i64
res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
return -1 if res < 0 else result
}
}
_unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> int {
when ODIN_ARCH == .amd64 {
return int(intrinsics.syscall(unix.SYS_stat, uintptr(rawptr(path)), uintptr(stat)))
} else when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(unix.SYS_stat64, uintptr(rawptr(path)), uintptr(stat)))
} else { // NOTE: arm64 does not have stat
return int(intrinsics.syscall(unix.SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), 0))
}
}
_unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> int {
when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
return int(intrinsics.syscall(unix.SYS_fstat, uintptr(fd), uintptr(stat)))
} else {
return int(intrinsics.syscall(unix.SYS_fstat64, uintptr(fd), uintptr(stat)))
}
}
_unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> int {
when ODIN_ARCH == .amd64 {
return int(intrinsics.syscall(unix.SYS_lstat, uintptr(rawptr(path)), uintptr(stat)))
} else when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(unix.SYS_lstat64, uintptr(rawptr(path)), uintptr(stat)))
} else { // NOTE: arm64 does not have any lstat
return int(intrinsics.syscall(unix.SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW))
}
}
_unix_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(unix.SYS_readlink, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
} else { // NOTE: arm64 does not have readlink
return int(intrinsics.syscall(unix.SYS_readlinkat, AT_FDCWD, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
}
}
_unix_access :: proc(path: cstring, mask: int) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(unix.SYS_access, uintptr(rawptr(path)), uintptr(mask)))
} else { // NOTE: arm64 does not have access
return int(intrinsics.syscall(unix.SYS_faccessat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask)))
}
}
_unix_getcwd :: proc(buf: rawptr, size: uint) -> int {
return int(intrinsics.syscall(unix.SYS_getcwd, uintptr(buf), uintptr(size)))
}
_unix_chdir :: proc(path: cstring) -> int {
return int(intrinsics.syscall(unix.SYS_chdir, uintptr(rawptr(path))))
}
_unix_rename :: proc(old, new: cstring) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(unix.SYS_rename, uintptr(rawptr(old)), uintptr(rawptr(new))))
} else { // NOTE: arm64 does not have rename
return int(intrinsics.syscall(unix.SYS_renameat, AT_FDCWD, uintptr(rawptr(old)), uintptr(rawptr(new))))
}
}
_unix_unlink :: proc(path: cstring) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(unix.SYS_unlink, uintptr(rawptr(path))))
} else { // NOTE: arm64 does not have unlink
return int(intrinsics.syscall(unix.SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), 0))
}
}
_unix_rmdir :: proc(path: cstring) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(unix.SYS_rmdir, uintptr(rawptr(path))))
} else { // NOTE: arm64 does not have rmdir
return int(intrinsics.syscall(unix.SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), AT_REMOVEDIR))
}
}
_unix_mkdir :: proc(path: cstring, mode: u32) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(unix.SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
} else { // NOTE: arm64 does not have mkdir
return int(intrinsics.syscall(unix.SYS_mkdirat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode)))
}
}
foreign libc {
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
@(link_name="get_nprocs") _unix_get_nprocs :: proc() -> c.int ---
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
@@ -414,6 +285,7 @@ foreign libc {
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
@(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> int ---
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
@(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@@ -441,12 +313,12 @@ _get_errno :: proc(res: int) -> Errno {
}
// get errno from libc
get_last_error :: proc() -> int {
get_last_error :: proc "contextless" () -> int {
return __errno_location()^
}
personality :: proc(persona: u64) -> (Errno) {
res := _unix_personality(persona)
res := unix.sys_personality(persona)
if res == -1 {
return _get_errno(res)
}
@@ -454,28 +326,48 @@ personality :: proc(persona: u64) -> (Errno) {
}
fork :: proc() -> (Pid, Errno) {
pid := _unix_fork()
pid := unix.sys_fork()
if pid == -1 {
return -1, _get_errno(int(pid))
return -1, _get_errno(pid)
}
return pid, ERROR_NONE
return Pid(pid), ERROR_NONE
}
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, flags, mode)
if handle < 0 {
return INVALID_HANDLE, _get_errno(int(handle))
execvp :: proc(path: string, args: []string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
args_cstrs := make([]cstring, len(args) + 2, context.temp_allocator)
args_cstrs[0] = strings.clone_to_cstring(path, context.temp_allocator)
for i := 0; i < len(args); i += 1 {
args_cstrs[i+1] = strings.clone_to_cstring(args[i], context.temp_allocator)
}
return handle, ERROR_NONE
_unix_execvp(path_cstr, raw_data(args_cstrs))
return Errno(get_last_error())
}
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0o000) -> (Handle, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := unix.sys_open(cstr, flags, uint(mode))
if handle < 0 {
return INVALID_HANDLE, _get_errno(handle)
}
return Handle(handle), ERROR_NONE
}
close :: proc(fd: Handle) -> Errno {
return _get_errno(_unix_close(fd))
return _get_errno(unix.sys_close(int(fd)))
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
if len(data) == 0 {
return 0, ERROR_NONE
}
bytes_read := unix.sys_read(int(fd), raw_data(data), len(data))
if bytes_read < 0 {
return -1, _get_errno(bytes_read)
}
@@ -486,50 +378,78 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE
}
bytes_written := _unix_write(fd, &data[0], uint(len(data)))
bytes_written := unix.sys_write(int(fd), raw_data(data), len(data))
if bytes_written < 0 {
return -1, _get_errno(bytes_written)
}
return int(bytes_written), ERROR_NONE
return bytes_written, ERROR_NONE
}
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE
}
bytes_read := unix.sys_pread(int(fd), raw_data(data), len(data), offset)
if bytes_read < 0 {
return -1, _get_errno(bytes_read)
}
return bytes_read, ERROR_NONE
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE
}
bytes_written := unix.sys_pwrite(int(fd), raw_data(data), uint(len(data)), offset)
if bytes_written < 0 {
return -1, _get_errno(bytes_written)
}
return bytes_written, ERROR_NONE
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
res := _unix_seek(fd, offset, whence)
res := unix.sys_lseek(int(fd), offset, whence)
if res < 0 {
return -1, _get_errno(int(res))
}
return res, ERROR_NONE
return i64(res), ERROR_NONE
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
result := _unix_fstat(fd, &s)
if result < 0 {
return 0, _get_errno(result)
}
return max(s.size, 0), ERROR_NONE
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
result := unix.sys_fstat(int(fd), rawptr(&s))
if result < 0 {
return 0, _get_errno(result)
}
return max(s.size, 0), ERROR_NONE
}
rename :: proc(old_path, new_path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
return _get_errno(_unix_rename(old_path_cstr, new_path_cstr))
return _get_errno(unix.sys_rename(old_path_cstr, new_path_cstr))
}
remove :: proc(path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _get_errno(_unix_unlink(path_cstr))
return _get_errno(unix.sys_unlink(path_cstr))
}
make_directory :: proc(path: string, mode: u32 = 0o775) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _get_errno(_unix_mkdir(path_cstr, mode))
return _get_errno(unix.sys_mkdir(path_cstr, uint(mode)))
}
remove_directory :: proc(path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _get_errno(_unix_rmdir(path_cstr))
return _get_errno(unix.sys_rmdir(path_cstr))
}
is_file_handle :: proc(fd: Handle) -> bool {
@@ -581,8 +501,9 @@ is_file :: proc {is_file_path, is_file_handle}
is_dir :: proc {is_dir_path, is_dir_handle}
exists :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_access(cpath, O_RDONLY)
res := unix.sys_access(cpath, O_RDONLY)
return res == 0
}
@@ -616,11 +537,12 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
result := _unix_stat(cstr, &s)
result := unix.sys_stat(cstr, &s)
if result < 0 {
return s, _get_errno(result)
}
@@ -629,11 +551,12 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
result := _unix_lstat(cstr, &s)
result := unix.sys_lstat(cstr, &s)
if result < 0 {
return s, _get_errno(result)
}
@@ -644,7 +567,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
result := _unix_fstat(fd, &s)
result := unix.sys_fstat(int(fd), rawptr(&s))
if result < 0 {
return s, _get_errno(result)
}
@@ -696,12 +619,13 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
@private
_readlink :: proc(path: string) -> (string, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
bufsz : uint = 256
buf := make([]byte, bufsz)
for {
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
rc := unix.sys_readlink(path_cstr, &(buf[0]), bufsz)
if rc < 0 {
delete(buf)
return "", _get_errno(rc)
@@ -731,6 +655,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
if rel == "" {
rel = "."
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
@@ -747,8 +672,9 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
}
access :: proc(path: string, mask: int) -> (bool, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := _unix_access(cstr, mask)
result := unix.sys_access(cstr, mask)
if result < 0 {
return false, _get_errno(result)
}
@@ -777,6 +703,7 @@ heap_free :: proc(ptr: rawptr) {
}
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator)
// NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
cstr := _unix_getenv(path_str)
@@ -792,6 +719,7 @@ get_env :: proc(key: string, allocator := context.allocator) -> (value: string)
}
set_env :: proc(key, value: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
s := strings.concatenate({key, "=", value, "\x00"}, context.temp_allocator)
res := _unix_putenv(strings.unsafe_string_to_cstring(s))
if res < 0 {
@@ -801,6 +729,7 @@ set_env :: proc(key, value: string) -> Errno {
}
unset_env :: proc(key: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
s := strings.clone_to_cstring(key, context.temp_allocator)
res := _unix_putenv(s)
if res < 0 {
@@ -816,12 +745,13 @@ get_current_directory :: proc() -> string {
page_size := get_page_size()
buf := make([dynamic]u8, page_size)
for {
#no_bounds_check res := _unix_getcwd(&buf[0], uint(len(buf)))
#no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
if res >= 0 {
return strings.string_from_nul_terminated_ptr(&buf[0], len(buf))
}
if _get_errno(res) != ERANGE {
delete(buf)
return ""
}
resize(&buf, len(buf)+page_size)
@@ -830,8 +760,9 @@ get_current_directory :: proc() -> string {
}
set_current_directory :: proc(path: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
res := unix.sys_chdir(cstr)
if res < 0 {
return _get_errno(res)
}
@@ -848,12 +779,14 @@ current_thread_id :: proc "contextless" () -> int {
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags))
return handle
}
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
proc_handle := _unix_dlsym(handle, cstr)
return proc_handle
@@ -878,6 +811,10 @@ get_page_size :: proc() -> int {
return page_size
}
@(private)
_processor_core_count :: proc() -> int {
return int(_unix_get_nprocs())
}
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
+24 -3
View File
@@ -269,6 +269,7 @@ foreign libc {
@(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
@(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long ---
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
@@ -294,7 +295,7 @@ is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
get_last_error :: proc() -> int {
get_last_error :: proc "contextless" () -> int {
return __errno()^
}
@@ -307,6 +308,7 @@ fork :: proc() -> (Pid, Errno) {
}
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 {
@@ -359,6 +361,7 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
}
rename :: proc(old_path, new_path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
res := _unix_rename(old_path_cstr, new_path_cstr)
@@ -369,6 +372,7 @@ rename :: proc(old_path, new_path: string) -> Errno {
}
remove :: proc(path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr)
if res == -1 {
@@ -378,6 +382,7 @@ remove :: proc(path: string) -> Errno {
}
make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode)
if res == -1 {
@@ -387,6 +392,7 @@ make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
}
remove_directory :: proc(path: string) -> Errno {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr)
if res == -1 {
@@ -472,6 +478,7 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
// deliberately uninitialized
@@ -485,6 +492,7 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
// deliberately uninitialized
@@ -551,6 +559,7 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
@private
_readlink :: proc(path: string) -> (string, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
bufsz : uint = MAX_PATH
@@ -568,7 +577,6 @@ _readlink :: proc(path: string) -> (string, Errno) {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
}
}
unreachable()
}
// XXX OpenBSD
@@ -582,6 +590,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
rel = "."
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
path_ptr := _unix_realpath(rel_cstr, nil)
@@ -597,6 +606,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
}
access :: proc(path: string, mask: int) -> (bool, Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_access(cstr, c.int(mask))
if res == -1 {
@@ -627,6 +637,7 @@ heap_free :: proc(ptr: rawptr) {
}
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator)
cstr := _unix_getenv(path_str)
if cstr == nil {
@@ -648,6 +659,7 @@ get_current_directory :: proc() -> string {
return string(cwd)
}
if Errno(get_last_error()) != ERANGE {
delete(buf)
return ""
}
resize(&buf, len(buf) + MAX_PATH)
@@ -656,6 +668,7 @@ get_current_directory :: proc() -> string {
}
set_current_directory :: proc(path: string) -> (err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
if res == -1 {
@@ -674,12 +687,14 @@ current_thread_id :: proc "contextless" () -> int {
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags))
return handle
}
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
proc_handle := _unix_dlsym(handle, cstr)
return proc_handle
@@ -704,6 +719,12 @@ get_page_size :: proc() -> int {
return page_size
}
_SC_NPROCESSORS_ONLN :: 503
@(private)
_processor_core_count :: proc() -> int {
return int(_sysconf(_SC_NPROCESSORS_ONLN))
}
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
@@ -711,4 +732,4 @@ _alloc_command_line_arguments :: proc() -> []string {
res[i] = string(arg)
}
return res
}
}
+4 -1
View File
@@ -89,7 +89,10 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
current_thread_id :: proc "contextless" () -> int {
return 0
}
@(private)
_processor_core_count :: proc() -> int {
return 1
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
stat, err := wasi.fd_filestat_get(wasi.fd_t(fd))
+24 -1
View File
@@ -3,6 +3,7 @@ package os
import win32 "core:sys/windows"
import "core:runtime"
import "core:intrinsics"
Handle :: distinct uintptr
File_Time :: distinct u64
@@ -126,7 +127,29 @@ get_page_size :: proc() -> int {
return page_size
}
@(private)
_processor_core_count :: proc() -> int {
length : win32.DWORD = 0
result := win32.GetLogicalProcessorInformation(nil, &length)
thread_count := 0
if !result && win32.GetLastError() == 122 && length > 0 {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
processors := make([]win32.SYSTEM_LOGICAL_PROCESSOR_INFORMATION, length, context.temp_allocator)
result = win32.GetLogicalProcessorInformation(&processors[0], &length)
if result {
for processor in processors {
if processor.Relationship == .RelationProcessorCore {
thread := intrinsics.count_ones(processor.ProcessorMask)
thread_count += int(thread)
}
}
}
}
return thread_count
}
exit :: proc "contextless" (code: int) -> ! {
runtime._cleanup_runtime_contextless()
@@ -214,4 +237,4 @@ is_windows_10 :: proc() -> bool {
is_windows_11 :: proc() -> bool {
osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF)
}
}
+7 -3
View File
@@ -1,6 +1,7 @@
package os
import "core:time"
import "core:runtime"
import win32 "core:sys/windows"
@(private)
@@ -11,6 +12,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
if name == "" {
name = "."
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
p := win32.utf8_to_utf16(name, context.temp_allocator)
buf := make([dynamic]u16, 100)
defer delete(buf)
@@ -36,6 +38,7 @@ _stat :: proc(name: string, create_file_attributes: u32, allocator := context.al
context.allocator = allocator
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
wname := win32.utf8_to_wstring(fix_long_path(name), context.temp_allocator)
fa: win32.WIN32_FILE_ATTRIBUTE_DATA
@@ -132,14 +135,15 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
@(private)
cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
buf, err := cleanpath_from_handle_u16(fd)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
buf, err := cleanpath_from_handle_u16(fd, context.temp_allocator)
if err != 0 {
return "", err
}
return win32.utf16_to_utf8(buf, context.allocator) or_else "", err
}
@(private)
cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) {
if fd == 0 {
return nil, ERROR_INVALID_HANDLE
}
@@ -149,7 +153,7 @@ cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
if n == 0 {
return nil, Errno(win32.GetLastError())
}
buf := make([]u16, max(n, win32.DWORD(260))+1, context.temp_allocator)
buf := make([]u16, max(n, win32.DWORD(260))+1, allocator)
buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0)
return buf[:buf_len], ERROR_NONE
}
+2 -1
View File
@@ -218,7 +218,6 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er
//
// glob ignores file system errors
//
glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []string, err: Match_Error) {
context.allocator = allocator
@@ -261,6 +260,8 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
}
return
}
// Internal implementation of `glob`, not meant to be used by the user. Prefer `glob`.
_glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := context.allocator) -> (m: [dynamic]string, e: Match_Error) {
context.allocator = allocator
+44 -5
View File
@@ -20,6 +20,8 @@ is_slash :: proc(c: byte) -> bool {
return c == '\\' || c == '/'
}
// Splits path immediate following the last separator; separating the path into a directory and file.
// If no separator is found, `dir` will be empty and `path` set to `path`.
split :: proc(path: string) -> (dir, file: string) {
vol := volume_name(path)
i := len(path) - 1
@@ -29,10 +31,18 @@ split :: proc(path: string) -> (dir, file: string) {
return path[:i+1], path[i+1:]
}
/*
Returns leading volume name.
e.g.
"C:\foo\bar\baz" will return "C:" on Windows.
Everything else will be "".
*/
volume_name :: proc(path: string) -> string {
return path[:volume_name_len(path)]
}
// Returns the length of the volume name in bytes.
volume_name_len :: proc(path: string) -> int {
if ODIN_OS == .Windows {
if len(path) < 2 {
@@ -74,7 +84,7 @@ volume_name_len :: proc(path: string) -> int {
/*
Gets the file name and extension from a path.
i.e:
e.g.
'path/to/name.tar.gz' -> 'name.tar.gz'
'path/to/name.txt' -> 'name.txt'
'path/to/name' -> 'name'
@@ -114,7 +124,7 @@ base :: proc(path: string) -> string {
Only the last dot is considered when splitting the file extension.
See `short_stem`.
i.e:
e.g.
'name.tar.gz' -> 'name.tar'
'name.txt' -> 'name'
@@ -147,7 +157,7 @@ stem :: proc(path: string) -> string {
The first dot is used to split off the file extension, unlike `stem` which uses the last dot.
i.e:
e.g.
'name.tar.gz' -> 'name'
'name.txt' -> 'name'
@@ -170,7 +180,7 @@ short_stem :: proc(path: string) -> string {
Only the last dot is considered when splitting the file extension.
See `long_ext`.
i.e:
e.g.
'name.tar.gz' -> '.gz'
'name.txt' -> '.txt'
@@ -193,7 +203,7 @@ ext :: proc(path: string) -> string {
The first dot is used to split off the file extension, unlike `ext` which uses the last dot.
i.e:
e.g.
'name.tar.gz' -> '.tar.gz'
'name.txt' -> '.txt'
@@ -219,6 +229,21 @@ long_ext :: proc(path: string) -> string {
return ""
}
/*
Returns the shortest path name equivalent to `path` through solely lexical processing.
It applies the folliwng rules until none of them can be applied:
* Replace multiple separators with a single one
* Remove each current directory (`.`) path name element
* Remove each inner parent directory (`..`) path and the preceding paths
* Remove `..` that begin at the root of a path
* All possible separators are replaced with the OS specific separator
The return path ends in a slash only if it represents the root of a directory (`C:\` on Windows and `/` on *nix systems).
If the result of the path is an empty string, the returned path with be `"."`.
*/
clean :: proc(path: string, allocator := context.allocator) -> string {
context.allocator = allocator
@@ -299,6 +324,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
return cleaned
}
// Returns the result of replacing each forward slash `/` character in the path with the separate OS specific character.
from_slash :: proc(path: string, allocator := context.allocator) -> (new_path: string, new_allocation: bool) {
if SEPARATOR == '/' {
return path, false
@@ -306,6 +332,7 @@ from_slash :: proc(path: string, allocator := context.allocator) -> (new_path: s
return strings.replace_all(path, "/", SEPARATOR_STRING, allocator)
}
// Returns the result of replacing each OS specific separator with a forward slash `/` character.
to_slash :: proc(path: string, allocator := context.allocator) -> (new_path: string, new_allocation: bool) {
if SEPARATOR == '/' {
return path, false
@@ -320,6 +347,13 @@ Relative_Error :: enum {
Cannot_Relate,
}
/*
Returns a relative path that is lexically equivalent to the `target_path` when joined with the `base_path` with an OS specific separator.
e.g. `join(base_path, rel(base_path, target_path))` is equivalent to `target_path`
On failure, the `Relative_Error` will be state it cannot compute the necessary relative path.
*/
rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (string, Relative_Error) {
context.allocator = allocator
base_clean, target_clean := clean(base_path), clean(target_path)
@@ -398,6 +432,11 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (
return target[t0:], .None
}
/*
Returns all but the last element path, usually the path's directory. Once the final element has been removed,
`dir` calls `clean` on the path and trailing separators are removed. If the path consists purely of separators,
then `"."` is returned.
*/
dir :: proc(path: string, allocator := context.allocator) -> string {
context.allocator = allocator
vol := volume_name(path)
+2
View File
@@ -7,6 +7,7 @@ when ODIN_OS == .Darwin {
foreign import libc "system:c"
}
import "core:runtime"
import "core:strings"
SEPARATOR :: '/'
@@ -41,6 +42,7 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
join :: proc(elems: []string, allocator := context.allocator) -> string {
for e, i in elems {
if e != "" {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
p := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator)
return clean(p, allocator)
}
+15 -12
View File
@@ -1,6 +1,7 @@
package filepath
import "core:strings"
import "core:runtime"
import "core:os"
import win32 "core:sys/windows"
@@ -60,25 +61,25 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) {
}
p := win32.utf8_to_utf16(name, ta)
buf := make([dynamic]u16, 100, ta)
for {
n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
if n == 0 {
delete(buf)
return "", os.Errno(win32.GetLastError())
}
if n <= u32(len(buf)) {
return win32.utf16_to_utf8(buf[:n], ta) or_else "", os.ERROR_NONE
}
resize(&buf, len(buf)*2)
n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
if n == 0 {
return "", os.Errno(win32.GetLastError())
}
return
buf := make([]u16, n, ta)
n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
if n == 0 {
delete(buf)
return "", os.Errno(win32.GetLastError())
}
return win32.utf16_to_utf8(buf[:n], ta) or_else "", os.ERROR_NONE
}
abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
full_path, err := temp_full_path(path)
if err != 0 {
return "", false
@@ -99,6 +100,8 @@ join :: proc(elems: []string, allocator := context.allocator) -> string {
join_non_empty :: proc(elems: []string, allocator := context.allocator) -> string {
context.allocator = allocator
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
if len(elems[0]) == 2 && elems[0][1] == ':' {
i := 1
+3 -1
View File
@@ -5,6 +5,7 @@
// To manipulate operating system specific paths, use the path/filepath package
package slashpath
import "core:runtime"
import "core:strings"
// is_separator checks whether the byte is a valid separator character
@@ -150,8 +151,9 @@ join :: proc(elems: []string, allocator := context.allocator) -> string {
context.allocator = allocator
for elem, i in elems {
if elem != "" {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
s := strings.join(elems[i:], "/", context.temp_allocator)
return clean(s)
return clean(s, allocator)
}
}
return ""
+208
View File
@@ -0,0 +1,208 @@
package prof_spall
import "core:os"
import "core:time"
import "core:intrinsics"
import "core:mem"
// File Format
MANUAL_MAGIC :: u64le(0x0BADF00D)
Manual_Header :: struct #packed {
magic: u64le,
version: u64le,
timestamp_scale: f64le,
reserved: u64le,
}
Manual_Event_Type :: enum u8 {
Invalid = 0,
Begin = 3,
End = 4,
Instant = 5,
Pad_Skip = 7,
}
Begin_Event :: struct #packed {
type: Manual_Event_Type,
category: u8,
pid: u32le,
tid: u32le,
ts: f64le,
name_len: u8,
args_len: u8,
}
BEGIN_EVENT_MAX :: size_of(Begin_Event) + 255 + 255
End_Event :: struct #packed {
type: Manual_Event_Type,
pid: u32le,
tid: u32le,
ts: f64le,
}
Pad_Skip :: struct #packed {
type: Manual_Event_Type,
size: u32le,
}
// User Interface
Context :: struct {
precise_time: bool,
timestamp_scale: f64,
fd: os.Handle,
}
Buffer :: struct {
data: []u8,
head: int,
tid: u32,
pid: u32,
}
BUFFER_DEFAULT_SIZE :: 0x10_0000
context_create :: proc(filename: string) -> (ctx: Context, ok: bool) #optional_ok {
fd, err := os.open(filename, os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC, 0o600)
if err != os.ERROR_NONE {
return
}
ctx.fd = fd
freq, freq_ok := time.tsc_frequency()
ctx.precise_time = freq_ok
ctx.timestamp_scale = ((1 / f64(freq)) * 1_000_000) if freq_ok else 1
temp := [size_of(Manual_Header)]u8{}
_build_header(temp[:], ctx.timestamp_scale)
os.write(ctx.fd, temp[:])
ok = true
return
}
context_destroy :: proc(ctx: ^Context) {
if ctx == nil {
return
}
os.close(ctx.fd)
ctx^ = Context{}
}
buffer_create :: proc(data: []byte, tid: u32 = 0, pid: u32 = 0) -> (buffer: Buffer, ok: bool) #optional_ok {
assert(len(data) > 0)
buffer.data = data
buffer.tid = tid
buffer.pid = pid
buffer.head = 0
ok = true
return
}
buffer_flush :: proc(ctx: ^Context, buffer: ^Buffer) {
os.write(ctx.fd, buffer.data[:buffer.head])
buffer.head = 0
}
buffer_destroy :: proc(ctx: ^Context, buffer: ^Buffer) {
buffer_flush(ctx, buffer)
buffer^ = Buffer{}
}
@(deferred_in=_scoped_buffer_end)
SCOPED_EVENT :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) -> bool {
_buffer_begin(ctx, buffer, name, args, location)
return true
}
@(private)
_scoped_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer, _, _: string, _ := #caller_location) {
_buffer_end(ctx, buffer)
}
_trace_now :: proc "contextless" (ctx: ^Context) -> f64 {
if !ctx.precise_time {
return f64(time.tick_now()._nsec) / 1_000
}
return f64(intrinsics.read_cycle_counter())
}
_build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok {
header_size = size_of(Manual_Header)
if header_size > len(buffer) {
return 0, false
}
hdr := (^Manual_Header)(raw_data(buffer))
hdr.magic = MANUAL_MAGIC
hdr.version = 1
hdr.timestamp_scale = f64le(timestamp_scale)
hdr.reserved = 0
ok = true
return
}
_build_begin :: proc "contextless" (buffer: []u8, name: string, args: string, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok {
ev := (^Begin_Event)(raw_data(buffer))
name_len := min(len(name), 255)
args_len := min(len(args), 255)
event_size = size_of(Begin_Event) + name_len + args_len
if event_size > len(buffer) {
return 0, false
}
ev.type = .Begin
ev.pid = u32le(pid)
ev.tid = u32le(tid)
ev.ts = f64le(ts)
ev.name_len = u8(name_len)
ev.args_len = u8(args_len)
mem.copy(raw_data(buffer[size_of(Begin_Event):]), raw_data(name), name_len)
mem.copy(raw_data(buffer[size_of(Begin_Event)+name_len:]), raw_data(args), args_len)
ok = true
return
}
_build_end :: proc(buffer: []u8, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok {
ev := (^End_Event)(raw_data(buffer))
event_size = size_of(End_Event)
if event_size > len(buffer) {
return 0, false
}
ev.type = .End
ev.pid = u32le(pid)
ev.tid = u32le(tid)
ev.ts = f64le(ts)
ok = true
return
}
_buffer_begin :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) {
if buffer.head + BEGIN_EVENT_MAX > len(buffer.data) {
buffer_flush(ctx, buffer)
}
name := location.procedure if name == "" else name
buffer.head += _build_begin(buffer.data[buffer.head:], name, args, _trace_now(ctx), buffer.tid, buffer.pid)
}
_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer) {
ts := _trace_now(ctx)
if buffer.head + size_of(End_Event) > len(buffer.data) {
buffer_flush(ctx, buffer)
}
buffer.head += _build_end(buffer.data[buffer.head:], ts, buffer.tid, buffer.pid)
}
+2
View File
@@ -2,6 +2,7 @@ package reflect
import "core:runtime"
@(require_results)
iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
if val == nil || it == nil {
return
@@ -41,6 +42,7 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
return
}
@(require_results)
iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
if val == nil || it == nil {
return
+65 -42
View File
@@ -25,7 +25,8 @@ Type_Info_Array :: runtime.Type_Info_Array
Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array
Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array
Type_Info_Slice :: runtime.Type_Info_Slice
Type_Info_Tuple :: runtime.Type_Info_Tuple
Type_Info_Parameters :: runtime.Type_Info_Parameters
Type_Info_Tuple :: runtime.Type_Info_Parameters
Type_Info_Struct :: runtime.Type_Info_Struct
Type_Info_Union :: runtime.Type_Info_Union
Type_Info_Enum :: runtime.Type_Info_Enum
@@ -74,6 +75,7 @@ Type_Kind :: enum {
}
@(require_results)
type_kind :: proc(T: typeid) -> Type_Kind {
ti := type_info_of(T)
if ti != nil {
@@ -95,7 +97,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
case Type_Info_Enumerated_Array: return .Enumerated_Array
case Type_Info_Dynamic_Array: return .Dynamic_Array
case Type_Info_Slice: return .Slice
case Type_Info_Tuple: return .Tuple
case Type_Info_Parameters: return .Tuple
case Type_Info_Struct: return .Struct
case Type_Info_Union: return .Union
case Type_Info_Enum: return .Enum
@@ -113,57 +115,31 @@ type_kind :: proc(T: typeid) -> Type_Kind {
}
// TODO(bill): Better name
@(require_results)
underlying_type_kind :: proc(T: typeid) -> Type_Kind {
return type_kind(runtime.typeid_base(T))
}
// TODO(bill): Better name
@(require_results)
backing_type_kind :: proc(T: typeid) -> Type_Kind {
return type_kind(runtime.typeid_core(T))
}
type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
if info == nil { return nil }
base := info
loop: for {
#partial switch i in base.variant {
case Type_Info_Named: base = i.base
case: break loop
}
}
return base
}
type_info_core :: proc(info: ^Type_Info) -> ^Type_Info {
if info == nil { return nil }
base := info
loop: for {
#partial switch i in base.variant {
case Type_Info_Named: base = i.base
case Type_Info_Enum: base = i.base
case: break loop
}
}
return base
}
type_info_base :: runtime.type_info_base
type_info_core :: runtime.type_info_core
type_info_base_without_enum :: type_info_core
typeid_base :: proc(id: typeid) -> typeid {
ti := type_info_of(id)
ti = type_info_base(ti)
return ti.id
when !ODIN_DISALLOW_RTTI {
typeid_base :: runtime.typeid_base
typeid_core :: runtime.typeid_core
typeid_base_without_enum :: typeid_core
}
typeid_core :: proc(id: typeid) -> typeid {
ti := type_info_base_without_enum(type_info_of(id))
return ti.id
}
typeid_base_without_enum :: typeid_core
@(require_results)
any_base :: proc(v: any) -> any {
v := v
if v != nil {
@@ -171,6 +147,7 @@ any_base :: proc(v: any) -> any {
}
return v
}
@(require_results)
any_core :: proc(v: any) -> any {
v := v
if v != nil {
@@ -179,6 +156,7 @@ any_core :: proc(v: any) -> any {
return v
}
@(require_results)
typeid_elem :: proc(id: typeid) -> typeid {
ti := type_info_of(id)
if ti == nil { return nil }
@@ -208,6 +186,7 @@ typeid_elem :: proc(id: typeid) -> typeid {
}
@(require_results)
size_of_typeid :: proc(T: typeid) -> int {
if ti := type_info_of(T); ti != nil {
return ti.size
@@ -215,6 +194,7 @@ size_of_typeid :: proc(T: typeid) -> int {
return 0
}
@(require_results)
align_of_typeid :: proc(T: typeid) -> int {
if ti := type_info_of(T); ti != nil {
return ti.align
@@ -222,6 +202,7 @@ align_of_typeid :: proc(T: typeid) -> int {
return 1
}
@(require_results)
as_bytes :: proc(v: any) -> []byte {
if v != nil {
sz := size_of_typeid(v.id)
@@ -230,10 +211,12 @@ as_bytes :: proc(v: any) -> []byte {
return nil
}
@(require_results)
any_data :: #force_inline proc(v: any) -> (data: rawptr, id: typeid) {
return v.data, v.id
}
@(require_results)
is_nil :: proc(v: any) -> bool {
if v == nil {
return true
@@ -250,6 +233,7 @@ is_nil :: proc(v: any) -> bool {
return true
}
@(require_results)
length :: proc(val: any) -> int {
if val == nil { return 0 }
@@ -285,6 +269,7 @@ length :: proc(val: any) -> int {
return 0
}
@(require_results)
capacity :: proc(val: any) -> int {
if val == nil { return 0 }
@@ -311,6 +296,7 @@ capacity :: proc(val: any) -> int {
}
@(require_results)
index :: proc(val: any, i: int, loc := #caller_location) -> any {
if val == nil { return nil }
@@ -370,6 +356,7 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
return nil
}
@(require_results)
deref :: proc(val: any) -> any {
if val != nil {
ti := type_info_base(type_info_of(val.id))
@@ -399,6 +386,7 @@ Struct_Field :: struct {
is_using: bool,
}
@(require_results)
struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -413,6 +401,7 @@ struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
return
}
@(require_results)
struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -430,6 +419,7 @@ struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
return
}
@(require_results)
struct_field_value_by_name :: proc(a: any, field: string, allow_using := false) -> any {
if a == nil { return nil }
@@ -461,6 +451,7 @@ struct_field_value_by_name :: proc(a: any, field: string, allow_using := false)
@(require_results)
struct_field_names :: proc(T: typeid) -> []string {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -469,6 +460,7 @@ struct_field_names :: proc(T: typeid) -> []string {
return nil
}
@(require_results)
struct_field_types :: proc(T: typeid) -> []^Type_Info {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -478,6 +470,7 @@ struct_field_types :: proc(T: typeid) -> []^Type_Info {
}
@(require_results)
struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -486,6 +479,7 @@ struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
return nil
}
@(require_results)
struct_field_offsets :: proc(T: typeid) -> []uintptr {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -494,6 +488,7 @@ struct_field_offsets :: proc(T: typeid) -> []uintptr {
return nil
}
@(require_results)
struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -510,11 +505,13 @@ struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
@(require_results)
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag) {
value, _ = struct_tag_lookup(tag, key)
return
}
@(require_results)
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, ok: bool) {
for t := tag; t != ""; /**/ {
i := 0
@@ -573,6 +570,7 @@ struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, o
}
@(require_results)
enum_string :: proc(a: any) -> string {
if a == nil { return "" }
ti := runtime.type_info_base(type_info_of(a.id))
@@ -591,6 +589,7 @@ enum_string :: proc(a: any) -> string {
}
// Given a enum type and a value name, get the enum value.
@(require_results)
enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, ok: bool) {
ti := type_info_base(type_info_of(Enum_Type))
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -607,6 +606,7 @@ enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, o
return
}
@(require_results)
enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info_Enum_Value, ok: bool) {
ti := runtime.type_info_base(type_info_of(Enum_Type))
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -623,6 +623,7 @@ enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info
}
@(require_results)
enum_field_names :: proc(Enum_Type: typeid) -> []string {
ti := runtime.type_info_base(type_info_of(Enum_Type))
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -630,6 +631,7 @@ enum_field_names :: proc(Enum_Type: typeid) -> []string {
}
return nil
}
@(require_results)
enum_field_values :: proc(Enum_Type: typeid) -> []Type_Info_Enum_Value {
ti := runtime.type_info_base(type_info_of(Enum_Type))
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -643,6 +645,7 @@ Enum_Field :: struct {
value: Type_Info_Enum_Value,
}
@(require_results)
enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
ti := runtime.type_info_base(type_info_of(Enum_Type))
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -653,15 +656,18 @@ enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
@(require_results)
union_variant_type_info :: proc(a: any) -> ^Type_Info {
id := union_variant_typeid(a)
return type_info_of(id)
}
@(require_results)
type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool {
return len(info.variants) == 1 && is_pointer(info.variants[0])
}
@(require_results)
union_variant_typeid :: proc(a: any) -> typeid {
if a == nil { return nil }
@@ -690,9 +696,10 @@ union_variant_typeid :: proc(a: any) -> typeid {
case: unimplemented()
}
if a.data != nil && tag != 0 {
i := tag if info.no_nil else tag-1
return info.variants[i].id
if info.no_nil {
return info.variants[tag].id
} else if tag != 0 {
return info.variants[tag-1].id
}
return nil
@@ -700,6 +707,7 @@ union_variant_typeid :: proc(a: any) -> typeid {
panic("expected a union to reflect.union_variant_typeid")
}
@(require_results)
get_union_variant_raw_tag :: proc(a: any) -> i64 {
if a == nil { return -1 }
@@ -730,6 +738,7 @@ get_union_variant_raw_tag :: proc(a: any) -> i64 {
panic("expected a union to reflect.get_union_variant_raw_tag")
}
@(require_results)
get_union_variant :: proc(a: any) -> any {
if a == nil {
return nil
@@ -741,6 +750,7 @@ get_union_variant :: proc(a: any) -> any {
return any{a.data, id}
}
@(require_results)
get_union_as_ptr_variants :: proc(val: ^$T) -> (res: intrinsics.type_convert_variants_to_pointers(T)) where intrinsics.type_is_union(T) {
ptr := rawptr(val)
tag := get_union_variant_raw_tag(val^)
@@ -881,6 +891,7 @@ set_union_value :: proc(dst: any, value: any) -> bool {
@(require_results)
as_bool :: proc(a: any) -> (value: bool, valid: bool) {
if a == nil { return }
a := a
@@ -903,6 +914,7 @@ as_bool :: proc(a: any) -> (value: bool, valid: bool) {
return
}
@(require_results)
as_int :: proc(a: any) -> (value: int, valid: bool) {
v: i64
v, valid = as_i64(a)
@@ -910,6 +922,7 @@ as_int :: proc(a: any) -> (value: int, valid: bool) {
return
}
@(require_results)
as_uint :: proc(a: any) -> (value: uint, valid: bool) {
v: u64
v, valid = as_u64(a)
@@ -917,6 +930,7 @@ as_uint :: proc(a: any) -> (value: uint, valid: bool) {
return
}
@(require_results)
as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
if a == nil { return }
a := a
@@ -1024,6 +1038,7 @@ as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
return
}
@(require_results)
as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
if a == nil { return }
a := a
@@ -1133,6 +1148,7 @@ as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
}
@(require_results)
as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
if a == nil { return }
a := a
@@ -1239,6 +1255,7 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
}
@(require_results)
as_string :: proc(a: any) -> (value: string, valid: bool) {
if a == nil { return }
a := a
@@ -1258,6 +1275,7 @@ as_string :: proc(a: any) -> (value: string, valid: bool) {
return
}
@(require_results)
relative_pointer_to_absolute :: proc(a: any) -> rawptr {
if a == nil { return nil }
a := a
@@ -1272,6 +1290,7 @@ relative_pointer_to_absolute :: proc(a: any) -> rawptr {
}
@(require_results)
relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
if ptr^ == 0 {
@@ -1314,6 +1333,7 @@ relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid)
@(require_results)
as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
if a == nil { return }
a := a
@@ -1341,6 +1361,7 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
}
@(require_results)
as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
if a == nil { return }
a := a
@@ -1377,9 +1398,11 @@ ne :: not_equal
DEFAULT_EQUAL_MAX_RECURSION_LEVEL :: 32
@(require_results)
not_equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
return !equal(a, b, including_indirect_array_recursion, recursion_level)
}
@(require_results)
equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
if a == nil && b == nil {
return true
@@ -1416,7 +1439,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
switch v in t.variant {
case Type_Info_Named:
unreachable()
case Type_Info_Tuple:
case Type_Info_Parameters:
unreachable()
case Type_Info_Any:
if !including_indirect_array_recursion {
+45 -9
View File
@@ -3,17 +3,16 @@ package reflect
import "core:io"
import "core:strings"
@(require_results)
are_types_identical :: proc(a, b: ^Type_Info) -> bool {
if a == b {
return true
}
if (a == nil && b != nil) ||
(a != nil && b == nil) {
if a == nil || b == nil {
return false
}
switch {
case a.size != b.size, a.align != b.align:
return false
@@ -102,8 +101,8 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
y := b.variant.(Type_Info_Slice) or_return
return are_types_identical(x.elem, y.elem)
case Type_Info_Tuple:
y := b.variant.(Type_Info_Tuple) or_return
case Type_Info_Parameters:
y := b.variant.(Type_Info_Parameters) or_return
if len(x.types) != len(y.types) { return false }
for _, i in x.types {
xt, yt := x.types[i], y.types[i]
@@ -180,6 +179,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
return false
}
@(require_results)
is_signed :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
#partial switch i in type_info_base(info).variant {
@@ -188,6 +188,7 @@ is_signed :: proc(info: ^Type_Info) -> bool {
}
return false
}
@(require_results)
is_unsigned :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
#partial switch i in type_info_base(info).variant {
@@ -197,6 +198,7 @@ is_unsigned :: proc(info: ^Type_Info) -> bool {
return false
}
@(require_results)
is_byte :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
#partial switch i in type_info_base(info).variant {
@@ -206,66 +208,79 @@ is_byte :: proc(info: ^Type_Info) -> bool {
}
@(require_results)
is_integer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Integer)
return ok
}
@(require_results)
is_rune :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Rune)
return ok
}
@(require_results)
is_float :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Float)
return ok
}
@(require_results)
is_complex :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Complex)
return ok
}
@(require_results)
is_quaternion :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Quaternion)
return ok
}
@(require_results)
is_any :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Any)
return ok
}
@(require_results)
is_string :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_String)
return ok
}
@(require_results)
is_cstring :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
v, ok := type_info_base(info).variant.(Type_Info_String)
return ok && v.is_cstring
}
@(require_results)
is_boolean :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Boolean)
return ok
}
@(require_results)
is_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Pointer)
return ok
}
@(require_results)
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
}
@(require_results)
is_soa_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer)
return ok
}
@(require_results)
is_pointer_internally :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
#partial switch v in info.variant {
@@ -277,76 +292,97 @@ is_pointer_internally :: proc(info: ^Type_Info) -> bool {
}
return false
}
@(require_results)
is_procedure :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Procedure)
return ok
}
@(require_results)
is_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Array)
return ok
}
@(require_results)
is_enumerated_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Enumerated_Array)
return ok
}
@(require_results)
is_dynamic_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Dynamic_Array)
return ok
}
@(require_results)
is_dynamic_map :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Map)
return ok
}
@(require_results)
is_bit_set :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Bit_Set)
return ok
}
@(require_results)
is_slice :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Slice)
return ok
}
is_tuple :: proc(info: ^Type_Info) -> bool {
@(require_results)
is_parameters :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Tuple)
_, ok := type_info_base(info).variant.(Type_Info_Parameters)
return ok
}
@(require_results, deprecated="prefer is_parameters")
is_tuple :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Parameters)
return ok
}
@(require_results)
is_struct :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
s, ok := type_info_base(info).variant.(Type_Info_Struct)
return ok && !s.is_raw_union
}
@(require_results)
is_raw_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
s, ok := type_info_base(info).variant.(Type_Info_Struct)
return ok && s.is_raw_union
}
@(require_results)
is_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Union)
return ok
}
@(require_results)
is_enum :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Enum)
return ok
}
@(require_results)
is_simd_vector :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Simd_Vector)
return ok
}
@(require_results)
is_relative_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Relative_Pointer)
return ok
}
@(require_results)
is_relative_slice :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Relative_Slice)
@@ -460,7 +496,7 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
if info.params == nil {
io.write_string(w, "()", &n) or_return
} else {
t := info.params.variant.(Type_Info_Tuple)
t := info.params.variant.(Type_Info_Parameters)
io.write_string(w, "(", &n) or_return
for t, i in t.types {
if i > 0 {
@@ -474,7 +510,7 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
io.write_string(w, " -> ", &n) or_return
write_type(w, info.results, &n) or_return
}
case Type_Info_Tuple:
case Type_Info_Parameters:
count := len(info.names)
if count != 1 {
io.write_string(w, "(", &n) or_return
+14 -9
View File
@@ -83,8 +83,8 @@ Type_Info_Multi_Pointer :: struct {
elem: ^Type_Info,
}
Type_Info_Procedure :: struct {
params: ^Type_Info, // Type_Info_Tuple
results: ^Type_Info, // Type_Info_Tuple
params: ^Type_Info, // Type_Info_Parameters
results: ^Type_Info, // Type_Info_Parameters
variadic: bool,
convention: Calling_Convention,
}
@@ -104,10 +104,12 @@ Type_Info_Enumerated_Array :: struct {
}
Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int}
Type_Info_Slice :: struct {elem: ^Type_Info, elem_size: int}
Type_Info_Tuple :: struct { // Only used for procedures parameters and results
Type_Info_Parameters :: struct { // Only used for procedures parameters and results
types: []^Type_Info,
names: []string,
}
Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually
Type_Info_Struct :: struct {
types: []^Type_Info,
@@ -208,7 +210,7 @@ Type_Info :: struct {
Type_Info_Enumerated_Array,
Type_Info_Dynamic_Array,
Type_Info_Slice,
Type_Info_Tuple,
Type_Info_Parameters,
Type_Info_Struct,
Type_Info_Union,
Type_Info_Enum,
@@ -329,6 +331,12 @@ Allocator :: struct {
data: rawptr,
}
Byte :: 1
Kilobyte :: 1024 * Byte
Megabyte :: 1024 * Kilobyte
Gigabyte :: 1024 * Megabyte
Terabyte :: 1024 * Gigabyte
// Logging stuff
Logger_Level :: enum uint {
@@ -499,11 +507,8 @@ Odin_Endian_Type :: type_of(ODIN_ENDIAN)
foreign {
@(link_name="__$startup_runtime")
_startup_runtime :: proc "odin" () ---
}
@(link_name="__$cleanup_runtime")
_cleanup_runtime :: proc() {
default_temp_allocator_destroy(&global_default_temp_allocator_data)
@(link_name="__$cleanup_runtime")
_cleanup_runtime :: proc "odin" () ---
}
_cleanup_runtime_contextless :: proc "contextless" () {
+6 -9
View File
@@ -231,13 +231,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
return
}
@(builtin)
make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error {
make_map_expr_error_loc(loc, capacity)
context.allocator = allocator
m: T
reserve_map(&m, capacity, loc)
return m
err = reserve_map(&m, capacity, loc)
return
}
@(builtin)
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) #optional_allocator_error {
@@ -276,10 +275,8 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
}
@builtin
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
if m != nil {
__dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc)
}
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) -> Allocator_Error {
return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) if m != nil else nil
}
/*
@@ -615,7 +612,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
old_size := a.cap * size_of(E)
new_size := new_cap * size_of(E)
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), a.allocator, loc)
if err != nil {
return
}
+2 -2
View File
@@ -150,14 +150,14 @@ make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.
}
@builtin
make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, auto_cast length: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
context.allocator = allocator
resize_soa(&array, length, loc)
return
}
@builtin
make_soa_dynamic_array_len_cap :: proc($T: typeid/#soa[dynamic]$E, auto_cast length, capacity: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
make_soa_dynamic_array_len_cap :: proc($T: typeid/#soa[dynamic]$E, #any_int length, capacity: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
context.allocator = allocator
if reserve_soa(&array, capacity, loc) {
resize_soa(&array, length, loc)
+304
View File
@@ -0,0 +1,304 @@
package runtime
import "core:intrinsics"
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE)
Memory_Block :: struct {
prev: ^Memory_Block,
allocator: Allocator,
base: [^]byte,
used: uint,
capacity: uint,
}
Arena :: struct {
backing_allocator: Allocator,
curr_block: ^Memory_Block,
total_used: uint,
total_capacity: uint,
minimum_block_size: uint,
temp_count: uint,
}
@(private, require_results)
safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
z, did_overflow := intrinsics.overflow_add(x, y)
return z, !did_overflow
}
@(require_results)
memory_block_alloc :: proc(allocator: Allocator, capacity: uint, loc := #caller_location) -> (block: ^Memory_Block, err: Allocator_Error) {
total_size := uint(capacity + size_of(Memory_Block))
base_offset := uintptr(size_of(Memory_Block))
min_alignment: int = max(16, align_of(Memory_Block))
data := mem_alloc(int(total_size), min_alignment, allocator, loc) or_return
block = (^Memory_Block)(raw_data(data))
end := uintptr(raw_data(data)[len(data):])
block.allocator = allocator
block.base = ([^]byte)(uintptr(block) + base_offset)
block.capacity = uint(end - uintptr(block.base))
// Should be zeroed
assert(block.used == 0)
assert(block.prev == nil)
return
}
memory_block_dealloc :: proc(block_to_free: ^Memory_Block, loc := #caller_location) {
if block_to_free != nil {
allocator := block_to_free.allocator
mem_free(block_to_free, allocator, loc)
}
}
@(require_results)
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint) -> (data: []byte, err: Allocator_Error) {
calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
alignment_offset := uint(0)
ptr := uintptr(block.base[block.used:])
mask := alignment-1
if ptr & mask != 0 {
alignment_offset = uint(alignment - (ptr & mask))
}
return alignment_offset
}
if block == nil {
return nil, .Out_Of_Memory
}
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
size, size_ok := safe_add(min_size, alignment_offset)
if !size_ok {
err = .Out_Of_Memory
return
}
if to_be_used, ok := safe_add(block.used, size); !ok || to_be_used > block.capacity {
err = .Out_Of_Memory
return
}
data = block.base[block.used+alignment_offset:][:min_size]
block.used += size
return
}
@(require_results)
arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
align_forward_uint :: proc "contextless" (ptr, align: uint) -> uint {
p := ptr
modulo := p & (align-1)
if modulo != 0 {
p += align - modulo
}
return p
}
assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc)
size := size
if size == 0 {
return
}
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.capacity {
size = align_forward_uint(size, alignment)
if arena.minimum_block_size == 0 {
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
}
block_size := max(size, arena.minimum_block_size)
if arena.backing_allocator.procedure == nil {
arena.backing_allocator = default_allocator()
}
new_block := memory_block_alloc(arena.backing_allocator, block_size, loc) or_return
new_block.prev = arena.curr_block
arena.curr_block = new_block
arena.total_capacity += new_block.capacity
}
prev_used := arena.curr_block.used
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
arena.total_used += arena.curr_block.used - prev_used
return
}
// `arena_init` will initialize the arena with a usuable block.
// This procedure is not necessary to use the Arena as the default zero as `arena_alloc` will set things up if necessary
@(require_results)
arena_init :: proc(arena: ^Arena, size: uint, backing_allocator: Allocator, loc := #caller_location) -> Allocator_Error {
arena^ = {}
arena.backing_allocator = backing_allocator
arena.minimum_block_size = max(size, 1<<12) // minimum block size of 4 KiB
new_block := memory_block_alloc(arena.backing_allocator, arena.minimum_block_size, loc) or_return
arena.curr_block = new_block
arena.total_capacity += new_block.capacity
return nil
}
arena_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) {
if free_block := arena.curr_block; free_block != nil {
arena.curr_block = free_block.prev
arena.total_capacity -= free_block.capacity
memory_block_dealloc(free_block, loc)
}
}
// `arena_free_all` will free all but the first memory block, and then reset the memory block
arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
for arena.curr_block != nil && arena.curr_block.prev != nil {
arena_free_last_memory_block(arena, loc)
}
if arena.curr_block != nil {
intrinsics.mem_zero(arena.curr_block.base, arena.curr_block.used)
arena.curr_block.used = 0
}
arena.total_used = 0
}
arena_destroy :: proc(arena: ^Arena, loc := #caller_location) {
for arena.curr_block != nil {
free_block := arena.curr_block
arena.curr_block = free_block.prev
arena.total_capacity -= free_block.capacity
memory_block_dealloc(free_block, loc)
}
arena.total_used = 0
arena.total_capacity = 0
}
arena_allocator :: proc(arena: ^Arena) -> Allocator {
return Allocator{arena_allocator_proc, arena}
}
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
arena := (^Arena)(allocator_data)
size, alignment := uint(size), uint(alignment)
old_size := uint(old_size)
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
return arena_alloc(arena, size, alignment, location)
case .Free:
err = .Mode_Not_Implemented
case .Free_All:
arena_free_all(arena, location)
case .Resize:
old_data := ([^]byte)(old_memory)
switch {
case old_data == nil:
return arena_alloc(arena, size, alignment, location)
case size == old_size:
// return old memory
data = old_data[:size]
return
case size == 0:
err = .Mode_Not_Implemented
return
case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
// shrink data in-place
data = old_data[:size]
return
}
new_memory := arena_alloc(arena, size, alignment, location) or_return
if new_memory == nil {
return
}
copy(new_memory, old_data[:old_size])
return new_memory, nil
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
}
case .Query_Info:
err = .Mode_Not_Implemented
}
return
}
Arena_Temp :: struct {
arena: ^Arena,
block: ^Memory_Block,
used: uint,
}
@(require_results)
arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
assert(arena != nil, "nil arena", loc)
temp.arena = arena
temp.block = arena.curr_block
if arena.curr_block != nil {
temp.used = arena.curr_block.used
}
arena.temp_count += 1
return
}
arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
if temp.arena == nil {
assert(temp.block == nil)
assert(temp.used == 0)
return
}
arena := temp.arena
if temp.block != nil {
memory_block_found := false
for block := arena.curr_block; block != nil; block = block.prev {
if block == temp.block {
memory_block_found = true
break
}
}
if !memory_block_found {
assert(arena.curr_block == temp.block, "memory block stored within Arena_Temp not owned by Arena", loc)
}
for arena.curr_block != temp.block {
arena_free_last_memory_block(arena)
}
if block := arena.curr_block; block != nil {
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
amount_to_zero := min(block.used-temp.used, block.capacity-block.used)
intrinsics.mem_zero(block.base[temp.used:], amount_to_zero)
block.used = temp.used
}
}
assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
arena.temp_count -= 1
}
// Ignore the use of a `arena_temp_begin` entirely
arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
arena.temp_count -= 1
}
arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) {
assert(arena.temp_count == 0, "Arena_Temp not been ended", loc)
}
+43 -164
View File
@@ -1,159 +1,38 @@
package runtime
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 4 * Megabyte)
when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
Default_Temp_Allocator :: struct {}
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {}
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {}
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {}
default_temp_allocator_proc :: nil_allocator_proc
@(require_results)
default_temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: Arena_Temp) {
return
}
default_temp_allocator_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
}
} else {
Default_Temp_Allocator :: struct {
data: []byte,
curr_offset: int,
prev_allocation: rawptr,
backup_allocator: Allocator,
leaked_allocations: [dynamic][]byte,
arena: Arena,
}
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator)
s.curr_offset = 0
s.prev_allocation = nil
s.backup_allocator = backup_allocator
s.leaked_allocations.allocator = backup_allocator
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {
_ = arena_init(&s.arena, uint(size), backing_allocator)
}
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
if s == nil {
return
if s != nil {
arena_destroy(&s.arena)
s^ = {}
}
for ptr in s.leaked_allocations {
free(raw_data(ptr), s.backup_allocator)
}
delete(s.leaked_allocations)
delete(s.data, s.backup_allocator)
s^ = {}
}
@(private)
default_temp_allocator_alloc :: proc(s: ^Default_Temp_Allocator, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
size := size
size = align_forward_int(size, alignment)
switch {
case s.curr_offset+size <= len(s.data):
start := uintptr(raw_data(s.data))
ptr := start + uintptr(s.curr_offset)
ptr = align_forward_uintptr(ptr, uintptr(alignment))
mem_zero(rawptr(ptr), size)
s.prev_allocation = rawptr(ptr)
offset := int(ptr - start)
s.curr_offset = offset + size
return byte_slice(rawptr(ptr), size), .None
case size <= len(s.data):
start := uintptr(raw_data(s.data))
ptr := align_forward_uintptr(start, uintptr(alignment))
mem_zero(rawptr(ptr), size)
s.prev_allocation = rawptr(ptr)
offset := int(ptr - start)
s.curr_offset = offset + size
return byte_slice(rawptr(ptr), size), .None
}
a := s.backup_allocator
if a.procedure == nil {
a = context.allocator
s.backup_allocator = a
}
data, err := mem_alloc_bytes(size, alignment, a, loc)
if err != nil {
return data, err
}
if s.leaked_allocations == nil {
s.leaked_allocations = make([dynamic][]byte, a)
}
append(&s.leaked_allocations, data)
// TODO(bill): Should leaks be notified about?
if logger := context.logger; logger.lowest_level <= .Warning {
if logger.procedure != nil {
logger.procedure(logger.data, .Warning, "default temp allocator resorted to backup_allocator" , logger.options, loc)
}
}
return data, .None
}
@(private)
default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error {
if old_memory == nil {
return .None
}
start := uintptr(raw_data(s.data))
end := start + uintptr(len(s.data))
old_ptr := uintptr(old_memory)
if s.prev_allocation == old_memory {
s.curr_offset = int(uintptr(s.prev_allocation) - start)
s.prev_allocation = nil
return .None
}
if start <= old_ptr && old_ptr < end {
// NOTE(bill): Cannot free this pointer but it is valid
return .None
}
if len(s.leaked_allocations) != 0 {
for data, i in s.leaked_allocations {
ptr := raw_data(data)
if ptr == old_memory {
free(ptr, s.backup_allocator)
ordered_remove(&s.leaked_allocations, i)
return .None
}
}
}
return .Invalid_Pointer
// panic("invalid pointer passed to default_temp_allocator");
}
@(private)
default_temp_allocator_free_all :: proc(s: ^Default_Temp_Allocator, loc := #caller_location) {
s.curr_offset = 0
s.prev_allocation = nil
for data in s.leaked_allocations {
free(raw_data(data), s.backup_allocator)
}
clear(&s.leaked_allocations)
}
@(private)
default_temp_allocator_resize :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, old_size, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
begin := uintptr(raw_data(s.data))
end := begin + uintptr(len(s.data))
old_ptr := uintptr(old_memory)
if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 {
if old_ptr+uintptr(size) < end {
s.curr_offset = int(old_ptr-begin)+size
return byte_slice(old_memory, size), .None
}
}
data, err := default_temp_allocator_alloc(s, size, alignment, loc)
if err == .None {
copy(data, byte_slice(old_memory, old_size))
err = default_temp_allocator_free(s, old_memory, loc)
}
return data, err
}
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
@@ -161,40 +40,40 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
s := (^Default_Temp_Allocator)(allocator_data)
return arena_allocator_proc(&s.arena, mode, size, alignment, old_memory, old_size, loc)
}
if s.data == nil {
default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator())
@(require_results)
default_temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: Arena_Temp) {
if context.temp_allocator.data == &global_default_temp_allocator_data {
temp = arena_temp_begin(&global_default_temp_allocator_data.arena, loc)
}
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
data, err = default_temp_allocator_alloc(s, size, alignment, loc)
case .Free:
err = default_temp_allocator_free(s, old_memory, loc)
case .Free_All:
default_temp_allocator_free_all(s, loc)
case .Resize:
data, err = default_temp_allocator_resize(s, old_memory, old_size, size, alignment, loc)
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
}
case .Query_Info:
return nil, .Mode_Not_Implemented
}
return
}
default_temp_allocator_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
arena_temp_end(temp, loc)
}
}
@(deferred_out=default_temp_allocator_temp_end)
DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD :: #force_inline proc(ignore := false, loc := #caller_location) -> (Arena_Temp, Source_Code_Location) {
if ignore {
return {}, loc
} else {
return default_temp_allocator_temp_begin(loc), loc
}
}
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
return Allocator{
procedure = default_temp_allocator_proc,
data = allocator,
data = allocator,
}
}
}
@(fini, private)
_destroy_temp_allocator_fini :: proc() {
default_temp_allocator_destroy(&global_default_temp_allocator_data)
}
+11 -10
View File
@@ -184,32 +184,33 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
return
}
mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
if allocator.procedure == nil {
return nil, nil
}
if new_size == 0 {
if ptr != nil {
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
return nil, err
_, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
return
}
return nil, nil
return
} else if ptr == nil {
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
return ([^]byte)(ptr)[:old_size], nil
data = ([^]byte)(ptr)[:old_size]
return
}
data, err := allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
if err == .Mode_Not_Implemented {
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
if err != nil {
return data, err
return
}
copy(data, ([^]byte)(ptr)[:old_size])
_, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
}
return data, err
return
}
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
@@ -223,7 +224,7 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
when size_of(uint) == 8 {
if word_length := length >> 3; word_length != 0 {
for i in 0..<word_length {
for _ in 0..<word_length {
if intrinsics.unaligned_load((^u64)(a)) != intrinsics.unaligned_load((^u64)(b)) {
return false
}
@@ -254,7 +255,7 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
return true
} else {
if word_length := length >> 2; word_length != 0 {
for i in 0..<word_length {
for _ in 0..<word_length {
if intrinsics.unaligned_load((^u32)(a)) != intrinsics.unaligned_load((^u32)(b)) {
return false
}
+89 -21
View File
@@ -2,6 +2,76 @@ package runtime
_INTEGER_DIGITS :: "0123456789abcdefghijklmnopqrstuvwxyz"
@(private="file")
_INTEGER_DIGITS_VAR := _INTEGER_DIGITS
when !ODIN_DISALLOW_RTTI {
print_any_single :: proc(arg: any) {
x := arg
if loc, ok := x.(Source_Code_Location); ok {
print_caller_location(loc)
return
}
x.id = typeid_base(x.id)
switch v in x {
case typeid: print_typeid(v)
case ^Type_Info: print_type(v)
case string: print_string(v)
case cstring: print_string(string(v))
case []byte: print_string(string(v))
case rune: print_rune(v)
case u8: print_u64(u64(v))
case u16: print_u64(u64(v))
case u16le: print_u64(u64(v))
case u16be: print_u64(u64(v))
case u32: print_u64(u64(v))
case u32le: print_u64(u64(v))
case u32be: print_u64(u64(v))
case u64: print_u64(u64(v))
case u64le: print_u64(u64(v))
case u64be: print_u64(u64(v))
case i8: print_i64(i64(v))
case i16: print_i64(i64(v))
case i16le: print_i64(i64(v))
case i16be: print_i64(i64(v))
case i32: print_i64(i64(v))
case i32le: print_i64(i64(v))
case i32be: print_i64(i64(v))
case i64: print_i64(i64(v))
case i64le: print_i64(i64(v))
case i64be: print_i64(i64(v))
case int: print_int(v)
case uint: print_uint(v)
case uintptr: print_uintptr(v)
case:
ti := type_info_of(x.id)
#partial switch v in ti.variant {
case Type_Info_Pointer:
print_uintptr((^uintptr)(x.data)^)
return
}
print_string("<invalid-value>")
}
}
println_any :: proc(args: ..any) {
loop: for arg, i in args {
if i != 0 {
print_string(" ")
}
print_any_single(arg)
}
print_string("\n")
}
}
encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
r := c
@@ -38,14 +108,14 @@ encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
return buf, 4
}
print_string :: proc "contextless" (str: string) -> (int, _OS_Errno) {
return os_write(transmute([]byte)str)
print_string :: proc "contextless" (str: string) -> (n: int) {
n, _ = os_write(transmute([]byte)str)
return
}
print_strings :: proc "contextless" (args: ..string) -> (n: int, err: _OS_Errno) {
print_strings :: proc "contextless" (args: ..string) -> (n: int) {
for str in args {
m: int
m, err = os_write(transmute([]byte)str)
m, err := os_write(transmute([]byte)str)
n += m
if err != 0 {
break
@@ -54,8 +124,9 @@ print_strings :: proc "contextless" (args: ..string) -> (n: int, err: _OS_Errno)
return
}
print_byte :: proc "contextless" (b: byte) -> (int, _OS_Errno) {
return os_write([]byte{b})
print_byte :: proc "contextless" (b: byte) -> (n: int) {
n, _ = os_write([]byte{b})
return
}
print_encoded_rune :: proc "contextless" (r: rune) {
@@ -74,11 +145,10 @@ print_encoded_rune :: proc "contextless" (r: rune) {
if r <= 0 {
print_string("\\x00")
} else if r < 32 {
digits := _INTEGER_DIGITS
n0, n1 := u8(r) >> 4, u8(r) & 0xf
print_string("\\x")
print_byte(digits[n0])
print_byte(digits[n1])
print_byte(_INTEGER_DIGITS_VAR[n0])
print_byte(_INTEGER_DIGITS_VAR[n1])
} else {
print_rune(r)
}
@@ -86,7 +156,7 @@ print_encoded_rune :: proc "contextless" (r: rune) {
print_byte('\'')
}
print_rune :: proc "contextless" (r: rune) -> (int, _OS_Errno) #no_bounds_check {
print_rune :: proc "contextless" (r: rune) -> int #no_bounds_check {
RUNE_SELF :: 0x80
if r < RUNE_SELF {
@@ -94,29 +164,27 @@ print_rune :: proc "contextless" (r: rune) -> (int, _OS_Errno) #no_bounds_check
}
b, n := encode_rune(r)
return os_write(b[:n])
m, _ := os_write(b[:n])
return m
}
print_u64 :: proc "contextless" (x: u64) #no_bounds_check {
digits := _INTEGER_DIGITS
a: [129]byte
i := len(a)
b := u64(10)
u := x
for u >= b {
i -= 1; a[i] = digits[u % b]
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
u /= b
}
i -= 1; a[i] = digits[u % b]
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
os_write(a[i:])
}
print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
digits := _INTEGER_DIGITS
b :: i64(10)
u := x
@@ -126,10 +194,10 @@ print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
a: [129]byte
i := len(a)
for u >= b {
i -= 1; a[i] = digits[u % b]
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
u /= b
}
i -= 1; a[i] = digits[u % b]
i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
if neg {
i -= 1; a[i] = '-'
}
@@ -236,7 +304,7 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
if info.params == nil {
print_string("()")
} else {
t := info.params.variant.(Type_Info_Tuple)
t := info.params.variant.(Type_Info_Parameters)
print_byte('(')
for t, i in t.types {
if i > 0 { print_string(", ") }
@@ -248,7 +316,7 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
print_string(" -> ")
print_type(info.results)
}
case Type_Info_Tuple:
case Type_Info_Parameters:
count := len(info.names)
if count != 1 { print_byte('(') }
for name, i in info.names {
+1 -1
View File
@@ -37,7 +37,7 @@ Map_Entry_Info :: struct($Key, $Value: typeid) {
}
map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V), err: runtime.Allocator) {
map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V), err: runtime.Allocator_Error) {
entries = make(type_of(entries), len(m), allocator) or_return
i := 0
for key, value in m {
+17 -15
View File
@@ -73,24 +73,26 @@ ptr_rotate :: proc(left: int, mid: ^$T, right: int) {
left, mid, right := left, mid, right
// TODO(bill): Optimization with a buffer for smaller ranges
if left >= right {
for {
ptr_swap_non_overlapping(ptr_sub(mid, right), mid, right)
mid = ptr_sub(mid, right)
for left > 0 && right > 0 {
if left >= right {
for {
ptr_swap_non_overlapping(ptr_sub(mid, right), mid, right * size_of(T))
mid = ptr_sub(mid, right)
left -= right
if left < right {
break
left -= right
if left < right {
break
}
}
}
} else {
for {
ptr_swap_non_overlapping(ptr_sub(mid, left), mid, left)
mid = ptr_add(mid, left)
} else {
for {
ptr_swap_non_overlapping(ptr_sub(mid, left), mid, left * size_of(T))
mid = ptr_add(mid, left)
right -= left
if right < left {
break
right -= left
if right < left {
break
}
}
}
}
+6 -5
View File
@@ -77,8 +77,7 @@ swap_between :: proc(a, b: $T/[]$E) {
reverse :: proc(array: $T/[]$E) {
n := len(array)/2
for i in 0..<n {
a, b := i, len(array)-i-1
array[a], array[b] = array[b], array[a]
swap(array, i, len(array)-i-1)
}
}
@@ -218,8 +217,10 @@ rotate_left :: proc(array: $T/[]$E, mid: int) {
n := len(array)
m := mid %% n
k := n - m
p := raw_data(array)
ptr_rotate(mid, ptr_add(p, mid), k)
// FIXME: (ap29600) this cast is a temporary fix for the compiler not matching
// [^T] with $P/^$T
p := cast(^E)raw_data(array)
ptr_rotate(m, ptr_add(p, m), k)
}
rotate_right :: proc(array: $T/[]$E, k: int) {
rotate_left(array, -k)
@@ -515,4 +516,4 @@ dot_product :: proc(a, b: $S/[]$T) -> (r: T, ok: bool)
enumerated_array :: proc(ptr: ^$T) -> []intrinsics.type_elem_type(T)
where intrinsics.type_is_enumerated_array(T) {
return ([^]intrinsics.type_elem_type(T))(ptr)[:len(T)]
}
}
-36
View File
@@ -1,36 +0,0 @@
package sort
import "core:intrinsics"
import "core:runtime"
import "core:slice"
_ :: runtime
_ :: slice
map_entries_by_key :: proc(m: ^$M/map[$K]$V, loc := #caller_location) where intrinsics.type_is_ordered(K) {
Entry :: struct {
hash: uintptr,
next: int,
key: K,
value: V,
}
header := runtime.__get_map_header(m)
entries := (^[dynamic]Entry)(&header.m.entries)
slice.sort_by_key(entries[:], proc(e: Entry) -> K { return e.key })
runtime.__dynamic_map_reset_entries(header, loc)
}
map_entries_by_value :: proc(m: ^$M/map[$K]$V, loc := #caller_location) where intrinsics.type_is_ordered(V) {
Entry :: struct {
hash: uintptr,
next: int,
key: K,
value: V,
}
header := runtime.__get_map_header(m)
entries := (^[dynamic]Entry)(&header.m.entries)
slice.sort_by_key(entries[:], proc(e: Entry) -> V { return e.value })
runtime.__dynamic_map_reset_entries(header, loc)
}
+121 -21
View File
@@ -214,25 +214,128 @@ shift_right :: proc(a: ^Decimal, k: uint) {
trim(a)
}
shift_left :: proc(a: ^Decimal, k: uint) {
// NOTE(bill): used to determine buffer size required for the decimal from the binary shift
// 'k' means `1<<k` == `2^k` which equates to roundup(k*log10(2)) digits required
log10_2 :: 0.301029995663981195213738894724493026768189881462108541310
capacity := int(f64(k)*log10_2 + 1)
import "core:runtime"
println :: proc(args: ..any) {
for arg, i in args {
if i != 0 {
runtime.print_string(" ")
}
switch v in arg {
case string: runtime.print_string(v)
case rune: runtime.print_rune(v)
case int: runtime.print_int(v)
case uint: runtime.print_uint(v)
case u8: runtime.print_u64(u64(v))
case u16: runtime.print_u64(u64(v))
case u32: runtime.print_u64(u64(v))
case u64: runtime.print_u64(v)
case i8: runtime.print_i64(i64(v))
case i16: runtime.print_i64(i64(v))
case i32: runtime.print_i64(i64(v))
case i64: runtime.print_i64(v)
case uintptr: runtime.print_uintptr(v)
case bool: runtime.print_string("true" if v else "false")
}
}
runtime.print_string("\n")
}
r := a.count // read index
w := a.count+capacity // write index
@(private="file")
_shift_left_offsets := [?]struct{delta: int, cutoff: string}{
{ 0, ""},
{ 1, "5"},
{ 1, "25"},
{ 1, "125"},
{ 2, "625"},
{ 2, "3125"},
{ 2, "15625"},
{ 3, "78125"},
{ 3, "390625"},
{ 3, "1953125"},
{ 4, "9765625"},
{ 4, "48828125"},
{ 4, "244140625"},
{ 4, "1220703125"},
{ 5, "6103515625"},
{ 5, "30517578125"},
{ 5, "152587890625"},
{ 6, "762939453125"},
{ 6, "3814697265625"},
{ 6, "19073486328125"},
{ 7, "95367431640625"},
{ 7, "476837158203125"},
{ 7, "2384185791015625"},
{ 7, "11920928955078125"},
{ 8, "59604644775390625"},
{ 8, "298023223876953125"},
{ 8, "1490116119384765625"},
{ 9, "7450580596923828125"},
{ 9, "37252902984619140625"},
{ 9, "186264514923095703125"},
{10, "931322574615478515625"},
{10, "4656612873077392578125"},
{10, "23283064365386962890625"},
{10, "116415321826934814453125"},
{11, "582076609134674072265625"},
{11, "2910383045673370361328125"},
{11, "14551915228366851806640625"},
{12, "72759576141834259033203125"},
{12, "363797880709171295166015625"},
{12, "1818989403545856475830078125"},
{13, "9094947017729282379150390625"},
{13, "45474735088646411895751953125"},
{13, "227373675443232059478759765625"},
{13, "1136868377216160297393798828125"},
{14, "5684341886080801486968994140625"},
{14, "28421709430404007434844970703125"},
{14, "142108547152020037174224853515625"},
{15, "710542735760100185871124267578125"},
{15, "3552713678800500929355621337890625"},
{15, "17763568394002504646778106689453125"},
{16, "88817841970012523233890533447265625"},
{16, "444089209850062616169452667236328125"},
{16, "2220446049250313080847263336181640625"},
{16, "11102230246251565404236316680908203125"},
{17, "55511151231257827021181583404541015625"},
{17, "277555756156289135105907917022705078125"},
{17, "1387778780781445675529539585113525390625"},
{18, "6938893903907228377647697925567626953125"},
{18, "34694469519536141888238489627838134765625"},
{18, "173472347597680709441192448139190673828125"},
{19, "867361737988403547205962240695953369140625"},
}
d := len(a.digits)
shift_left :: proc(a: ^Decimal, k: uint) #no_bounds_check {
prefix_less :: #force_inline proc "contextless" (b: []byte, s: string) -> bool #no_bounds_check {
for i in 0..<len(s) {
if i >= len(b) {
return true
}
if b[i] != s[i] {
return b[i] < s[i]
}
}
return false
}
assert(k < 61)
delta := _shift_left_offsets[k].delta
if prefix_less(a.digits[:a.count], _shift_left_offsets[k].cutoff) {
delta -= 1
}
read_index := a.count
write_index := a.count+delta
n: uint
for r -= 1; r >= 0; r -= 1 {
n += (uint(a.digits[r]) - '0') << k
for read_index -= 1; read_index >= 0; read_index -= 1 {
n += (uint(a.digits[read_index]) - '0') << k
quo := n/10
rem := n - 10*quo
w -= 1
if w < d {
a.digits[w] = byte('0' + rem)
write_index -= 1
if write_index < len(a.digits) {
a.digits[write_index] = byte('0' + rem)
} else if rem != 0 {
a.trunc = true
}
@@ -242,21 +345,18 @@ shift_left :: proc(a: ^Decimal, k: uint) {
for n > 0 {
quo := n/10
rem := n - 10*quo
w -= 1
if w < d {
a.digits[w] = byte('0' + rem)
write_index -= 1
if write_index < len(a.digits) {
a.digits[write_index] = byte('0' + rem)
} else if rem != 0 {
a.trunc = true
}
n = quo
}
// NOTE(bill): Remove unused buffer size
assert(w >= 0)
capacity -= w
a.decimal_point += delta
a.count = min(a.count+capacity, d)
a.decimal_point += capacity
a.count = clamp(a.count, 0, len(a.digits))
trim(a)
}
+5 -5
View File
@@ -287,13 +287,13 @@ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Inf
@(private)
decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64, overflow: bool) {
end :: proc "contextless" (d: ^decimal.Decimal, mant: u64, exp: int, info: ^Float_Info) -> (b: u64) {
bits := mant & (u64(1)<<info.mantbits - 1)
end :: proc "contextless" (d: ^decimal.Decimal, mant: u64, exp: int, info: ^Float_Info) -> (bits: u64) {
bits = mant & (u64(1)<<info.mantbits - 1)
bits |= u64((exp-info.bias) & (1<<info.expbits - 1)) << info.mantbits
if d.neg {
bits |= 1<< info.mantbits << info.expbits
}
return bits
return
}
set_overflow :: proc "contextless" (mant: ^u64, exp: ^int, info: ^Float_Info) -> bool {
mant^ = 0
@@ -303,7 +303,7 @@ decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64
mant: u64
exp: int
if d.decimal_point == 0 {
if d.count == 0 {
mant = 0
exp = info.bias
b = end(d, mant, exp, info)
@@ -326,7 +326,7 @@ decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64
exp = 0
for d.decimal_point > 0 {
n := 27 if d.decimal_point >= len(power_table) else power_table[d.decimal_point]
decimal.shift(d, n)
decimal.shift(d, -n)
exp += n
}
for d.decimal_point < 0 || d.decimal_point == 0 && d.digits[0] < '5' {
-5
View File
@@ -3,7 +3,6 @@ package strconv
Int_Flag :: enum {
Prefix,
Plus,
Space,
}
Int_Flags :: bit_set[Int_Flag]
@@ -73,8 +72,6 @@ append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: i
i-=1; a[i] = '-'
case .Plus in flags:
i-=1; a[i] = '+'
case .Space in flags:
i-=1; a[i] = ' '
}
out := a[i:]
@@ -157,8 +154,6 @@ append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_si
i-=1; a[i] = '-'
case .Plus in flags:
i-=1; a[i] = '+'
case .Space in flags:
i-=1; a[i] = ' '
}
out := a[i:]
+1 -2
View File
@@ -819,7 +819,7 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
}
if mantissa>>_f64_info.mantbits != 0 {
return
break trunc_block
}
f := f64(mantissa)
if neg {
@@ -841,7 +841,6 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
return f / pow10[-exp], true
}
}
d: decimal.Decimal
decimal.set(&d, str[:nr])
b, overflow := decimal_to_float_bits(&d, &_f64_info)
+3
View File
@@ -5,6 +5,7 @@ import "core:io"
import "core:mem"
import "core:slice"
import "core:unicode"
import "core:runtime"
import "core:unicode/utf8"
// returns a clone of the string `s` allocated using the `allocator`
@@ -1425,7 +1426,9 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator
// TODO maybe remove duplicate substrs
// sort substrings by string size, largest to smallest
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
temp_substrs := slice.clone(substrs, context.temp_allocator)
defer delete(temp_substrs)
slice.sort_by(temp_substrs, proc(a, b: string) -> bool {
return len(a) > len(b)
})
+95 -30
View File
@@ -1,6 +1,8 @@
package sync
import "core:time"
import vg "core:sys/valgrind"
_ :: vg
// A Wait_Group waits for a collection of threads to finish
//
@@ -11,7 +13,7 @@ Wait_Group :: struct {
cond: Cond,
}
wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) {
if delta == 0 {
return
}
@@ -20,32 +22,32 @@ wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
atomic_add(&wg.counter, delta)
if wg.counter < 0 {
panic("sync.Wait_Group negative counter")
_panic("sync.Wait_Group negative counter")
}
if wg.counter == 0 {
cond_broadcast(&wg.cond)
if wg.counter != 0 {
panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
_panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
}
}
}
wait_group_done :: proc(wg: ^Wait_Group) {
wait_group_done :: proc "contextless" (wg: ^Wait_Group) {
wait_group_add(wg, -1)
}
wait_group_wait :: proc(wg: ^Wait_Group) {
wait_group_wait :: proc "contextless" (wg: ^Wait_Group) {
guard(&wg.mutex)
if wg.counter != 0 {
cond_wait(&wg.cond, &wg.mutex)
if wg.counter != 0 {
panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
_panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
}
}
}
wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) -> bool {
wait_group_wait_with_timeout :: proc "contextless" (wg: ^Wait_Group, duration: time.Duration) -> bool {
if duration <= 0 {
return false
}
@@ -56,7 +58,7 @@ wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) -
return false
}
if wg.counter != 0 {
panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
_panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
}
}
return true
@@ -76,7 +78,7 @@ Example:
barrier := &sync.Barrier{}
main :: proc() {
main :: proc "contextless" () {
fmt.println("Start")
THREAD_COUNT :: 4
@@ -107,7 +109,10 @@ Barrier :: struct {
thread_count: int,
}
barrier_init :: proc(b: ^Barrier, thread_count: int) {
barrier_init :: proc "contextless" (b: ^Barrier, thread_count: int) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_barrier_resize_pre(b, uint(thread_count))
}
b.index = 0
b.generation_id = 0
b.thread_count = thread_count
@@ -115,7 +120,10 @@ barrier_init :: proc(b: ^Barrier, thread_count: int) {
// Block the current thread until all threads have rendezvoused
// Barrier can be reused after all threads rendezvoused once, and can be used continuously
barrier_wait :: proc(b: ^Barrier) -> (is_leader: bool) {
barrier_wait :: proc "contextless" (b: ^Barrier) -> (is_leader: bool) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_barrier_wait_pre(b)
}
guard(&b.mutex)
local_gen := b.generation_id
b.index += 1
@@ -141,7 +149,7 @@ Auto_Reset_Event :: struct {
sema: Sema,
}
auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
auto_reset_event_signal :: proc "contextless" (e: ^Auto_Reset_Event) {
old_status := atomic_load_explicit(&e.status, .Relaxed)
for {
new_status := old_status + 1 if old_status < 1 else 1
@@ -155,7 +163,7 @@ auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
}
}
auto_reset_event_wait :: proc(e: ^Auto_Reset_Event) {
auto_reset_event_wait :: proc "contextless" (e: ^Auto_Reset_Event) {
old_status := atomic_sub_explicit(&e.status, 1, .Acquire)
if old_status < 1 {
sema_wait(&e.sema)
@@ -169,18 +177,18 @@ Ticket_Mutex :: struct {
serving: uint,
}
ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
ticket_mutex_lock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) {
ticket := atomic_add_explicit(&m.ticket, 1, .Relaxed)
for ticket != atomic_load_explicit(&m.serving, .Acquire) {
cpu_relax()
}
}
ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
ticket_mutex_unlock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) {
atomic_add_explicit(&m.serving, 1, .Relaxed)
}
@(deferred_in=ticket_mutex_unlock)
ticket_mutex_guard :: proc(m: ^Ticket_Mutex) -> bool {
ticket_mutex_guard :: proc "contextless" (m: ^Ticket_Mutex) -> bool {
ticket_mutex_lock(m)
return true
}
@@ -191,25 +199,25 @@ Benaphore :: struct {
sema: Sema,
}
benaphore_lock :: proc(b: ^Benaphore) {
benaphore_lock :: proc "contextless" (b: ^Benaphore) {
if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
sema_wait(&b.sema)
}
}
benaphore_try_lock :: proc(b: ^Benaphore) -> bool {
benaphore_try_lock :: proc "contextless" (b: ^Benaphore) -> bool {
v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire)
return v == 0
}
benaphore_unlock :: proc(b: ^Benaphore) {
benaphore_unlock :: proc "contextless" (b: ^Benaphore) {
if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
sema_post(&b.sema)
}
}
@(deferred_in=benaphore_unlock)
benaphore_guard :: proc(m: ^Benaphore) -> bool {
benaphore_guard :: proc "contextless" (m: ^Benaphore) -> bool {
benaphore_lock(m)
return true
}
@@ -221,7 +229,7 @@ Recursive_Benaphore :: struct {
sema: Sema,
}
recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
recursive_benaphore_lock :: proc "contextless" (b: ^Recursive_Benaphore) {
tid := current_thread_id()
if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
if tid != b.owner {
@@ -233,7 +241,7 @@ recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
b.recursion += 1
}
recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
recursive_benaphore_try_lock :: proc "contextless" (b: ^Recursive_Benaphore) -> bool {
tid := current_thread_id()
if b.owner == tid {
atomic_add_explicit(&b.counter, 1, .Acquire)
@@ -248,9 +256,9 @@ recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
return true
}
recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
recursive_benaphore_unlock :: proc "contextless" (b: ^Recursive_Benaphore) {
tid := current_thread_id()
assert(tid == b.owner)
_assert(tid == b.owner, "tid != b.owner")
b.recursion -= 1
recursion := b.recursion
if recursion == 0 {
@@ -265,7 +273,7 @@ recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
}
@(deferred_in=recursive_benaphore_unlock)
recursive_benaphore_guard :: proc(m: ^Recursive_Benaphore) -> bool {
recursive_benaphore_guard :: proc "contextless" (m: ^Recursive_Benaphore) -> bool {
recursive_benaphore_lock(m)
return true
}
@@ -282,7 +290,15 @@ Once :: struct {
}
// once_do calls the procedure fn if and only if once_do is being called for the first for this instance of Once.
once_do :: proc(o: ^Once, fn: proc()) {
once_do :: proc{
once_do_without_data,
once_do_without_data_contextless,
once_do_with_data,
once_do_with_data_contextless,
}
// once_do_without_data calls the procedure fn if and only if once_do_without_data is being called for the first for this instance of Once.
once_do_without_data :: proc(o: ^Once, fn: proc()) {
@(cold)
do_slow :: proc(o: ^Once, fn: proc()) {
guard(&o.m)
@@ -292,12 +308,61 @@ once_do :: proc(o: ^Once, fn: proc()) {
}
}
if atomic_load_explicit(&o.done, .Acquire) == false {
do_slow(o, fn)
}
}
// once_do_without_data calls the procedure fn if and only if once_do_without_data is being called for the first for this instance of Once.
once_do_without_data_contextless :: proc(o: ^Once, fn: proc "contextless" ()) {
@(cold)
do_slow :: proc(o: ^Once, fn: proc "contextless" ()) {
guard(&o.m)
if !o.done {
fn()
atomic_store_explicit(&o.done, true, .Release)
}
}
if atomic_load_explicit(&o.done, .Acquire) == false {
do_slow(o, fn)
}
}
// once_do_with_data calls the procedure fn if and only if once_do_with_data is being called for the first for this instance of Once.
once_do_with_data :: proc(o: ^Once, fn: proc(data: rawptr), data: rawptr) {
@(cold)
do_slow :: proc(o: ^Once, fn: proc(data: rawptr), data: rawptr) {
guard(&o.m)
if !o.done {
fn(data)
atomic_store_explicit(&o.done, true, .Release)
}
}
if atomic_load_explicit(&o.done, .Acquire) == false {
do_slow(o, fn, data)
}
}
// once_do_with_data_contextless calls the procedure fn if and only if once_do_with_data_contextless is being called for the first for this instance of Once.
once_do_with_data_contextless :: proc "contextless" (o: ^Once, fn: proc "contextless" (data: rawptr), data: rawptr) {
@(cold)
do_slow :: proc "contextless" (o: ^Once, fn: proc "contextless" (data: rawptr), data: rawptr) {
guard(&o.m)
if !o.done {
fn(data)
atomic_store_explicit(&o.done, true, .Release)
}
}
if atomic_load_explicit(&o.done, .Acquire) == false {
do_slow(o, fn, data)
}
}
// A Parker is an associated token which is initially not present:
@@ -314,7 +379,7 @@ Parker :: struct {
// Blocks the current thread until the token is made available.
//
// Assumes this is only called by the thread that owns the Parker.
park :: proc(p: ^Parker) {
park :: proc "contextless" (p: ^Parker) {
EMPTY :: 0
NOTIFIED :: 1
PARKED :: max(u32)
@@ -333,7 +398,7 @@ park :: proc(p: ^Parker) {
// for a limited duration.
//
// Assumes this is only called by the thread that owns the Parker
park_with_timeout :: proc(p: ^Parker, duration: time.Duration) {
park_with_timeout :: proc "contextless" (p: ^Parker, duration: time.Duration) {
EMPTY :: 0
NOTIFIED :: 1
PARKED :: max(u32)
@@ -345,7 +410,7 @@ park_with_timeout :: proc(p: ^Parker, duration: time.Duration) {
}
// Automatically makes thee token available if it was not already.
unpark :: proc(p: ^Parker) {
unpark :: proc "contextless" (p: ^Parker) {
EMPTY :: 0
NOTIFIED :: 1
PARKED :: max(Futex)
+7 -7
View File
@@ -24,11 +24,11 @@ EINTR :: -4
EFAULT :: -14
ETIMEDOUT :: -60
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
return _futex_wait_with_timeout(f, expected, 0)
}
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
timeout_ns := u32(duration) * 1000
s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns)
@@ -41,13 +41,13 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
case ETIMEDOUT:
return false
case:
panic("futex_wait failure")
_panic("futex_wait failure")
}
return true
}
_futex_signal :: proc(f: ^Futex) {
_futex_signal :: proc "contextless" (f: ^Futex) {
loop: for {
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0)
if s >= 0 {
@@ -59,12 +59,12 @@ _futex_signal :: proc(f: ^Futex) {
case ENOENT:
return
case:
panic("futex_wake_single failure")
_panic("futex_wake_single failure")
}
}
}
_futex_broadcast :: proc(f: ^Futex) {
_futex_broadcast :: proc "contextless" (f: ^Futex) {
loop: for {
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0)
if s >= 0 {
@@ -76,7 +76,7 @@ _futex_broadcast :: proc(f: ^Futex) {
case ENOENT:
return
case:
panic("futex_wake_all failure")
_panic("futex_wake_all failure")
}
}
}
+8 -8
View File
@@ -17,7 +17,7 @@ foreign libc {
__error :: proc "c" () -> ^c.int ---
}
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
timeout := [2]i64{14400, 0} // 4 hours
for {
res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
@@ -30,12 +30,12 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
continue
}
panic("_futex_wait failure")
_panic("_futex_wait failure")
}
unreachable()
}
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
if duration <= 0 {
return false
}
@@ -51,21 +51,21 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
return false
}
panic("_futex_wait_with_timeout failure")
_panic("_futex_wait_with_timeout failure")
}
_futex_signal :: proc(f: ^Futex) {
_futex_signal :: proc "contextless" (f: ^Futex) {
res := _umtx_op(f, UMTX_OP_WAKE, 1, nil, nil)
if res == -1 {
panic("_futex_signal failure")
_panic("_futex_signal failure")
}
}
_futex_broadcast :: proc(f: ^Futex) {
_futex_broadcast :: proc "contextless" (f: ^Futex) {
res := _umtx_op(f, UMTX_OP_WAKE, c.ulong(max(i32)), nil, nil)
if res == -1 {
panic("_futex_broadcast failure")
_panic("_futex_broadcast failure")
}
}
+10 -10
View File
@@ -21,20 +21,20 @@ EFAULT :: -14
EINVAL :: -22
ETIMEDOUT :: -110
get_errno :: proc(r: int) -> int {
get_errno :: proc "contextless" (r: int) -> int {
if -4096 < r && r < 0 {
return r
}
return 0
}
internal_futex :: proc(f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> int {
internal_futex :: proc "contextless" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> int {
code := int(intrinsics.syscall(unix.SYS_futex, uintptr(f), uintptr(op), uintptr(val), uintptr(timeout), 0, 0))
return get_errno(code)
}
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
err := internal_futex(f, FUTEX_WAIT_PRIVATE | FUTEX_WAIT, expected, nil)
switch err {
case ESUCCESS, EINTR, EAGAIN, EINVAL:
@@ -44,12 +44,12 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
case EFAULT:
fallthrough
case:
panic("futex_wait failure")
_panic("futex_wait failure")
}
return true
}
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
if duration <= 0 {
return false
}
@@ -71,27 +71,27 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
case EFAULT:
fallthrough
case:
panic("futex_wait_with_timeout failure")
_panic("futex_wait_with_timeout failure")
}
return true
}
_futex_signal :: proc(f: ^Futex) {
_futex_signal :: proc "contextless" (f: ^Futex) {
err := internal_futex(f, FUTEX_WAKE_PRIVATE | FUTEX_WAKE, 1, nil)
switch err {
case ESUCCESS, EINVAL, EFAULT:
// okay
case:
panic("futex_wake_single failure")
_panic("futex_wake_single failure")
}
}
_futex_broadcast :: proc(f: ^Futex) {
_futex_broadcast :: proc "contextless" (f: ^Futex) {
err := internal_futex(f, FUTEX_WAKE_PRIVATE | FUTEX_WAKE, u32(max(i32)), nil)
switch err {
case ESUCCESS, EINVAL, EFAULT:
// okay
case:
panic("_futex_wake_all failure")
_panic("_futex_wake_all failure")
}
}
+8 -8
View File
@@ -21,7 +21,7 @@ foreign libc {
_unix_futex :: proc "c" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> c.int ---
}
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, nil)
if res != -1 {
@@ -32,10 +32,10 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
return false
}
panic("futex_wait failure")
_panic("futex_wait failure")
}
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
if duration <= 0 {
return false
}
@@ -58,21 +58,21 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
return false
}
panic("futex_wait_with_timeout failure")
_panic("futex_wait_with_timeout failure")
}
_futex_signal :: proc(f: ^Futex) {
_futex_signal :: proc "contextless" (f: ^Futex) {
res := _unix_futex(f, FUTEX_WAKE_PRIVATE, 1, nil)
if res == -1 {
panic("futex_wake_single failure")
_panic("futex_wake_single failure")
}
}
_futex_broadcast :: proc(f: ^Futex) {
_futex_broadcast :: proc "contextless" (f: ^Futex) {
res := _unix_futex(f, FUTEX_WAKE_PRIVATE, u32(max(i32)), nil)
if res == -1 {
panic("_futex_wake_all failure")
_panic("_futex_wake_all failure")
}
}
+4 -4
View File
@@ -5,18 +5,18 @@ package sync
import "core:intrinsics"
import "core:time"
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
return s != 0
}
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration))
return s != 0
}
_futex_signal :: proc(f: ^Futex) {
_futex_signal :: proc "contextless" (f: ^Futex) {
loop: for {
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
if s >= 1 {
@@ -25,7 +25,7 @@ _futex_signal :: proc(f: ^Futex) {
}
}
_futex_broadcast :: proc(f: ^Futex) {
_futex_broadcast :: proc "contextless" (f: ^Futex) {
loop: for {
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
if s >= 0 {
+4 -4
View File
@@ -39,22 +39,22 @@ CustomWaitOnAddress :: proc "stdcall" (Address: rawptr, CompareAddress: rawptr,
}
_futex_wait :: proc(f: ^Futex, expect: u32) -> bool {
_futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> bool {
expect := expect
return CustomWaitOnAddress(f, &expect, size_of(expect), nil)
}
_futex_wait_with_timeout :: proc(f: ^Futex, expect: u32, duration: time.Duration) -> bool {
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> bool {
expect := expect
// NOTE(bill): for some bizarre reason, this has be a negative number
timeout := -i64(duration / 100)
return CustomWaitOnAddress(f, &expect, size_of(expect), &timeout)
}
_futex_signal :: proc(f: ^Futex) {
_futex_signal :: proc "contextless" (f: ^Futex) {
WakeByAddressSingle(f)
}
_futex_broadcast :: proc(f: ^Futex) {
_futex_broadcast :: proc "contextless" (f: ^Futex) {
WakeByAddressAll(f)
}
+44 -28
View File
@@ -1,5 +1,6 @@
package sync
import "core:runtime"
import "core:time"
current_thread_id :: proc "contextless" () -> int {
@@ -15,17 +16,17 @@ Mutex :: struct {
}
// mutex_lock locks m
mutex_lock :: proc(m: ^Mutex) {
mutex_lock :: proc "contextless" (m: ^Mutex) {
_mutex_lock(m)
}
// mutex_unlock unlocks m
mutex_unlock :: proc(m: ^Mutex) {
mutex_unlock :: proc "contextless" (m: ^Mutex) {
_mutex_unlock(m)
}
// mutex_try_lock tries to lock m, will return true on success, and false on failure
mutex_try_lock :: proc(m: ^Mutex) -> bool {
mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
return _mutex_try_lock(m)
}
@@ -36,7 +37,7 @@ Example:
}
*/
@(deferred_in=mutex_unlock)
mutex_guard :: proc(m: ^Mutex) -> bool {
mutex_guard :: proc "contextless" (m: ^Mutex) -> bool {
mutex_lock(m)
return true
}
@@ -52,32 +53,32 @@ RW_Mutex :: struct {
// rw_mutex_lock locks rw for writing (with a single writer)
// If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
rw_mutex_lock :: proc(rw: ^RW_Mutex) {
rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
_rw_mutex_lock(rw)
}
// rw_mutex_unlock unlocks rw for writing (with a single writer)
rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
_rw_mutex_unlock(rw)
}
// rw_mutex_try_lock tries to lock rw for writing (with a single writer)
rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
return _rw_mutex_try_lock(rw)
}
// rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
_rw_mutex_shared_lock(rw)
}
// rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers)
rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
_rw_mutex_shared_unlock(rw)
}
// rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
return _rw_mutex_try_shared_lock(rw)
}
/*
@@ -87,7 +88,7 @@ Example:
}
*/
@(deferred_in=rw_mutex_unlock)
rw_mutex_guard :: proc(m: ^RW_Mutex) -> bool {
rw_mutex_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
rw_mutex_lock(m)
return true
}
@@ -99,7 +100,7 @@ Example:
}
*/
@(deferred_in=rw_mutex_shared_unlock)
rw_mutex_shared_guard :: proc(m: ^RW_Mutex) -> bool {
rw_mutex_shared_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
rw_mutex_shared_lock(m)
return true
}
@@ -114,15 +115,15 @@ Recursive_Mutex :: struct {
impl: _Recursive_Mutex,
}
recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
_recursive_mutex_lock(m)
}
recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {
_recursive_mutex_unlock(m)
}
recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
recursive_mutex_try_lock :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
return _recursive_mutex_try_lock(m)
}
@@ -133,7 +134,7 @@ Example:
}
*/
@(deferred_in=recursive_mutex_unlock)
recursive_mutex_guard :: proc(m: ^Recursive_Mutex) -> bool {
recursive_mutex_guard :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
recursive_mutex_lock(m)
return true
}
@@ -147,22 +148,22 @@ Cond :: struct {
impl: _Cond,
}
cond_wait :: proc(c: ^Cond, m: ^Mutex) {
cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
_cond_wait(c, m)
}
cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
if duration <= 0 {
return false
}
return _cond_wait_with_timeout(c, m, duration)
}
cond_signal :: proc(c: ^Cond) {
cond_signal :: proc "contextless" (c: ^Cond) {
_cond_signal(c)
}
cond_broadcast :: proc(c: ^Cond) {
cond_broadcast :: proc "contextless" (c: ^Cond) {
_cond_broadcast(c)
}
@@ -175,15 +176,15 @@ Sema :: struct {
impl: _Sema,
}
sema_post :: proc(s: ^Sema, count := 1) {
sema_post :: proc "contextless" (s: ^Sema, count := 1) {
_sema_post(s, count)
}
sema_wait :: proc(s: ^Sema) {
sema_wait :: proc "contextless" (s: ^Sema) {
_sema_wait(s)
}
sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
return _sema_wait_with_timeout(s, duration)
}
@@ -194,16 +195,16 @@ sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
// An Futex must not be copied after first use
Futex :: distinct u32
futex_wait :: proc(f: ^Futex, expected: u32) {
futex_wait :: proc "contextless" (f: ^Futex, expected: u32) {
if u32(atomic_load_explicit(f, .Acquire)) != expected {
return
}
assert(_futex_wait(f, expected), "futex_wait failure")
_assert(_futex_wait(f, expected), "futex_wait failure")
}
// returns true if the wait happened within the duration, false if it exceeded the time duration
futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
if u32(atomic_load_explicit(f, .Acquire)) != expected {
return true
}
@@ -214,10 +215,25 @@ futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duratio
return _futex_wait_with_timeout(f, expected, duration)
}
futex_signal :: proc(f: ^Futex) {
futex_signal :: proc "contextless" (f: ^Futex) {
_futex_signal(f)
}
futex_broadcast :: proc(f: ^Futex) {
futex_broadcast :: proc "contextless" (f: ^Futex) {
_futex_broadcast(f)
}
@(private)
_assert :: proc "contextless" (cond: bool, msg: string) {
if !cond {
_panic(msg)
}
}
@(private)
_panic :: proc "contextless" (msg: string) -> ! {
runtime.print_string(msg)
runtime.print_byte('\n')
runtime.trap()
}
+26 -26
View File
@@ -18,9 +18,9 @@ Atomic_Mutex :: struct {
}
// atomic_mutex_lock locks m
atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
atomic_mutex_lock :: proc "contextless" (m: ^Atomic_Mutex) {
@(cold)
lock_slow :: proc(m: ^Atomic_Mutex, curr_state: Atomic_Mutex_State) {
lock_slow :: proc "contextless" (m: ^Atomic_Mutex, curr_state: Atomic_Mutex_State) {
new_state := curr_state // Make a copy of it
spin_lock: for spin in 0..<i32(100) {
@@ -58,9 +58,9 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
}
// atomic_mutex_unlock unlocks m
atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
atomic_mutex_unlock :: proc "contextless" (m: ^Atomic_Mutex) {
@(cold)
unlock_slow :: proc(m: ^Atomic_Mutex) {
unlock_slow :: proc "contextless" (m: ^Atomic_Mutex) {
futex_signal((^Futex)(&m.state))
}
@@ -76,7 +76,7 @@ atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
}
// atomic_mutex_try_lock tries to lock m, will return true on success, and false on failure
atomic_mutex_try_lock :: proc(m: ^Atomic_Mutex) -> bool {
atomic_mutex_try_lock :: proc "contextless" (m: ^Atomic_Mutex) -> bool {
_, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .Acquire, .Consume)
return ok
}
@@ -88,7 +88,7 @@ Example:
}
*/
@(deferred_in=atomic_mutex_unlock)
atomic_mutex_guard :: proc(m: ^Atomic_Mutex) -> bool {
atomic_mutex_guard :: proc "contextless" (m: ^Atomic_Mutex) -> bool {
atomic_mutex_lock(m)
return true
}
@@ -117,7 +117,7 @@ Atomic_RW_Mutex :: struct {
// atomic_rw_mutex_lock locks rw for writing (with a single writer)
// If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
atomic_rw_mutex_lock :: proc(rw: ^Atomic_RW_Mutex) {
atomic_rw_mutex_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
_ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Writer)
atomic_mutex_lock(&rw.mutex)
@@ -128,13 +128,13 @@ atomic_rw_mutex_lock :: proc(rw: ^Atomic_RW_Mutex) {
}
// atomic_rw_mutex_unlock unlocks rw for writing (with a single writer)
atomic_rw_mutex_unlock :: proc(rw: ^Atomic_RW_Mutex) {
atomic_rw_mutex_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
_ = atomic_and(&rw.state, ~Atomic_RW_Mutex_State_Is_Writing)
atomic_mutex_unlock(&rw.mutex)
}
// atomic_rw_mutex_try_lock tries to lock rw for writing (with a single writer)
atomic_rw_mutex_try_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_try_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) -> bool {
if atomic_mutex_try_lock(&rw.mutex) {
state := atomic_load(&rw.state)
if state & Atomic_RW_Mutex_State_Reader_Mask == 0 {
@@ -148,7 +148,7 @@ atomic_rw_mutex_try_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
}
// atomic_rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
atomic_rw_mutex_shared_lock :: proc(rw: ^Atomic_RW_Mutex) {
atomic_rw_mutex_shared_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
state := atomic_load(&rw.state)
for state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
ok: bool
@@ -164,7 +164,7 @@ atomic_rw_mutex_shared_lock :: proc(rw: ^Atomic_RW_Mutex) {
}
// atomic_rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers)
atomic_rw_mutex_shared_unlock :: proc(rw: ^Atomic_RW_Mutex) {
atomic_rw_mutex_shared_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
state := atomic_sub(&rw.state, Atomic_RW_Mutex_State_Reader)
if (state & Atomic_RW_Mutex_State_Reader_Mask == Atomic_RW_Mutex_State_Reader) &&
@@ -174,7 +174,7 @@ atomic_rw_mutex_shared_unlock :: proc(rw: ^Atomic_RW_Mutex) {
}
// atomic_rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
atomic_rw_mutex_try_shared_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) -> bool {
state := atomic_load(&rw.state)
if state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
_, ok := atomic_compare_exchange_strong(&rw.state, state, state + Atomic_RW_Mutex_State_Reader)
@@ -198,7 +198,7 @@ Example:
}
*/
@(deferred_in=atomic_rw_mutex_unlock)
atomic_rw_mutex_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_guard :: proc "contextless" (m: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_lock(m)
return true
}
@@ -210,7 +210,7 @@ Example:
}
*/
@(deferred_in=atomic_rw_mutex_shared_unlock)
atomic_rw_mutex_shared_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_shared_guard :: proc "contextless" (m: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_shared_lock(m)
return true
}
@@ -228,7 +228,7 @@ Atomic_Recursive_Mutex :: struct {
mutex: Mutex,
}
atomic_recursive_mutex_lock :: proc(m: ^Atomic_Recursive_Mutex) {
atomic_recursive_mutex_lock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) {
tid := current_thread_id()
if tid != m.owner {
mutex_lock(&m.mutex)
@@ -238,9 +238,9 @@ atomic_recursive_mutex_lock :: proc(m: ^Atomic_Recursive_Mutex) {
m.recursion += 1
}
atomic_recursive_mutex_unlock :: proc(m: ^Atomic_Recursive_Mutex) {
atomic_recursive_mutex_unlock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) {
tid := current_thread_id()
assert(tid == m.owner)
_assert(tid == m.owner, "tid != m.owner")
m.recursion -= 1
recursion := m.recursion
if recursion == 0 {
@@ -253,7 +253,7 @@ atomic_recursive_mutex_unlock :: proc(m: ^Atomic_Recursive_Mutex) {
}
atomic_recursive_mutex_try_lock :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
atomic_recursive_mutex_try_lock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) -> bool {
tid := current_thread_id()
if m.owner == tid {
return mutex_try_lock(&m.mutex)
@@ -274,7 +274,7 @@ Example:
}
*/
@(deferred_in=atomic_recursive_mutex_unlock)
atomic_recursive_mutex_guard :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
atomic_recursive_mutex_guard :: proc "contextless" (m: ^Atomic_Recursive_Mutex) -> bool {
atomic_recursive_mutex_lock(m)
return true
}
@@ -289,7 +289,7 @@ Atomic_Cond :: struct {
state: Futex,
}
atomic_cond_wait :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex) {
atomic_cond_wait :: proc "contextless" (c: ^Atomic_Cond, m: ^Atomic_Mutex) {
state := u32(atomic_load_explicit(&c.state, .Relaxed))
unlock(m)
futex_wait(&c.state, state)
@@ -297,7 +297,7 @@ atomic_cond_wait :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex) {
}
atomic_cond_wait_with_timeout :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex, duration: time.Duration) -> (ok: bool) {
atomic_cond_wait_with_timeout :: proc "contextless" (c: ^Atomic_Cond, m: ^Atomic_Mutex, duration: time.Duration) -> (ok: bool) {
state := u32(atomic_load_explicit(&c.state, .Relaxed))
unlock(m)
ok = futex_wait_with_timeout(&c.state, state, duration)
@@ -306,12 +306,12 @@ atomic_cond_wait_with_timeout :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex, duratio
}
atomic_cond_signal :: proc(c: ^Atomic_Cond) {
atomic_cond_signal :: proc "contextless" (c: ^Atomic_Cond) {
atomic_add_explicit(&c.state, 1, .Release)
futex_signal(&c.state)
}
atomic_cond_broadcast :: proc(c: ^Atomic_Cond) {
atomic_cond_broadcast :: proc "contextless" (c: ^Atomic_Cond) {
atomic_add_explicit(&c.state, 1, .Release)
futex_broadcast(&c.state)
}
@@ -324,7 +324,7 @@ Atomic_Sema :: struct {
count: Futex,
}
atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) {
atomic_sema_post :: proc "contextless" (s: ^Atomic_Sema, count := 1) {
atomic_add_explicit(&s.count, Futex(count), .Release)
if count == 1 {
futex_signal(&s.count)
@@ -333,7 +333,7 @@ atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) {
}
}
atomic_sema_wait :: proc(s: ^Atomic_Sema) {
atomic_sema_wait :: proc "contextless" (s: ^Atomic_Sema) {
for {
original_count := atomic_load_explicit(&s.count, .Relaxed)
for original_count == 0 {
@@ -346,7 +346,7 @@ atomic_sema_wait :: proc(s: ^Atomic_Sema) {
}
}
atomic_sema_wait_with_timeout :: proc(s: ^Atomic_Sema, duration: time.Duration) -> bool {
atomic_sema_wait_with_timeout :: proc "contextless" (s: ^Atomic_Sema, duration: time.Duration) -> bool {
if duration <= 0 {
return false
}
+78 -19
View File
@@ -2,20 +2,31 @@
package sync
import "core:time"
import vg "core:sys/valgrind"
_ :: vg
_Sema :: struct {
atomic: Atomic_Sema,
}
_sema_post :: proc(s: ^Sema, count := 1) {
_sema_post :: proc "contextless" (s: ^Sema, count := 1) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_sem_post_pre(s)
}
atomic_sema_post(&s.impl.atomic, count)
}
_sema_wait :: proc(s: ^Sema) {
_sema_wait :: proc "contextless" (s: ^Sema) {
atomic_sema_wait(&s.impl.atomic)
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_sem_wait_post(s)
}
}
_sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
_sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
when ODIN_VALGRIND_SUPPORT {
defer vg.helgrind_sem_wait_post(s)
}
return atomic_sema_wait_with_timeout(&s.impl.atomic, duration)
}
@@ -25,7 +36,12 @@ _Recursive_Mutex :: struct {
recursion: i32,
}
_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
_recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_mutex_lock_pre(m, false)
defer vg.helgrind_mutex_lock_post(m)
}
tid := Futex(current_thread_id())
for {
prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
@@ -40,7 +56,12 @@ _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
}
}
_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
_recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_mutex_unlock_pre(m)
defer vg.helgrind_mutex_unlock_post(m)
}
m.impl.recursion -= 1
if m.impl.recursion != 0 {
return
@@ -52,7 +73,7 @@ _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
}
_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
_recursive_mutex_try_lock :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
tid := Futex(current_thread_id())
prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
switch prev_owner {
@@ -70,15 +91,27 @@ when ODIN_OS != .Windows {
mutex: Atomic_Mutex,
}
_mutex_lock :: proc(m: ^Mutex) {
_mutex_lock :: proc "contextless" (m: ^Mutex) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_mutex_lock_pre(m, false)
defer vg.helgrind_mutex_lock_post(m)
}
atomic_mutex_lock(&m.impl.mutex)
}
_mutex_unlock :: proc(m: ^Mutex) {
_mutex_unlock :: proc "contextless" (m: ^Mutex) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_mutex_unlock_pre(m)
defer vg.helgrind_mutex_unlock_post(m)
}
atomic_mutex_unlock(&m.impl.mutex)
}
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
_mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_mutex_lock_pre(m, true)
defer vg.helgrind_mutex_lock_post(m)
}
return atomic_mutex_try_lock(&m.impl.mutex)
}
@@ -86,19 +119,33 @@ when ODIN_OS != .Windows {
cond: Atomic_Cond,
}
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
_cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
when ODIN_VALGRIND_SUPPORT {
_ = vg.helgrind_cond_wait_pre(c, m)
defer _ = vg.helgrind_cond_wait_post(c, m)
}
atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
}
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
_cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
when ODIN_VALGRIND_SUPPORT {
_ = vg.helgrind_cond_wait_pre(c, m)
defer _ = vg.helgrind_cond_wait_post(c, m)
}
return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
}
_cond_signal :: proc(c: ^Cond) {
_cond_signal :: proc "contextless" (c: ^Cond) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_cond_signal_pre(c)
}
atomic_cond_signal(&c.impl.cond)
}
_cond_broadcast :: proc(c: ^Cond) {
_cond_broadcast :: proc "contextless" (c: ^Cond) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_cond_broadcast_pre(c)
}
atomic_cond_broadcast(&c.impl.cond)
}
@@ -107,27 +154,39 @@ when ODIN_OS != .Windows {
mutex: Atomic_RW_Mutex,
}
_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
_rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_rwlock_lock_pre(rw, true)
}
atomic_rw_mutex_lock(&rw.impl.mutex)
}
_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
_rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
atomic_rw_mutex_unlock(&rw.impl.mutex)
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_rwlock_unlock_post(rw, true)
}
}
_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
_rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
return atomic_rw_mutex_try_lock(&rw.impl.mutex)
}
_rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
_rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_rwlock_lock_pre(rw, false)
}
atomic_rw_mutex_shared_lock(&rw.impl.mutex)
}
_rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
_rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
atomic_rw_mutex_shared_unlock(&rw.impl.mutex)
when ODIN_VALGRIND_SUPPORT {
vg.helgrind_rwlock_unlock_post(rw, false)
}
}
_rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
return atomic_rw_mutex_try_shared_lock(&rw.impl.mutex)
}
}
+13 -13
View File
@@ -13,15 +13,15 @@ _Mutex :: struct {
srwlock: win32.SRWLOCK,
}
_mutex_lock :: proc(m: ^Mutex) {
_mutex_lock :: proc "contextless" (m: ^Mutex) {
win32.AcquireSRWLockExclusive(&m.impl.srwlock)
}
_mutex_unlock :: proc(m: ^Mutex) {
_mutex_unlock :: proc "contextless" (m: ^Mutex) {
win32.ReleaseSRWLockExclusive(&m.impl.srwlock)
}
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
_mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
return bool(win32.TryAcquireSRWLockExclusive(&m.impl.srwlock))
}
@@ -29,27 +29,27 @@ _RW_Mutex :: struct {
srwlock: win32.SRWLOCK,
}
_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
_rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
win32.AcquireSRWLockExclusive(&rw.impl.srwlock)
}
_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
_rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
win32.ReleaseSRWLockExclusive(&rw.impl.srwlock)
}
_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
_rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
return bool(win32.TryAcquireSRWLockExclusive(&rw.impl.srwlock))
}
_rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
_rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
win32.AcquireSRWLockShared(&rw.impl.srwlock)
}
_rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
_rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
win32.ReleaseSRWLockShared(&rw.impl.srwlock)
}
_rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
return bool(win32.TryAcquireSRWLockShared(&rw.impl.srwlock))
}
@@ -58,22 +58,22 @@ _Cond :: struct {
cond: win32.CONDITION_VARIABLE,
}
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
_cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
_ = win32.SleepConditionVariableSRW(&c.impl.cond, &m.impl.srwlock, win32.INFINITE, 0)
}
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
_cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
duration := u32(duration / time.Millisecond)
ok := win32.SleepConditionVariableSRW(&c.impl.cond, &m.impl.srwlock, duration, 0)
return bool(ok)
}
_cond_signal :: proc(c: ^Cond) {
_cond_signal :: proc "contextless" (c: ^Cond) {
win32.WakeConditionVariable(&c.impl.cond)
}
_cond_broadcast :: proc(c: ^Cond) {
_cond_broadcast :: proc "contextless" (c: ^Cond) {
win32.WakeAllConditionVariable(&c.impl.cond)
}
@@ -97,6 +97,7 @@ clone_to_cstring :: proc(s: string, allocator: runtime.Allocator, loc := #caller
sys_open :: proc(path: string, oflag: Open_Flags, mode: Permission) -> (c.int, bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cmode: u32 = 0
cflags: u32 = 0
@@ -132,30 +133,35 @@ sys_open :: proc(path: string, oflag: Open_Flags, mode: Permission) -> (c.int, b
}
sys_mkdir :: proc(path: string, mode: Permission) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cflags := _sys_permission_mode(mode)
return syscall_mkdir(cpath, cflags) != -1
}
sys_mkdir_at :: proc(fd: c.int, path: string, mode: Permission) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cflags := _sys_permission_mode(mode)
return syscall_mkdir_at(fd, cpath, cflags) != -1
}
sys_rmdir :: proc(path: string, mode: Permission) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cflags := _sys_permission_mode(mode)
return syscall_rmdir(cpath, cflags) != -1
}
sys_rename :: proc(path: string, new_path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cnpath: cstring = clone_to_cstring(new_path, context.temp_allocator)
return syscall_rename(cpath, cnpath) != -1
}
sys_rename_at :: proc(fd: c.int, path: string, to_fd: c.int, new_path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cnpath: cstring = clone_to_cstring(new_path, context.temp_allocator)
return syscall_rename_at(fd, cpath, to_fd, cnpath) != -1
@@ -166,12 +172,14 @@ sys_lseek :: proc(fd: c.int, offset: i64, whence: Offset_From) -> i64 {
}
sys_chmod :: proc(path: string, mode: Permission) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cmode := _sys_permission_mode(mode)
return syscall_chmod(cpath, cmode) != -1
}
sys_lstat :: proc(path: string, status: ^stat) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
return syscall_lstat(cpath, status) != -1
}
+1 -1
View File
@@ -1,6 +1,6 @@
package darwin
unix_offset_syscall :: proc(number: System_Call_Number) -> uintptr {
unix_offset_syscall :: proc "contextless" (number: System_Call_Number) -> uintptr {
return uintptr(number) + uintptr(0x2000000)
}
+48 -48
View File
@@ -229,191 +229,191 @@ _Proc_Bsdinfo :: struct {
/*--==========================================================================--*/
syscall_fsync :: #force_inline proc(fildes: c.int) -> bool {
syscall_fsync :: #force_inline proc "contextless" (fildes: c.int) -> bool {
return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.fsync), uintptr(fildes)))
}
syscall_write :: #force_inline proc (fildes: c.int, buf: ^byte, nbyte: u64) -> bool {
syscall_write :: #force_inline proc "contextless" (fildes: c.int, buf: ^byte, nbyte: u64) -> bool {
return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.write), uintptr(fildes), uintptr(buf), uintptr(nbyte)))
}
syscall_read :: #force_inline proc(fildes: c.int, buf: ^byte, nbyte: u64) -> i64 {
syscall_read :: #force_inline proc "contextless" (fildes: c.int, buf: ^byte, nbyte: u64) -> i64 {
return cast(i64)intrinsics.syscall(unix_offset_syscall(.read), uintptr(fildes), uintptr(buf), uintptr(nbyte))
}
syscall_open :: #force_inline proc(path: cstring, oflag: u32, mode: u32) -> c.int {
syscall_open :: #force_inline proc "contextless" (path: cstring, oflag: u32, mode: u32) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.open), transmute(uintptr)path, uintptr(oflag), uintptr(mode))
}
syscall_close :: #force_inline proc(fd: c.int) -> bool {
syscall_close :: #force_inline proc "contextless" (fd: c.int) -> bool {
return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.close), uintptr(fd)))
}
syscall_fchmod :: #force_inline proc(fildes: c.int, mode: u32) -> c.int {
syscall_fchmod :: #force_inline proc "contextless" (fildes: c.int, mode: u32) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.fchmod), uintptr(fildes), uintptr(mode)))
}
syscall_chmod :: #force_inline proc(path: cstring, mode: u32) -> c.int {
syscall_chmod :: #force_inline proc "contextless" (path: cstring, mode: u32) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.chmod), transmute(uintptr)path, uintptr(mode)))
}
syscall_mkdir :: #force_inline proc(path: cstring, mode: u32) -> c.int {
syscall_mkdir :: #force_inline proc "contextless" (path: cstring, mode: u32) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.mkdir), transmute(uintptr)path, uintptr(mode)))
}
syscall_mkdir_at :: #force_inline proc(fd: c.int, path: cstring, mode: u32) -> c.int {
syscall_mkdir_at :: #force_inline proc "contextless" (fd: c.int, path: cstring, mode: u32) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.mkdir), uintptr(fd), transmute(uintptr)path, uintptr(mode)))
}
syscall_rmdir :: #force_inline proc(path: cstring, mode: u32) -> c.int {
syscall_rmdir :: #force_inline proc "contextless" (path: cstring, mode: u32) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.rmdir), transmute(uintptr)path, uintptr(mode)))
}
syscall_rename :: #force_inline proc(path_old: cstring, path_new: cstring) -> c.int {
syscall_rename :: #force_inline proc "contextless" (path_old: cstring, path_new: cstring) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.rename), transmute(uintptr)path_old, transmute(uintptr)path_new))
}
syscall_rename_at :: #force_inline proc(from_fd: c.int, from: cstring, to_fd: c.int, to: cstring) -> c.int {
syscall_rename_at :: #force_inline proc "contextless" (from_fd: c.int, from: cstring, to_fd: c.int, to: cstring) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.renameat), uintptr(from_fd), transmute(uintptr)from, uintptr(to_fd), transmute(uintptr)to))
}
syscall_lseek :: #force_inline proc(fd: c.int, offset: i64, whence: c.int) -> i64 {
syscall_lseek :: #force_inline proc "contextless" (fd: c.int, offset: i64, whence: c.int) -> i64 {
return cast(i64)intrinsics.syscall(unix_offset_syscall(.lseek), uintptr(fd), uintptr(offset), uintptr(whence))
}
syscall_gettid :: #force_inline proc() -> u64 {
syscall_gettid :: #force_inline proc "contextless" () -> u64 {
return cast(u64)intrinsics.syscall(unix_offset_syscall(.gettid))
}
syscall_fstat :: #force_inline proc(fd: c.int, status: ^stat) -> c.int {
syscall_fstat :: #force_inline proc "contextless" (fd: c.int, status: ^stat) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fstat), uintptr(fd), uintptr(status))
}
syscall_lstat :: #force_inline proc(path: cstring, status: ^stat) -> c.int {
syscall_lstat :: #force_inline proc "contextless" (path: cstring, status: ^stat) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.lstat), transmute(uintptr)path, uintptr(status))
}
syscall_stat :: #force_inline proc(path: cstring, status: ^stat) -> c.int {
syscall_stat :: #force_inline proc "contextless" (path: cstring, status: ^stat) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.stat), transmute(uintptr)path, uintptr(status))
}
syscall_fstatat :: #force_inline proc(fd: c.int, path: cstring, status: ^stat) -> c.int {
syscall_fstatat :: #force_inline proc "contextless" (fd: c.int, path: cstring, status: ^stat) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fstatat), uintptr(fd), transmute(uintptr)path, uintptr(status))
}
syscall_link :: #force_inline proc(path: cstring, to_link: cstring) -> c.int {
syscall_link :: #force_inline proc "contextless" (path: cstring, to_link: cstring) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.link), transmute(uintptr)path, transmute(uintptr)to_link)
}
syscall_linkat :: #force_inline proc(fd: c.int, path: cstring, fd2: c.int, to_link: cstring) -> c.int {
syscall_linkat :: #force_inline proc "contextless" (fd: c.int, path: cstring, fd2: c.int, to_link: cstring) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.linkat), uintptr(fd), transmute(uintptr)path, uintptr(fd2), transmute(uintptr)to_link)
}
syscall_readlink :: #force_inline proc(path: cstring, buf: ^u8, buf_size: u64) -> i64 {
syscall_readlink :: #force_inline proc "contextless" (path: cstring, buf: ^u8, buf_size: u64) -> i64 {
return cast(i64)intrinsics.syscall(unix_offset_syscall(.readlink), transmute(uintptr)path, uintptr(buf), uintptr(buf_size))
}
syscall_readlinkat :: #force_inline proc(fd: c.int, path: cstring, buf: ^u8, buf_size: u64) -> i64 {
syscall_readlinkat :: #force_inline proc "contextless" (fd: c.int, path: cstring, buf: ^u8, buf_size: u64) -> i64 {
return cast(i64)intrinsics.syscall(unix_offset_syscall(.readlinkat), uintptr(fd), transmute(uintptr)path, uintptr(buf), uintptr(buf_size))
}
syscall_access :: #force_inline proc(path: cstring, mode: c.int) -> c.int {
syscall_access :: #force_inline proc "contextless" (path: cstring, mode: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.access), transmute(uintptr)path, uintptr(mode))
}
syscall_faccessat :: #force_inline proc(fd: c.int, path: cstring, mode: c.int, flag: c.int) -> c.int {
syscall_faccessat :: #force_inline proc "contextless" (fd: c.int, path: cstring, mode: c.int, flag: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.faccessat), uintptr(fd), transmute(uintptr)path, uintptr(mode), uintptr(flag))
}
syscall_getdirentries :: #force_inline proc(fd: c.int, buf: ^u8, nbytes: c.int, base_pointer: ^u32) -> c.int {
syscall_getdirentries :: #force_inline proc "contextless" (fd: c.int, buf: ^u8, nbytes: c.int, base_pointer: ^u32) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getdirentries), uintptr(fd), uintptr(buf), uintptr(nbytes), uintptr(base_pointer))
}
syscall_truncate :: #force_inline proc (path: cstring, length: off_t) -> c.int {
syscall_truncate :: #force_inline proc "contextless" (path: cstring, length: off_t) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.truncate), transmute(uintptr)path, uintptr(length))
}
syscall_ftruncate :: #force_inline proc (fd: c.int, length: off_t) -> c.int {
syscall_ftruncate :: #force_inline proc "contextless" (fd: c.int, length: off_t) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.ftruncate), uintptr(fd), uintptr(length))
}
syscall_sysctl :: #force_inline proc (name: ^c.int, namelen: c.uint, oldp: rawptr, oldlenp: ^i64, newp: ^i8, newlen: i64) -> c.int {
syscall_sysctl :: #force_inline proc "contextless" (name: ^c.int, namelen: c.uint, oldp: rawptr, oldlenp: ^i64, newp: ^i8, newlen: i64) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.sysctl), uintptr(name), uintptr(namelen), uintptr(oldp), uintptr(oldlenp), uintptr(newp), uintptr(newlen))
}
syscall_copyfile :: #force_inline proc(from: cstring, to: cstring, state: rawptr, flags: u32) -> c.int {
syscall_copyfile :: #force_inline proc "contextless" (from: cstring, to: cstring, state: rawptr, flags: u32) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.copyfile), transmute(uintptr)from, transmute(uintptr)to, uintptr(state), uintptr(flags))
}
// think about this? last arg should be more than one
syscall_fcntl :: #force_inline proc(fd: c.int, cmd: c.int, other: rawptr) -> c.int {
syscall_fcntl :: #force_inline proc "contextless" (fd: c.int, cmd: c.int, other: rawptr) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fsctl), uintptr(fd), uintptr(cmd), uintptr(other))
}
syscall_exit :: #force_inline proc(code: c.int) {
syscall_exit :: #force_inline proc "contextless" (code: c.int) {
intrinsics.syscall(unix_offset_syscall(.exit), uintptr(code))
}
syscall_kill :: #force_inline proc(pid: pid_t, sig: c.int) -> c.int {
syscall_kill :: #force_inline proc "contextless" (pid: pid_t, sig: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.kill), uintptr(pid), uintptr(sig))
}
syscall_dup :: #force_inline proc(fd: c.int) -> c.int {
syscall_dup :: #force_inline proc "contextless" (fd: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.dup), uintptr(fd))
}
syscall_execve :: #force_inline proc(path: cstring, argv: [^]cstring, env: [^]cstring) -> c.int {
syscall_execve :: #force_inline proc "contextless" (path: cstring, argv: [^]cstring, env: [^]cstring) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.execve), transmute(uintptr)path, transmute(uintptr)argv, transmute(uintptr)env)
}
syscall_munmap :: #force_inline proc(addr: rawptr, len: u64) -> c.int {
syscall_munmap :: #force_inline proc "contextless" (addr: rawptr, len: u64) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.mmap), uintptr(addr), uintptr(len))
}
syscall_mmap :: #force_inline proc(addr: ^u8, len: u64, port: c.int, flags: c.int, fd: int, offset: off_t) -> ^u8 {
syscall_mmap :: #force_inline proc "contextless" (addr: ^u8, len: u64, port: c.int, flags: c.int, fd: int, offset: off_t) -> ^u8 {
return cast(^u8)intrinsics.syscall(unix_offset_syscall(.mmap), uintptr(addr), uintptr(len), uintptr(port), uintptr(flags), uintptr(fd), uintptr(offset))
}
syscall_flock :: #force_inline proc(fd: c.int, operation: c.int) -> c.int {
syscall_flock :: #force_inline proc "contextless" (fd: c.int, operation: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.flock), uintptr(fd), uintptr(operation))
}
syscall_utimes :: #force_inline proc(path: cstring, times: ^timeval) -> c.int {
syscall_utimes :: #force_inline proc "contextless" (path: cstring, times: ^timeval) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.utimes), transmute(uintptr)path, uintptr(times))
}
syscall_futimes :: #force_inline proc(fd: c.int, times: ^timeval) -> c.int {
syscall_futimes :: #force_inline proc "contextless" (fd: c.int, times: ^timeval) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.futimes), uintptr(fd), uintptr(times))
}
syscall_adjtime :: #force_inline proc(delta: ^timeval, old_delta: ^timeval) -> c.int {
syscall_adjtime :: #force_inline proc "contextless" (delta: ^timeval, old_delta: ^timeval) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.adjtime), uintptr(delta), uintptr(old_delta))
}
syscall_sysctlbyname :: #force_inline proc(name: cstring, oldp: rawptr, oldlenp: ^i64, newp: rawptr, newlen: i64) -> c.int {
syscall_sysctlbyname :: #force_inline proc "contextless" (name: cstring, oldp: rawptr, oldlenp: ^i64, newp: rawptr, newlen: i64) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.sysctlbyname), transmute(uintptr)name, uintptr(oldp), uintptr(oldlenp), uintptr(newp), uintptr(newlen))
}
syscall_proc_info :: #force_inline proc(num: c.int, pid: u32, flavor: c.int, arg: u64, buffer: rawptr, buffer_size: c.int) -> c.int {
syscall_proc_info :: #force_inline proc "contextless" (num: c.int, pid: u32, flavor: c.int, arg: u64, buffer: rawptr, buffer_size: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.proc_info), uintptr(num), uintptr(pid), uintptr(flavor), uintptr(arg), uintptr(buffer), uintptr(buffer_size))
}
syscall_openat :: #force_inline proc(fd: int, path: cstring, oflag: u32, mode: u32) -> c.int {
syscall_openat :: #force_inline proc "contextless" (fd: int, path: cstring, oflag: u32, mode: u32) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.openat), uintptr(fd), transmute(uintptr)path, uintptr(oflag), uintptr(mode))
}
syscall_getentropy :: #force_inline proc(buf: [^]u8, buflen: u64) -> c.int {
syscall_getentropy :: #force_inline proc "contextless" (buf: [^]u8, buflen: u64) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(buf), uintptr(buflen))
}
syscall_pipe :: #force_inline proc(fds: [^]c.int) -> c.int {
syscall_pipe :: #force_inline proc "contextless" (fds: [^]c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(&fds[0]), uintptr(&fds[1]))
}
syscall_chdir :: #force_inline proc(path: cstring) -> c.int {
syscall_chdir :: #force_inline proc "contextless" (path: cstring) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), transmute(uintptr)path)
}
syscall_fchdir :: #force_inline proc(fd: c.int, path: cstring) -> c.int {
syscall_fchdir :: #force_inline proc "contextless" (fd: c.int, path: cstring) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(fd), transmute(uintptr)path)
}
}
+5
View File
@@ -4,6 +4,7 @@ package sysinfo
import sys "core:sys/unix"
import "core:strconv"
import "core:strings"
import "core:runtime"
@(private)
version_string_buf: [1024]u8
@@ -41,6 +42,8 @@ init_os_version :: proc () {
major_ok, minor_ok, patch_ok: bool
tmp := runtime.default_temp_allocator_temp_begin()
triplet := strings.split(string(cstring(&version_bits[0])), ".", context.temp_allocator)
if len(triplet) != 3 {
have_kernel_version = false
@@ -54,6 +57,8 @@ init_os_version :: proc () {
}
}
runtime.default_temp_allocator_temp_end(tmp)
if !have_kernel_version {
// We don't know the kernel version, but we do know the build
strings.write_string(&b, "macOS Unknown (build ")
+3
View File
@@ -4,6 +4,7 @@ package sysinfo
import sys "core:sys/unix"
import "core:strings"
import "core:strconv"
import "core:runtime"
@(private)
version_string_buf: [1024]u8
@@ -47,6 +48,8 @@ init_os_version :: proc () {
return
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Parse kernel version
release := string(cstring(raw_data(kernel_version_buf[:])))
version_bits := strings.split_n(release, "-", 2, context.temp_allocator)
+3
View File
@@ -4,6 +4,7 @@ package sysinfo
import "core:c"
import sys "core:sys/unix"
import "core:intrinsics"
import "core:runtime"
import "core:os"
import "core:strings"
import "core:strconv"
@@ -69,6 +70,8 @@ init_os_version :: proc () {
l := strings.builder_len(b)
strings.write_string(&b, string(cstring(&uts.release[0])))
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Parse kernel version, as substrings of the version info in `version_string_buf`
version_bits := strings.split_n(strings.to_string(b)[l:], "-", 2, context.temp_allocator)
if len(version_bits) > 1 {
+4 -1
View File
@@ -4,6 +4,7 @@ package sysinfo
import sys "core:sys/unix"
import "core:strings"
import "core:strconv"
import "core:runtime"
@(private)
version_string_buf: [1024]u8
@@ -32,7 +33,9 @@ init_os_version :: proc () {
version := string(cstring(raw_data(kernel_version_buf[:])))
strings.write_string(&b, version)
// // Parse kernel version
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Parse kernel version
triplet := strings.split(version, ".", context.temp_allocator)
if len(triplet) == 2 {
major, major_ok := strconv.parse_int(triplet[0])
+3
View File
@@ -7,6 +7,7 @@ import "core:strings"
import "core:unicode/utf16"
import "core:fmt"
import "core:runtime"
@(private)
version_string_buf: [1024]u8
@@ -314,6 +315,8 @@ read_reg :: proc(hkey: sys.HKEY, subkey, val: string, $T: typeid) -> (res: T, ok
return {}, false
}
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
key_name_wide := make([]u16, BUF_SIZE, context.temp_allocator)
val_name_wide := make([]u16, BUF_SIZE, context.temp_allocator)
+181 -11
View File
@@ -1563,15 +1563,167 @@ MADV_WIPEONFORK :: 18
MADV_KEEPONFORK :: 19
MADV_HWPOISON :: 100
// pipe2 flags
O_CLOEXEC :: 0o2000000
// perf event data
Perf_Sample :: struct #raw_union {
period: u64,
frequency: u64,
}
Perf_Wakeup :: struct #raw_union {
events: u32,
watermark: u32,
}
Perf_Field1 :: struct #raw_union {
breakpoint_addr: u64,
kprobe_func: u64,
uprobe_path: u64,
config1: u64,
}
Perf_Field2 :: struct #raw_union {
breakpoint_len: u64,
kprobe_addr: u64,
uprobe_offset: u64,
config2: u64,
}
Perf_Event_Attr :: struct #packed {
type: u32,
size: u32,
config: u64,
sample: Perf_Sample,
sample_type: u64,
read_format: u64,
flags: Perf_Flags,
wakeup: Perf_Wakeup,
breakpoint_type: u32,
field1: Perf_Field1,
field2: Perf_Field2,
branch_sample_type: u64,
sample_regs_user: u64,
sample_stack_user: u32,
clock_id: i32,
sample_regs_intr: u64,
aux_watermark: u32,
sample_max_stack: u16,
_padding: u16,
}
Perf_Event_Flags :: distinct bit_set[Perf_Event_Flag; u64]
Perf_Event_Flag :: enum u64 {
Bit0 = 0,
Bit0_Is_Deprecated = 1,
User_Rdpmc = 2,
User_Time = 3,
User_Time_Zero = 4,
User_Time_Short = 5,
}
Perf_Capabilities :: struct #raw_union {
capabilities: u64,
flags: Perf_Event_Flags,
}
Perf_Event_mmap_Page :: struct #packed {
version: u32,
compat_version: u32,
lock: u32,
index: u32,
offset: i64,
time_enabled: u64,
time_running: u64,
cap: Perf_Capabilities,
pmc_width: u16,
time_shift: u16,
time_mult: u32,
time_offset: u64,
time_zero: u64,
size: u32,
reserved1: u32,
time_cycles: u64,
time_mask: u64,
reserved2: [116*8]u8,
data_head: u64,
data_tail: u64,
data_offset: u64,
data_size: u64,
aux_head: u64,
aux_tail: u64,
aux_offset: u64,
aux_size: u64,
}
Perf_Type_Id :: enum u32 {
Hardware = 0,
Software = 1,
Tracepoint = 2,
HW_Cache = 3,
Raw = 4,
Breakpoint = 5,
}
Perf_Hardware_Id :: enum u64 {
CPU_Cycles = 0,
Instructions = 1,
Cache_References = 2,
Cache_Misses = 3,
Branch_Instructions = 4,
Branch_Misses = 5,
Bus_Cycles = 6,
Stalled_Cycles_Frontend = 7,
Stalled_Cycles_Backend = 8,
Ref_CPU_Cycles = 9,
}
Perf_Flags :: distinct bit_set[Perf_Flag; u64]
Perf_Flag :: enum u64 {
Disabled = 0,
Inherit = 1,
Pinned = 2,
Exclusive = 3,
Exclude_User = 4,
Exclude_Kernel = 5,
Exclude_HV = 6,
Exclude_Idle = 7,
mmap = 8,
Comm = 9,
Freq = 10,
Inherit_Stat = 11,
Enable_On_Exec = 12,
Task = 13,
Watermark = 14,
Precise_IP_0 = 15,
Precise_IP_1 = 16,
mmap_Data = 17,
Sample_Id_All = 18,
Exclude_Host = 19,
Exclude_Guest = 20,
Exclude_Callchain_Kernel = 21,
Exclude_Callchain_User = 22,
mmap2 = 23,
Comm_Exec = 24,
Use_Clockid = 25,
Context_Switch = 26,
Write_Backward = 27,
Namespaces = 28,
KSymbol = 29,
BPF_Event = 30,
Aux_Output = 31,
CGroup = 32,
Text_Poke = 33,
Build_Id = 34,
Inherit_Thread = 35,
Remove_On_Exec = 36,
Sigtrap = 37,
}
sys_gettid :: proc "contextless" () -> int {
return cast(int)intrinsics.syscall(SYS_gettid)
return int(intrinsics.syscall(SYS_gettid))
}
sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int {
return cast(int)intrinsics.syscall(SYS_getrandom, uintptr(buf), uintptr(buflen), uintptr(flags))
sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: uint, flags: int) -> int {
return int(intrinsics.syscall(SYS_getrandom, uintptr(buf), uintptr(buflen), uintptr(flags)))
}
sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int {
sys_open :: proc "contextless" (path: cstring, flags: int, mode: uint = 0o000) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
} else { // NOTE: arm64 does not have open
@@ -1579,7 +1731,7 @@ sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) ->
}
}
sys_openat :: proc "contextless" (dfd: int, path: cstring, flags: int, mode: int = 0o000) -> int {
sys_openat :: proc "contextless" (dfd: int, path: cstring, flags: int, mode: uint = 0o000) -> int {
return int(intrinsics.syscall(SYS_openat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
}
@@ -1691,7 +1843,7 @@ sys_fchdir :: proc "contextless" (fd: int) -> int {
return int(intrinsics.syscall(SYS_fchdir, uintptr(fd)))
}
sys_chmod :: proc "contextless" (path: cstring, mode: int) -> int {
sys_chmod :: proc "contextless" (path: cstring, mode: uint) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_chmod, uintptr(rawptr(path)), uintptr(mode)))
} else { // NOTE: arm64 does not have chmod
@@ -1699,7 +1851,7 @@ sys_chmod :: proc "contextless" (path: cstring, mode: int) -> int {
}
}
sys_fchmod :: proc "contextless" (fd: int, mode: int) -> int {
sys_fchmod :: proc "contextless" (fd: int, mode: uint) -> int {
return int(intrinsics.syscall(SYS_fchmod, uintptr(fd), uintptr(mode)))
}
@@ -1759,7 +1911,7 @@ sys_rmdir :: proc "contextless" (path: cstring) -> int {
}
}
sys_mkdir :: proc "contextless" (path: cstring, mode: int) -> int {
sys_mkdir :: proc "contextless" (path: cstring, mode: uint) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
} else { // NOTE: arm64 does not have mkdir
@@ -1767,11 +1919,11 @@ sys_mkdir :: proc "contextless" (path: cstring, mode: int) -> int {
}
}
sys_mkdirat :: proc "contextless" (dfd: int, path: cstring, mode: int) -> int {
sys_mkdirat :: proc "contextless" (dfd: int, path: cstring, mode: uint) -> int {
return int(intrinsics.syscall(SYS_mkdirat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode)))
}
sys_mknod :: proc "contextless" (path: cstring, mode: int, dev: int) -> int {
sys_mknod :: proc "contextless" (path: cstring, mode: uint, dev: int) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_mknod, uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
} else { // NOTE: arm64 does not have mknod
@@ -1779,7 +1931,7 @@ sys_mknod :: proc "contextless" (path: cstring, mode: int, dev: int) -> int {
}
}
sys_mknodat :: proc "contextless" (dfd: int, path: cstring, mode: int, dev: int) -> int {
sys_mknodat :: proc "contextless" (dfd: int, path: cstring, mode: uint, dev: int) -> int {
return int(intrinsics.syscall(SYS_mknodat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
}
@@ -1818,6 +1970,16 @@ sys_fork :: proc "contextless" () -> int {
return int(intrinsics.syscall(SYS_clone, SIGCHLD))
}
}
sys_pipe2 :: proc "contextless" (fds: rawptr, flags: int) -> int {
return int(intrinsics.syscall(SYS_pipe2, uintptr(fds), uintptr(flags)))
}
sys_dup2 :: proc "contextless" (oldfd: int, newfd: int) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_dup2, uintptr(oldfd), uintptr(newfd)))
} else {
return int(intrinsics.syscall(SYS_dup3, uintptr(oldfd), uintptr(newfd), 0))
}
}
sys_mmap :: proc "contextless" (addr: rawptr, length: uint, prot, flags, fd: int, offset: uintptr) -> int {
return int(intrinsics.syscall(SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset))
@@ -1846,6 +2008,14 @@ sys_utimensat :: proc "contextless" (dfd: int, path: cstring, times: rawptr, fla
return int(intrinsics.syscall(SYS_utimensat, uintptr(dfd), uintptr(rawptr(path)), uintptr(times), uintptr(flags)))
}
sys_perf_event_open :: proc "contextless" (event_attr: rawptr, pid: i32, cpu: i32, group_fd: i32, flags: u32) -> int {
return int(intrinsics.syscall(SYS_perf_event_open, uintptr(event_attr), uintptr(pid), uintptr(cpu), uintptr(group_fd), uintptr(flags)))
}
sys_personality :: proc(persona: u64) -> int {
return int(intrinsics.syscall(SYS_personality, uintptr(persona)))
}
get_errno :: proc "contextless" (res: int) -> i32 {
if res < 0 && res > -4096 {
return i32(-res)
+38
View File
@@ -3,7 +3,45 @@ package sys_windows
foreign import dwmapi "system:Dwmapi.lib"
DWMWINDOWATTRIBUTE :: enum {
DWMWA_NCRENDERING_ENABLED,
DWMWA_NCRENDERING_POLICY,
DWMWA_TRANSITIONS_FORCEDISABLED,
DWMWA_ALLOW_NCPAINT,
DWMWA_CAPTION_BUTTON_BOUNDS,
DWMWA_NONCLIENT_RTL_LAYOUT,
DWMWA_FORCE_ICONIC_REPRESENTATION,
DWMWA_FLIP3D_POLICY,
DWMWA_EXTENDED_FRAME_BOUNDS,
DWMWA_HAS_ICONIC_BITMAP,
DWMWA_DISALLOW_PEEK,
DWMWA_EXCLUDED_FROM_PEEK,
DWMWA_CLOAK,
DWMWA_CLOAKED,
DWMWA_FREEZE_REPRESENTATION,
DWMWA_PASSIVE_UPDATE_MODE,
DWMWA_USE_HOSTBACKDROPBRUSH,
DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
DWMWA_WINDOW_CORNER_PREFERENCE = 33,
DWMWA_BORDER_COLOR,
DWMWA_CAPTION_COLOR,
DWMWA_TEXT_COLOR,
DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,
DWMWA_SYSTEMBACKDROP_TYPE,
DWMWA_LAST,
}
DWMNCRENDERINGPOLICY :: enum {
DWMNCRP_USEWINDOWSTYLE,
DWMNCRP_DISABLED,
DWMNCRP_ENABLED,
DWMNCRP_LAST,
}
@(default_calling_convention="stdcall")
foreign dwmapi {
DwmFlush :: proc() -> HRESULT ---
DwmIsCompositionEnabled :: proc(pfEnabled: ^BOOL) -> HRESULT ---
DwmExtendFrameIntoClientArea :: proc(hWnd: HWND, pMarInset: PMARGINS) -> HRESULT ---
DwmSetWindowAttribute :: proc(hWnd: HWND, dwAttribute: DWORD, pvAttribute: LPCVOID, cbAttribute: DWORD) -> HRESULT ---
}
+3
View File
@@ -62,6 +62,7 @@ foreign gdi32 {
SetPixelFormat :: proc(hdc: HDC, format: c_int, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL ---
ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
DescribePixelFormat :: proc(hdc: HDC, iPixelFormat: c_int, nBytes: UINT, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
SwapBuffers :: proc(HDC) -> BOOL ---
SetDCBrushColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
@@ -78,6 +79,8 @@ foreign gdi32 {
TextOutW :: proc(hdc: HDC, x, y: c_int, lpString: LPCWSTR, c: c_int) -> BOOL ---
GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: c_int, psizl: LPSIZE) -> BOOL ---
GetTextMetricsW :: proc(hdc: HDC, lptm: LPTEXTMETRICW) -> BOOL ---
CreateSolidBrush :: proc(color: COLORREF) -> HBRUSH ---
}
RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF {
+58
View File
@@ -315,6 +315,13 @@ foreign kernel32 {
lpOverlapped: LPOVERLAPPED,
lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
) -> BOOL ---
FindFirstChangeNotificationW :: proc(
lpPathName: LPWSTR,
bWatchSubtree: BOOL,
dwNotifyFilter: DWORD,
) -> HANDLE ---
FindNextChangeNotification :: proc(hChangeHandle: HANDLE) -> BOOL ---
FindCloseChangeNotification :: proc(hChangeHandle: HANDLE) -> BOOL ---
InitializeSRWLock :: proc(SRWLock: ^SRWLOCK) ---
AcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) ---
@@ -363,6 +370,9 @@ foreign kernel32 {
GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL ---
FreeConsole :: proc() -> BOOL ---
GetConsoleWindow :: proc() -> HWND ---
GetConsoleScreenBufferInfo :: proc(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: PCONSOLE_SCREEN_BUFFER_INFO) -> BOOL ---
SetConsoleScreenBufferSize :: proc(hConsoleOutput: HANDLE, dwSize: COORD) -> BOOL ---
SetConsoleWindowInfo :: proc(hConsoleOutput: HANDLE, bAbsolute : BOOL, lpConsoleWindow: ^SMALL_RECT) -> BOOL ---
GetDiskFreeSpaceExW :: proc(
lpDirectoryName: LPCWSTR,
@@ -370,6 +380,8 @@ foreign kernel32 {
lpTotalNumberOfBytes: PULARGE_INTEGER,
lpTotalNumberOfFreeBytes: PULARGE_INTEGER,
) -> BOOL ---
GetLogicalProcessorInformation :: proc(buffer: ^SYSTEM_LOGICAL_PROCESSOR_INFORMATION, returnedLength: PDWORD) -> BOOL ---
}
@@ -999,3 +1011,49 @@ foreign kernel32 {
ConvertThreadToFiber :: proc(lpParameter: LPVOID) -> LPVOID ---
SwitchToFiber :: proc(lpFiber: LPVOID) ---
}
LOGICAL_PROCESSOR_RELATIONSHIP :: enum c_int {
RelationProcessorCore,
RelationNumaNode,
RelationCache,
RelationProcessorPackage,
RelationGroup,
RelationProcessorDie,
RelationNumaNodeEx,
RelationProcessorModule,
RelationAll = 0xffff,
}
PROCESSOR_CACHE_TYPE :: enum c_int {
CacheUnified,
CacheInstruction,
CacheData,
CacheTrace,
}
CACHE_DESCRIPTOR :: struct {
Level: BYTE,
Associativity: BYTE,
LineSize: WORD,
Size: DWORD,
Type: PROCESSOR_CACHE_TYPE,
}
ProcessorCore :: struct {
Flags: BYTE,
}
NumaNode :: struct {
NodeNumber: DWORD,
}
DUMMYUNIONNAME_u :: struct #raw_union {
Core: ProcessorCore,
Node: NumaNode,
Cache: CACHE_DESCRIPTOR,
Reserved: [2]ULONGLONG,
}
SYSTEM_LOGICAL_PROCESSOR_INFORMATION :: struct {
ProcessorMask: ULONG_PTR,
Relationship: LOGICAL_PROCESSOR_RELATIONSHIP,
DummyUnion: DUMMYUNIONNAME_u,
}
+33
View File
@@ -22,4 +22,37 @@ foreign shell32 {
) -> c_int ---
SHFileOperationW :: proc(lpFileOp: LPSHFILEOPSTRUCTW) -> c_int ---
SHGetFolderPathW :: proc(hwnd: HWND, csidl: c_int, hToken: HANDLE, dwFlags: DWORD, pszPath: LPWSTR) -> HRESULT ---
SHAppBarMessage :: proc(dwMessage: DWORD, pData: PAPPBARDATA) -> UINT_PTR ---
}
APPBARDATA :: struct {
cbSize: DWORD,
hWnd: HWND,
uCallbackMessage: UINT,
uEdge: UINT,
rc: RECT,
lParam: LPARAM,
}
PAPPBARDATA :: ^APPBARDATA
ABM_NEW :: 0x00000000
ABM_REMOVE :: 0x00000001
ABM_QUERYPOS :: 0x00000002
ABM_SETPOS :: 0x00000003
ABM_GETSTATE :: 0x00000004
ABM_GETTASKBARPOS :: 0x00000005
ABM_ACTIVATE :: 0x00000006
ABM_GETAUTOHIDEBAR :: 0x00000007
ABM_SETAUTOHIDEBAR :: 0x00000008
ABM_WINDOWPOSCHANGED :: 0x0000009
ABM_SETSTATE :: 0x0000000a
ABN_STATECHANGE :: 0x0000000
ABN_POSCHANGED :: 0x0000001
ABN_FULLSCREENAPP :: 0x0000002
ABN_WINDOWARRANGE :: 0x0000003
ABS_AUTOHIDE :: 0x0000001
ABS_ALWAYSONTOP :: 0x0000002
ABE_LEFT :: 0
ABE_TOP :: 1
ABE_RIGHT :: 2
ABE_BOTTOM :: 3
+38
View File
@@ -38,6 +38,7 @@ HHOOK :: distinct HANDLE
HKEY :: distinct HANDLE
HDESK :: distinct HANDLE
HFONT :: distinct HANDLE
HRGN :: distinct HANDLE
BOOL :: distinct b32
BYTE :: distinct u8
BOOLEAN :: distinct b8
@@ -1554,6 +1555,25 @@ WA_INACTIVE :: 0
WA_ACTIVE :: 1
WA_CLICKACTIVE :: 2
// Struct pointed to by WM_GETMINMAXINFO lParam
MINMAXINFO :: struct {
ptReserved: POINT,
ptMaxSize: POINT,
ptMaxPosition: POINT,
ptMinTrackSize: POINT,
ptMaxTrackSize: POINT,
}
PMINMAXINFO :: ^MINMAXINFO
LPMINMAXINFO :: PMINMAXINFO
MONITORINFO :: struct {
cbSize: DWORD,
rcMonitor: RECT,
rcWork: RECT,
dwFlags: DWORD,
}
LPMONITORINFO :: ^MONITORINFO
// SetWindowsHook() codes
WH_MIN :: -1
WH_MSGFILTER :: -1
@@ -3864,3 +3884,21 @@ COORD :: struct {
X: SHORT,
Y: SHORT,
}
SMALL_RECT :: struct {
Left: SHORT,
Top: SHORT,
Right: SHORT,
Bottom: SHORT,
}
CONSOLE_SCREEN_BUFFER_INFO :: struct {
dwSize: COORD,
dwCursorPosition: COORD,
wAttributes: WORD,
srWindow: SMALL_RECT,
dwMaximumWindowSize: COORD,
}
PCONSOLE_SCREEN_BUFFER_INFO :: ^CONSOLE_SCREEN_BUFFER_INFO
+38
View File
@@ -100,6 +100,7 @@ foreign user32 {
AdjustWindowRectExForDpi :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD, dpi: UINT) -> BOOL ---
SystemParametersInfoW :: proc(uiAction, uiParam: UINT, pvParam: PVOID, fWinIni: UINT) -> BOOL ---
GetMonitorInfoW :: proc(hMonitor: HMONITOR, lpmi: LPMONITORINFO) -> BOOL ---
GetWindowDC :: proc(hWnd: HWND) -> HDC ---
GetDC :: proc(hWnd: HWND) -> HDC ---
@@ -122,6 +123,8 @@ foreign user32 {
GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
GetKeyboardState :: proc(lpKeyState: PBYTE) -> BOOL ---
MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
@@ -202,6 +205,17 @@ foreign user32 {
GetRawInputDeviceList :: proc(pRawInputDeviceList: PRAWINPUTDEVICELIST, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
GetRegisteredRawInputDevices :: proc(pRawInputDevices: PRAWINPUTDEVICE, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
RegisterRawInputDevices :: proc(pRawInputDevices: PCRAWINPUTDEVICE, uiNumDevices: UINT, cbSize: UINT) -> BOOL ---
SetLayeredWindowAttributes :: proc(hWnd: HWND, crKey: COLORREF, bAlpha: BYTE, dwFlags: DWORD) -> BOOL ---
FillRect :: proc(hDC: HDC, lprc: ^RECT, hbr: HBRUSH) -> int ---
EqualRect :: proc(lprc1: ^RECT, lprc2: ^RECT) -> BOOL ---
GetWindowInfo :: proc(hwnd: HWND, pwi: PWINDOWINFO) -> BOOL ---
GetWindowPlacement :: proc(hWnd: HWND, lpwndpl: ^WINDOWPLACEMENT) -> BOOL ---
SetWindowRgn :: proc(hWnd: HWND, hRgn: HRGN, bRedraw: BOOL) -> int ---
CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN ---
GetSystemMetricsForDpi :: proc(nIndex: int, dpi: UINT) -> int ---
}
CreateWindowW :: #force_inline proc "stdcall" (
@@ -432,3 +446,27 @@ RI_MOUSE_BUTTON_5_DOWN :: 0x0100
RI_MOUSE_BUTTON_5_UP :: 0x0200
RI_MOUSE_WHEEL :: 0x0400
RI_MOUSE_HWHEEL :: 0x0800
WINDOWPLACEMENT :: struct {
length: UINT,
flags: UINT,
showCmd: UINT,
ptMinPosition: POINT,
ptMaxPosition: POINT,
rcNormalPosition: RECT,
rcDevice: RECT,
}
WINDOWINFO :: struct {
cbSize: DWORD,
rcWindow: RECT,
rcClient: RECT,
dwStyle: DWORD,
dwExStyle: DWORD,
dwWindowStatus: DWORD,
cxWindowBorders: UINT,
cyWindowBorders: UINT,
atomWindowType: ATOM,
wCreatorVersion: WORD,
}
PWINDOWINFO :: ^WINDOWINFO
+12
View File
@@ -0,0 +1,12 @@
// +build windows
package sys_windows
foreign import uxtheme "system:UxTheme.lib"
MARGINS :: distinct [4]int
PMARGINS :: ^MARGINS
@(default_calling_convention="stdcall")
foreign uxtheme {
IsThemeActive :: proc() -> BOOL ---
}
+959
View File
@@ -0,0 +1,959 @@
package text_match
import "core:runtime"
import "core:unicode"
import "core:unicode/utf8"
import "core:strings"
MAX_CAPTURES :: 32
Capture :: struct {
init: int,
len: int,
}
Match :: struct {
byte_start, byte_end: int,
}
Error :: enum {
OK,
OOB,
Invalid_Capture_Index,
Invalid_Pattern_Capture,
Unfinished_Capture,
Malformed_Pattern,
Rune_Error,
Match_Invalid,
}
L_ESC :: '%'
CAP_POSITION :: -2
CAP_UNFINISHED :: -1
INVALID :: -1
Match_State :: struct {
src: string,
pattern: string,
level: int,
capture: [MAX_CAPTURES]Capture,
}
match_class :: proc(c: rune, cl: rune) -> (res: bool) {
switch unicode.to_lower(cl) {
case 'a': res = is_alpha(c)
case 'c': res = is_cntrl(c)
case 'd': res = is_digit(c)
case 'g': res = is_graph(c)
case 'l': res = is_lower(c)
case 'p': res = is_punct(c)
case 's': res = is_space(c)
case 'u': res = is_upper(c)
case 'w': res = is_alnum(c)
case 'x': res = is_xdigit(c)
case: return cl == c
}
return is_lower(cl) ? res : !res
}
is_alpha :: unicode.is_alpha
is_digit :: unicode.is_digit
is_lower :: unicode.is_lower
is_upper :: unicode.is_upper
is_punct :: unicode.is_punct
is_space :: unicode.is_space
is_cntrl :: unicode.is_control
is_alnum :: proc(c: rune) -> bool {
return unicode.is_alpha(c) || unicode.is_digit(c)
}
is_graph :: proc(c: rune) -> bool {
return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || unicode.is_digit(c)
}
is_xdigit :: proc(c: rune) -> bool {
return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || unicode.is_digit(c)
}
// find the first utf8 charater and its size, return an error if the character is an error
utf8_peek :: proc(bytes: string) -> (c: rune, size: int, err: Error) {
c, size = utf8.decode_rune_in_string(bytes)
if c == utf8.RUNE_ERROR {
err = .Rune_Error
}
return
}
// find the first utf8 charater and its size and advance the index
// return an error if the character is an error
utf8_advance :: proc(bytes: string, index: ^int) -> (c: rune, err: Error) {
size: int
c, size = utf8.decode_rune_in_string(bytes[index^:])
if c == utf8.RUNE_ERROR {
err = .Rune_Error
}
index^ += size
return
}
// continuation byte?
is_cont :: proc(b: byte) -> bool {
return b & 0xc0 == 0x80
}
utf8_prev :: proc(bytes: string, a, b: int) -> int {
b := b
for a < b && is_cont(bytes[b - 1]) {
b -= 1
}
return a < b ? b - 1 : a
}
utf8_next :: proc(bytes: string, a: int) -> int {
a := a
b := len(bytes)
for a < b - 1 && is_cont(bytes[a + 1]) {
a += 1
}
return a < b ? a + 1 : b
}
check_capture :: proc(ms: ^Match_State, l: rune) -> (int, Error) {
l := int(l - '1')
if l < 0 || l >= ms.level || ms.capture[l].len == CAP_UNFINISHED {
return 0, .Invalid_Capture_Index
}
return l, .OK
}
capture_to_close :: proc(ms: ^Match_State) -> (int, Error) {
level := ms.level - 1
for level >= 0 {
if ms.capture[level].len == CAP_UNFINISHED {
return level, .OK
}
level -= 1
}
return 0, .Invalid_Pattern_Capture
}
class_end :: proc(ms: ^Match_State, p: int) -> (step: int, err: Error) {
step = p
ch := utf8_advance(ms.pattern, &step) or_return
switch ch {
case L_ESC:
if step == len(ms.pattern) {
err = .Malformed_Pattern
return
}
utf8_advance(ms.pattern, &step) or_return
case '[':
// fine with step by 1
if step + 1 < len(ms.pattern) && ms.pattern[step] == '^' {
step += 1
}
// run till end is reached
for {
if step == len(ms.pattern) {
err = .Malformed_Pattern
return
}
if ms.pattern[step] == ']' {
break
}
// dont care about utf8 here
step += 1
if step < len(ms.pattern) && ms.pattern[step] == L_ESC {
// skip escapes like '%'
step += 1
}
}
// advance last time
step += 1
}
return
}
match_bracket_class :: proc(ms: ^Match_State, c: rune, p, ec: int) -> (sig: bool, err: Error) {
sig = true
p := p
if ms.pattern[p + 1] == '^' {
p += 1
sig = false
}
// while inside of class range
for p < ec {
char := utf8_advance(ms.pattern, &p) or_return
// e.g. %a
if char == L_ESC {
next := utf8_advance(ms.pattern, &p) or_return
if match_class(c, next) {
return
}
} else {
next, next_size := utf8_peek(ms.pattern[p:]) or_return
// TODO test case for [a-???] where ??? is missing
if next == '-' && p + next_size < len(ms.pattern) {
// advance 2 codepoints
p += next_size
last := utf8_advance(ms.pattern, &p) or_return
if char <= c && c <= last {
return
}
} else if char == c {
return
}
}
}
sig = !sig
return
}
single_match :: proc(ms: ^Match_State, s, p, ep: int) -> (matched: bool, schar_size: int, err: Error) {
if s >= len(ms.src) {
return
}
pchar, psize := utf8_peek(ms.pattern[p:]) or_return
schar, ssize := utf8_peek(ms.src[s:]) or_return
schar_size = ssize
switch pchar {
case '.': matched = true
case L_ESC:
pchar_next, _ := utf8_peek(ms.pattern[p + psize:]) or_return
matched = match_class(schar, pchar_next)
case '[': matched = match_bracket_class(ms, schar, p, ep - 1) or_return
case: matched = schar == pchar
}
return
}
match_balance :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
if p >= len(ms.pattern) - 1 {
return INVALID, .Invalid_Pattern_Capture
}
schar, ssize := utf8_peek(ms.src[s:]) or_return
pchar, psize := utf8_peek(ms.pattern[p:]) or_return
// skip until the src and pattern match
if schar != pchar {
return INVALID, .OK
}
s_begin := s
cont := 1
s := s + ssize
begin := pchar
end, _ := utf8_peek(ms.pattern[p + psize:]) or_return
for s < len(ms.src) {
ch := utf8_advance(ms.src, &s) or_return
switch ch{
case end:
cont -= 1
if cont == 0 {
return s, .OK
}
case begin:
cont += 1
}
}
return INVALID, .OK
}
max_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
m := s
// count up matches
for {
matched, size := single_match(ms, m, p, ep) or_return
if !matched {
break
}
m += size
}
for s <= m {
result := match(ms, m, ep + 1) or_return
if result != INVALID {
return result, .OK
}
if s == m {
break
}
m = utf8_prev(ms.src, s, m)
}
return INVALID, .OK
}
min_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
s := s
for {
result := match(ms, s, ep + 1) or_return
if result != INVALID {
return result, .OK
} else {
// TODO receive next step maybe?
matched, rune_size := single_match(ms, s, p, ep) or_return
if matched {
s += rune_size
} else {
return INVALID, .OK
}
}
}
}
start_capture :: proc(ms: ^Match_State, s, p, what: int) -> (res: int, err: Error) {
level := ms.level
ms.capture[level].init = s
ms.capture[level].len = what
ms.level += 1
res = match(ms, s, p) or_return
if res == INVALID {
ms.level -= 1
}
return
}
end_capture :: proc(ms: ^Match_State, s, p: int) -> (res: int, err: Error) {
l := capture_to_close(ms) or_return
// TODO double check, could do string as int index
ms.capture[l].len = s - ms.capture[l].init
res = match(ms, s, p) or_return
if res == INVALID {
ms.capture[l].len = CAP_UNFINISHED
}
return
}
match_capture :: proc(ms: ^Match_State, s: int, char: rune) -> (res: int, err: Error) {
index := check_capture(ms, char) or_return
length := ms.capture[index].len
if len(ms.src) - s >= length {
return s + length, .OK
}
return INVALID, .OK
}
match :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
s := s
p := p
if p == len(ms.pattern) {
return s, .OK
}
// NOTE we can walk by ascii steps if we know the characters are ascii
char, _ := utf8_peek(ms.pattern[p:]) or_return
switch char {
case '(':
if p + 1 < len(ms.pattern) && ms.pattern[p + 1] == ')' {
s = start_capture(ms, s, p + 2, CAP_POSITION) or_return
} else {
s = start_capture(ms, s, p + 1, CAP_UNFINISHED) or_return
}
case ')':
s = end_capture(ms, s, p + 1) or_return
case '$':
if p + 1 != len(ms.pattern) {
return match_default(ms, s, p)
}
if len(ms.src) != s {
s = INVALID
}
case L_ESC:
// stop short patterns like "%" only
if p + 1 >= len(ms.pattern) {
err = .OOB
return
}
switch ms.pattern[p + 1] {
// balanced string
case 'b':
s = match_balance(ms, s, p + 2) or_return
if s != INVALID {
// eg after %b()
return match(ms, s, p + 4)
}
// frontier
case 'f':
p += 2
if ms.pattern[p] != '[' {
return INVALID, .Invalid_Pattern_Capture
}
ep := class_end(ms, p) or_return
previous, current: rune
// get previous
if s != 0 {
temp := utf8_prev(ms.src, 0, s)
previous, _ = utf8_peek(ms.src[temp:]) or_return
}
// get current
if s != len(ms.src) {
current, _ = utf8_peek(ms.src[s:]) or_return
}
m1 := match_bracket_class(ms, previous, p, ep - 1) or_return
m2 := match_bracket_class(ms, current, p, ep - 1) or_return
if !m1 && m2 {
return match(ms, s, ep)
}
s = INVALID
// capture group
case '0'..<'9':
s = match_capture(ms, s, rune(ms.pattern[p + 1])) or_return
if s != INVALID {
return match(ms, s, p + 2)
}
case: return match_default(ms, s, p)
}
case:
return match_default(ms, s, p)
}
return s, .OK
}
match_default :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
s := s
ep := class_end(ms, p) or_return
single_matched, ssize := single_match(ms, s, p, ep) or_return
if !single_matched {
epc := ep < len(ms.pattern) ? ms.pattern[ep] : 0
switch epc {
case '*', '?', '-': return match(ms, s, ep + 1)
case: s = INVALID
}
} else {
epc := ep < len(ms.pattern) ? ms.pattern[ep] : 0
switch epc {
case '?':
result := match(ms, s + ssize, ep + 1) or_return
if result != INVALID {
s = result
} else {
return match(ms, s, ep + 1)
}
case '+': s = max_expand(ms, s + ssize, p, ep) or_return
case '*': s = max_expand(ms, s, p, ep) or_return
case '-': s = min_expand(ms, s, p, ep) or_return
case: return match(ms, s + ssize, ep)
}
}
return s, .OK
}
push_onecapture :: proc(ms: ^Match_State, i: int, s: int, e: int, matches: []Match) -> (err: Error) {
if i >= ms.level {
if i == 0 {
matches[0] = { 0, e - s }
} else {
err = .Invalid_Capture_Index
}
} else {
init := ms.capture[i].init
length := ms.capture[i].len
switch length {
case CAP_UNFINISHED: err = .Unfinished_Capture
case CAP_POSITION: matches[i] = { init, init + 1 }
case: matches[i] = { init, init + length }
}
}
return
}
push_captures :: proc(
ms: ^Match_State,
s: int,
e: int,
matches: []Match,
) -> (nlevels: int, err: Error) {
nlevels = 1 if ms.level == 0 && s != -1 else ms.level
for i in 0..<nlevels {
push_onecapture(ms, i, s, e, matches) or_return
}
return
}
// SPECIALS := "^$*+?.([%-"
// all special characters inside a small ascii array
SPECIALS_TABLE := [256]bool {
'^' = true,
'$' = true,
'*' = true,
'+' = true,
'?' = true,
'.' = true,
'(' = true,
'[' = true,
'%' = true,
'-' = true,
}
// helper call to quick search for special characters
index_special :: proc(text: string) -> int {
for i in 0..<len(text) {
if SPECIALS_TABLE[text[i]] {
return i
}
}
return -1
}
lmem_find :: proc(s1, s2: string) -> int {
l1 := len(s1)
l2 := len(s2)
if l2 == 0 {
return 0
} else if l2 > l1 {
return -1
} else {
init := strings.index_byte(s1, s2[0])
end := init + l2
for end <= l1 && init != -1 {
init += 1
if s1[init - 1:end] == s2 {
return init - 1
} else {
next := strings.index_byte(s1[init:], s2[0])
if next == -1 {
return -1
} else {
init = init + next
end = init + l2
}
}
}
}
return -1
}
// find a pattern with in a haystack with an offset
// allow_memfind will speed up simple searches
find_aux :: proc(
haystack: string,
pattern: string,
offset: int,
allow_memfind: bool,
matches: ^[MAX_CAPTURES]Match,
) -> (captures: int, err: Error) {
s := offset
p := 0
specials_idx := index_special(pattern)
if allow_memfind && specials_idx == -1 {
if index := lmem_find(haystack[s:], pattern); index != -1 {
matches[0] = { index + s, index + s + len(pattern) }
captures = 1
return
} else {
return
}
}
pattern := pattern
anchor: bool
if len(pattern) > 0 && pattern[0] == '^' {
anchor = true
pattern = pattern[1:]
}
ms := Match_State {
src = haystack,
pattern = pattern,
}
for {
res := match(&ms, s, p) or_return
if res != INVALID {
// disallow non advancing match
if s == res {
err = .Match_Invalid
}
// NOTE(Skytrias): first result is reserved for a full match
matches[0] = { s, res }
// rest are the actual captures
captures = push_captures(&ms, -1, -1, matches[1:]) or_return
captures += 1
return
}
s += 1
if !(s < len(ms.src) && !anchor) {
break
}
}
return
}
// iterative matching which returns the 0th/1st match
// rest has to be used from captures
gmatch :: proc(
haystack: ^string,
pattern: string,
captures: ^[MAX_CAPTURES]Match,
) -> (res: string, ok: bool) {
if len(haystack) > 0 {
length, err := find_aux(haystack^, pattern, 0, false, captures)
if length != 0 && err == .OK {
ok = true
first := length > 1 ? 1 : 0
cap := captures[first]
res = haystack[cap.byte_start:cap.byte_end]
haystack^ = haystack[cap.byte_end:]
}
}
return
}
// gsub with builder, replace patterns found with the replace content
gsub_builder :: proc(
builder: ^strings.Builder,
haystack: string,
pattern: string,
replace: string,
) -> string {
// find matches
captures: [MAX_CAPTURES]Match
haystack := haystack
for {
length, err := find_aux(haystack, pattern, 0, false, &captures)
// done
if length == 0 {
break
}
if err != .OK {
return {}
}
cap := captures[0]
// write front till capture
strings.write_string(builder, haystack[:cap.byte_start])
// write replacements
strings.write_string(builder, replace)
// advance string till end
haystack = haystack[cap.byte_end:]
}
strings.write_string(builder, haystack[:])
return strings.to_string(builder^)
}
// uses temp builder to build initial string - then allocates the result
gsub_allocator :: proc(
haystack: string,
pattern: string,
replace: string,
allocator := context.allocator,
) -> string {
builder := strings.builder_make(0, 256, context.temp_allocator)
return gsub_builder(&builder, haystack, pattern, replace)
}
Gsub_Proc :: proc(
// optional passed data
data: rawptr,
// word match found
word: string,
// current haystack for found captures
haystack: string,
// found captures - empty for no captures
captures: []Match,
)
// call a procedure on every match in the haystack
gsub_with :: proc(
haystack: string,
pattern: string,
data: rawptr,
call: Gsub_Proc,
) {
// find matches
captures: [MAX_CAPTURES]Match
haystack := haystack
for {
length, err := find_aux(haystack, pattern, 0, false, &captures)
// done
if length == 0 || err != .OK {
break
}
cap := captures[0]
word := haystack[cap.byte_start:cap.byte_end]
call(data, word, haystack, captures[1:length])
// advance string till end
haystack = haystack[cap.byte_end:]
}
}
gsub :: proc { gsub_builder, gsub_allocator }
// iterative find with zeroth capture only
gfind :: proc(
haystack: ^string,
pattern: string,
captures: ^[MAX_CAPTURES]Match,
) -> (res: string, ok: bool) {
if len(haystack) > 0 {
length, err := find_aux(haystack^, pattern, 0, true, captures)
if length != 0 && err == .OK {
ok = true
cap := captures[0]
res = haystack[cap.byte_start:cap.byte_end]
haystack^ = haystack[cap.byte_end:]
}
}
return
}
// rebuilds a pattern into a case insensitive pattern
pattern_case_insensitive_builder :: proc(
builder: ^strings.Builder,
pattern: string,
) -> (res: string) {
p := pattern
last_percent: bool
for len(p) > 0 {
char, size := utf8.decode_rune_in_string(p)
if unicode.is_alpha(char) && !last_percent {
// write character class in manually
strings.write_byte(builder, '[')
strings.write_rune(builder, unicode.to_lower(char))
strings.write_rune(builder, unicode.to_upper(char))
strings.write_byte(builder, ']')
} else {
strings.write_rune(builder, char)
}
last_percent = char == L_ESC
p = p[size:]
}
return strings.to_string(builder^)
}
pattern_case_insensitive_allocator :: proc(
pattern: string,
cap: int = 256,
allocator := context.allocator,
) -> (res: string) {
builder := strings.builder_make(0, cap, context.temp_allocator)
return pattern_case_insensitive_builder(&builder, pattern)
}
pattern_case_insensitive :: proc { pattern_case_insensitive_builder, pattern_case_insensitive_allocator }
// Matcher helper struct that stores optional data you might want to use or not
// as lua is far more dynamic this helps dealing with too much data
// this also allows use of find/match/gmatch at through one struct
Matcher :: struct {
haystack: string,
pattern: string,
captures: [MAX_CAPTURES]Match,
captures_length: int,
offset: int,
err: Error,
// changing content for iterators
iter: string,
iter_index: int,
}
// init using haystack & pattern and an optional byte offset
matcher_init :: proc(haystack, pattern: string, offset: int = 0) -> (res: Matcher) {
res.haystack = haystack
res.pattern = pattern
res.offset = offset
res.iter = haystack
return
}
// find the first match and return the byte start / end position in the string, true on success
matcher_find :: proc(matcher: ^Matcher) -> (start, end: int, ok: bool) #no_bounds_check {
matcher.captures_length, matcher.err = find_aux(
matcher.haystack,
matcher.pattern,
matcher.offset,
true,
&matcher.captures,
)
ok = matcher.captures_length > 0 && matcher.err == .OK
match := matcher.captures[0]
start = match.byte_start
end = match.byte_end
return
}
// find the first match and return the matched word, true on success
matcher_match :: proc(matcher: ^Matcher) -> (word: string, ok: bool) #no_bounds_check {
matcher.captures_length, matcher.err = find_aux(
matcher.haystack,
matcher.pattern,
matcher.offset,
false,
&matcher.captures,
)
ok = matcher.captures_length > 0 && matcher.err == .OK
match := matcher.captures[0]
word = matcher.haystack[match.byte_start:match.byte_end]
return
}
// get the capture at the "correct" spot, as spot 0 is reserved for the first match
matcher_capture :: proc(matcher: ^Matcher, index: int, loc := #caller_location) -> string #no_bounds_check {
runtime.bounds_check_error_loc(loc, index + 1, MAX_CAPTURES - 1)
cap := matcher.captures[index + 1]
return matcher.haystack[cap.byte_start:cap.byte_end]
}
// get the raw match out of the captures, skipping spot 0
matcher_capture_raw :: proc(matcher: ^Matcher, index: int, loc := #caller_location) -> Match #no_bounds_check {
runtime.bounds_check_error_loc(loc, index + 1, MAX_CAPTURES - 1)
return matcher.captures[index + 1]
}
// alias
matcher_gmatch :: matcher_match_iter
// iteratively match the haystack till it cant find any matches
matcher_match_iter :: proc(matcher: ^Matcher) -> (res: string, index: int, ok: bool) {
if len(matcher.iter) > 0 {
matcher.captures_length, matcher.err = find_aux(
matcher.iter,
matcher.pattern,
matcher.offset,
false,
&matcher.captures,
)
if matcher.captures_length != 0 && matcher.err == .OK {
ok = true
first := matcher.captures_length > 1 ? 1 : 0
match := matcher.captures[first]
// output
res = matcher.iter[match.byte_start:match.byte_end]
index = matcher.iter_index
// advance
matcher.iter_index += 1
matcher.iter = matcher.iter[match.byte_end:]
}
}
return
}
// get a slice of all valid captures above the first match
matcher_captures_slice :: proc(matcher: ^Matcher) -> []Match {
return matcher.captures[1:matcher.captures_length]
}

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