Compare commits

...

452 Commits

Author SHA1 Message Date
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
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 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
skytrias 63a0395a79 refactor SPECIALS_TABLE 2022-12-21 22:08:03 +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
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 521ed28632 Keep -vet happy 2022-12-06 19:57:41 +00:00
gingerBill d6300314c0 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-12-06 19:55:24 +00:00
gingerBill 27130259cc Coalesce tombstones on delete_key to reduce all map slots from being filled on insertion
This is a bodge and will need to be replaced with an actual solution involving backward shift deletion rather than relying on tombstone slots in the first place.
2022-12-06 19:55:17 +00:00
Jeroen van Rijn b4fb295bb3 Merge pull request #2232 from DragosPopse/master
Added CSIDL_PROFILE in core:sys/windows
2022-12-06 03:22:32 +01:00
Dragos Popescu f7e608628b Merge branch 'odin-lang:master' into master 2022-12-06 02:59:53 +01:00
Dragos Popescu 605d66845a core:sys/windows: Added CSIDL_PROFILE 2022-12-06 02:58:33 +01:00
Jeroen van Rijn 37ec3d7006 Merge pull request #2231 from ftphikari/master
sys/windows: fix wgl function loading in accordance with OpenGL wiki
2022-12-06 01:06:08 +01:00
hikari 89eb351d2b sys/windows: wgl style fix 2022-12-06 02:01:35 +02:00
hikari abaacfe78d sys/windows: fix wgl function loading in accordance with OpenGL wiki 2022-12-06 01:53:19 +02:00
gingerBill f9f4551e8d Add user_data: rawptr to filepath.Walk_Proc callback 2022-12-05 22:31:35 +00:00
Jeroen van Rijn daf005d1ab Merge pull request #2230 from ftphikari/master
sys/windows: added helper gl proc
2022-12-05 23:05:31 +01:00
hikari ce1ee962f5 OpenGL: updated README 2022-12-06 00:00:05 +02:00
hikari d0e4edfb43 sys/windows: added helper gl proc 2022-12-05 23:58:31 +02:00
Jeroen van Rijn 749e5067fb Merge pull request #2228 from DragosPopse/master
-ignore-unknown-attributes: fixed compiler error caused by values being type checked
2022-12-03 00:23:50 +01:00
Dragos Popescu 75dcaf6d8d -ignore-unknown-attributes: fixed the attribute value being type checked in variable declarations 2022-12-03 00:11:18 +01:00
Jeroen van Rijn 00a0a1e95d Merge pull request #2227 from thePHTest/master
correct compare_exact_values(x,y) for TypeId and Procedure
2022-12-02 22:20:43 +01:00
Phil 7a4106077a correct compare_exact_values(x,y) for TypeId and Procedure 2022-12-02 13:13:01 -08:00
Jeroen van Rijn 9c8eaeb988 Merge pull request #2225 from ftphikari/master
sys/windows: add CreateWaitableTimerExW
2022-12-02 04:38:40 +01:00
hikari 7ed28e8a84 sys/windows: add CreateWaitableTimerExW 2022-12-02 05:24:30 +02:00
gingerBill a3d53a6288 Merge pull request #2203 from janivanecky/content_layout_rect
Add NSWindow::content_layout_rect
2022-11-29 19:51:19 +00:00
gingerBill 2127dc56b1 Add math.pow10 2022-11-29 14:31:56 +00:00
gingerBill e59e34d334 Change order of map_free_dynamic in usage 2022-11-29 12:27:32 +00:00
gingerBill 4fd97c3ba6 Remove panic 2022-11-29 12:22:37 +00:00
gingerBill 107c7a36d0 Treat .Mode_Not_Implemented as not an error when doing runtime.map_free_dynamic 2022-11-29 12:20:01 +00:00
gingerBill dcf2c43863 Add aliases for fnv* no_a forms 2022-11-29 11:55:01 +00:00
gingerBill 0c25f7cdc5 Improve core:math procedures and add loads of unit tests 2022-11-29 11:39:44 +00:00
gingerBill e5c243ee93 Fix atan2 by swapping the arguments internally 2022-11-29 09:19:45 +00:00
ftphikari e9b6a8fc9a sys/windows: add SHGetFolderPathW (#2213)
* sys/windows: add SHGetFolderPathW
* sys/windows: add some hittest constants
2022-11-28 18:47:15 +01:00
Jeroen van Rijn a27c00862c Merge pull request #2216 from oskarnp/auto_vcvarsall_build
Automatically initialize x64 environment if CL.exe is missing
2022-11-26 18:01:10 +01:00
Oskar Nordquist a06f75b6fb Automatically initialize x64 environment if CL.exe is missing + make sure found CL.exe is for x64 (credit to mmozeiko) 2022-11-26 11:50:06 -05: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 d6cb105d5f Fix LLVM type cycle nonsense with procedure types 2022-11-23 23:32:34 +00:00
gingerBill b7b9a016d3 Merge branch 'master' into multiple-return-abi-experiment 2022-11-23 22:48:56 +00:00
gingerBill 5ac36b5f25 HACK: Get around debugging type generation for slices and dynamic arrays of *nix systems 2022-11-23 22:46:02 +00:00
gingerBill 22bcf1ba70 Extra check for slices and dynamic arrays for -debug 2022-11-23 22:31:21 +00:00
gingerBill 51c705edf1 Add extra check to debug information of named composite types 2022-11-23 21:59:53 +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 e45401bfb4 Fix #2207 2022-11-23 14:14:22 +00:00
Jeroen van Rijn 6b652afb8e Merge pull request #2206 from thisisnotnull/fix-wprintf
fix wprintf return value
2022-11-22 16:53:23 +01:00
gingerBill 0a0db23b17 Remove copy elision code 2022-11-22 15:49:27 +00:00
thisisnotnull 76b85c0622 fix wprintf return value 2022-11-21 21:39:43 +01:00
gingerBill 6fa0679be9 Fix #2109 2022-11-21 13:12:44 +00:00
gingerBill afea221d64 Make structs with the same fields but different tags different types
Fixes #2105
2022-11-21 13:10:49 +00:00
gingerBill b9ec2de4db strconv.parse_f64 - accurately parse floats 2022-11-21 13:00:24 +00:00
gingerBill 3dfd53aee0 Improve error handling for trailing commas #2136 2022-11-21 11:56:59 +00:00
gingerBill b54fc8ff95 Fix UUID 2022-11-21 11:53:49 +00:00
gingerBill 8745942255 Fix #2174 2022-11-21 11:42:43 +00:00
gingerBill c7be30e0ea Fix #2172 2022-11-21 11:38:29 +00:00
gingerBill 1baa47c78e Fix #2179 2022-11-21 11:31:35 +00:00
gingerBill 0b33df4e9d Fix #2186 2022-11-21 11:30:21 +00:00
gingerBill 4c40495742 Fix #2188 2022-11-21 11:23:10 +00:00
gingerBill 824b97d250 Fix #2197 2022-11-21 11:14:29 +00:00
gingerBill 5bbab05161 Fix #2199 2022-11-21 11:02:52 +00:00
gingerBill 83558a1352 Fix #2201 2022-11-21 11:01:01 +00:00
gingerBill cb183e968a Fix #2202 2022-11-21 10:30:59 +00:00
gingerBill 27d56d0da4 Fix #2125 2022-11-21 10:25:34 +00:00
gingerBill c663566cd5 Fixed comparison against nil for maps 2022-11-20 01:34:57 +00:00
gingerBill 13d052027f Merge pull request #2204 from colrdavidson/fix_wasm_mem
fix missing wasm memory case
2022-11-19 10:17:40 +00:00
Colin Davidson 7076cf69e4 fix missing wasm memory case 2022-11-18 23:41:47 -08:00
Jan Ivanecky 6ae8adaa45 Add NSWindow::content_layout_rect 2022-11-17 17:57:34 -05:00
gingerBill 15bbdb2030 Merge pull request #2181 from odin-lang/map-dev
New `map` internals
2022-11-17 15:29:28 +00:00
Jeroen van Rijn 48c9c1682c Merge pull request #2198 from Kelimion/ms-craziness
Fix microsoft_craziness.h
2022-11-15 14:02:11 +01:00
Jeroen van Rijn d3c5143292 Fix microsoft_craziness.h 2022-11-15 13:57:02 +01:00
Jeroen van Rijn 3949e2220f Test new map when used as a set.
map[K]struct{} works fine.
2022-11-15 01:27:29 +01:00
Jeroen van Rijn 9ed4f95c1a Merge pull request #2196 from Skytrias/opengl-defines
Add missing OpenGL constants based on GLAD
2022-11-15 01:13:50 +01:00
Michael Kutowski 8daecf7532 Update constants.odin 2022-11-15 01:06:03 +01:00
Michael Kutowski 11d665c25a Update enums.odin 2022-11-15 01:05:38 +01:00
Jeroen van Rijn 98a086b91b Merge pull request #2194 from mifreundorfer/fix-scratch-allocator
Handle freeing nil in scratch allocator
2022-11-14 22:04:00 +01:00
Michael Freundorfer f323a179d9 Handle freeing nil in scratch allocator 2022-11-14 21:44:20 +01:00
Jeroen van Rijn c6f282d20b Merge pull request #2193 from ftphikari/master
sys/windows: add ShellExecuteExW
2022-11-14 21:06:27 +01:00
hikari ba49a9100d sys/windows: add ShellExecuteExW 2022-11-14 21:58:12 +02:00
Jeroen van Rijn 6fe77155b5 Merge pull request #2191 from Kelimion/build_float
Don't write leading + unless +Inf or we ask for it.
2022-11-14 16:46:10 +01:00
Jeroen van Rijn 677e7ff642 Don't write leading + unless +Inf or we ask for it. 2022-11-14 16:32:50 +01:00
gingerBill 682b5fa0d3 Merge pull request #2190 from colrdavidson/write_float
add floats to string builder
2022-11-14 12:42:49 +00:00
gingerBill ab00db2ebd Add write_(f16|f32|f64) calls 2022-11-14 12:37:55 +00:00
Colin Davidson 0a0e8f36eb add floats to string builder 2022-11-14 04:30:14 -08:00
gingerBill bbe44b49bc Correct map_insert 2022-11-14 11:47:56 +00:00
gingerBill 25bec19b1f Revert "Minor improvement to multi return value reducing stack usage" 2022-11-13 23:56:05 +00:00
gingerBill 81f83d5780 Fix prototype 2022-11-13 23:51:59 +00:00
gingerBill d2019e3e4d Enforce pointer cast 2022-11-13 23:50:45 +00:00
gingerBill 489e8dc592 Add @(require_results) to map procedures where possible 2022-11-13 23:47:00 +00:00
gingerBill 3edb3d8d8c Simplify the handling of the hashing calls for maps 2022-11-13 23:24:08 +00:00
gingerBill a705a2e38b Minor improvement to multi return value reducing stack usage 2022-11-13 22:55:32 +00:00
Jeroen van Rijn 7dfbda58d9 Fix CI typo. 2022-11-13 16:38:22 +01:00
Jeroen van Rijn 9b88a38e54 map tests for Linux and Mac 2022-11-13 16:32:24 +01:00
Jeroen van Rijn 16a494347c map: Add tests for update + delete. 2022-11-13 16:24:20 +01:00
gingerBill ad0f11668b Correct map_reserve_dynamic caused by an bizarre code generation bug 2022-11-13 14:53:58 +00:00
Jeroen van Rijn 699cabeb1c Update tests/internal/build.bat 2022-11-12 17:36:20 +01:00
Jeroen van Rijn 7207f4b0c5 Add tests/internal/build.bat 2022-11-12 17:31:26 +01:00
Jeroen van Rijn 9c1b464c94 Add tests for new map implementation. 2022-11-12 17:25:42 +01:00
gingerBill 04a1e7d638 Correct json/unmarshal.odin 2022-11-11 16:15:21 +00:00
gingerBill 7cfbd87f57 Merge branch 'master' into map-dev 2022-11-11 15:56:14 +00:00
gingerBill e9e05a3783 Fix typo 2022-11-11 15:55:55 +00:00
gingerBill 2b83f27f06 Merge branch 'master' into map-dev 2022-11-11 15:54:33 +00:00
gingerBill 3d0e194298 Check for non-zero sized elements for intrinsics.ptr_sub 2022-11-11 15:54:13 +00:00
gingerBill fcd8860990 Make intrinsics.ptr_sub use explicit integer arithmetic internally 2022-11-11 15:52:49 +00:00
gingerBill 22840ddf97 Add noinline LLVM attribute to static map procedures 2022-11-11 15:35:05 +00:00
gingerBill f9576c2f5b Add internal linkage to static map calls 2022-11-11 15:28:20 +00:00
gingerBill 16fc961010 Begin work on map static set 2022-11-11 14:45:22 +00:00
gingerBill d2701d8b13 Make __dynamic_map_set take the hash rather than compute it internally 2022-11-11 13:04:38 +00:00
gingerBill a0bd31646b Make map get internal calls take the hash value rather than compute it internally 2022-11-11 13:02:23 +00:00
gingerBill 0d37da54b4 Add minor optimization for lb_map_cell_index_static 2022-11-11 11:41:28 +00:00
gingerBill 5d47e2a166 Change map_reserve_dynamic no do anything when current capacity is greater than specified for the reserve 2022-11-11 11:24:34 +00:00
gingerBill 035c75d6a9 Add contextless where appropriate 2022-11-11 11:23:59 +00:00
gingerBill b475481788 Get deleted key and value for delete_key 2022-11-11 11:19:34 +00:00
gingerBill 033525fe13 Force inline of hasher proc where possible 2022-11-11 11:10:26 +00:00
gingerBill 8852d090b6 Correct static map get; make get take a pointer to simplify compiler internals 2022-11-10 12:46:53 +00:00
gingerBill ac259ac790 Unify reserve and grow code 2022-11-10 12:34:01 +00:00
gingerBill 7b4a87d37c Correct iterate_map 2022-11-10 12:33:49 +00:00
gingerBill f6fc3ebe37 Add reflect/iterator.odin 2022-11-10 12:27:12 +00:00
gingerBill 5c106abe3f Make map_alloc_dynamic handle the nil_allocator() 2022-11-10 12:01:40 +00:00
gingerBill db748b7a05 Correct logic for __dynamic_map_set 2022-11-09 23:10:18 +00:00
gingerBill f2f2d532f5 Add extra calls to Tracking_Allocator 2022-11-09 22:31:49 +00:00
gingerBill 1bcec3f769 Change map internal calls to use a pointer 2022-11-09 22:21:36 +00:00
gingerBill b035ee2bcd Swap hashes 2022-11-09 22:05:28 +00:00
gingerBill 0424fb486b Rewrite map_insert_hash_dynamic 2022-11-09 21:00:17 +00:00
gingerBill 3858422f1d Use mem_resize where possible 2022-11-09 20:59:49 +00:00
gingerBill d4f343751e Inline __dynamic_map_set code where possible 2022-11-08 21:57:18 +00:00
gingerBill 79baddc157 Merge pull request #2176 from jaspergeer/fix-untyped-segfault
fix #2129 Segfault in compiler when void function used for its return value
2022-11-08 21:23:12 +00:00
gingerBill bcf437dc11 Check for existence before setting
Test code
2022-11-08 21:21:19 +00:00
gingerBill 503eb470a7 Do an extra check before insertion for pre-existing keys
This is test code
2022-11-08 21:10:38 +00:00
gingerBill 667af1be58 Correct map_insert_hash_dynamic and map_insert_dynamic 2022-11-08 20:44:52 +00:00
gingerBill 366779f8c7 Fix bug with allocator not getting set on a map 2022-11-08 16:06:10 +00:00
gingerBill dae299b781 Make map_free_dynamic take the total size of the allocation 2022-11-08 15:40:30 +00:00
gingerBill 2f29894b45 Minor change to map_cell_index_static 2022-11-08 15:15:00 +00:00
gingerBill 0819d05a0b Fix for in for map 2022-11-08 15:07:57 +00:00
gingerBill 6a4e44607c Fix json marshal for maps 2022-11-08 14:59:09 +00:00
gingerBill a71daee545 Allow for -use-static-map-calls which generates a get procedure per map; add runtime.map_get 2022-11-08 14:58:05 +00:00
gingerBill 046dd55032 Change __dynamic_map_get signature 2022-11-08 13:02:32 +00:00
gingerBill 2fc3da3fde Change Raw_Map.len to int from uintptr 2022-11-08 12:29:20 +00:00
gingerBill a74093784c Add intrinsics.map_cell_info and intrinsics.map_info 2022-11-08 12:24:00 +00:00
gingerBill ed58374964 Make Map_Info store pointers to cell info rather than inline 2022-11-08 12:18:36 +00:00
gingerBill 6dd4d1a924 Correct reflection usage of maps 2022-11-08 11:50:55 +00:00
gingerBill d77269dee2 Disallow zero sized map keys 2022-11-08 11:42:42 +00:00
gingerBill ea263b8cc5 Add runtime.map_exists_dynamic 2022-11-08 11:29:09 +00:00
gingerBill 45f0c812af Correct reflect.map_entry_info_slice 2022-11-08 11:21:45 +00:00
gingerBill 810a1eee41 Remove the need for type->Map.internal_type and replace with the definition of runtime.Raw_Map 2022-11-08 11:13:46 +00:00
gingerBill e3e225d21b Support for in loops for map 2022-11-08 11:04:37 +00:00
gingerBill 50e10ceb3b Correct hashing for map types 2022-11-08 01:20:08 +00:00
gingerBill da774e3fd2 General modifications 2022-11-08 00:38:31 +00:00
gingerBill 2c3febd620 Correct fmt printing to be robust 2022-11-07 23:35:44 +00:00
gingerBill bce62b98d4 Basic fmt printing for map 2022-11-07 23:32:59 +00:00
gingerBill e914a8710d Basic get and set support for new map 2022-11-07 23:17:37 +00:00
gingerBill c96e0afbf1 Begin work on implementing the new map internals 2022-11-07 23:02:21 +00:00
gingerBill f1c24f434b -default-to-nil-allocator also enables -no-dynamic-literals 2022-11-07 10:24:14 +00:00
Jasper Geer e8517e1d02 check for nullptr when evaluating untypedness 2022-11-04 16:29:04 -04:00
gingerBill 92e406cef0 Implement asin in native Odin 2022-11-04 14:30:18 +00:00
gingerBill 269913ede0 Implement acos in native Odin 2022-11-04 14:26:31 +00:00
gingerBill 2ed16240a7 Add core:text/edit 2022-11-04 14:08:19 +00:00
gingerBill ff36b754cb Fix atrig functions 2022-11-04 13:53:28 +00:00
gingerBill 503b897677 Fix formatting 2022-11-04 12:31:53 +00:00
gingerBill d69c74665a Add @(require_results) 2022-11-04 12:31:39 +00:00
gingerBill fcf081283c Move LICENSE 2022-11-04 12:23:48 +00:00
gingerBill 7a6e8543a6 Use #by_ptr and @(require_results) were useful 2022-11-04 12:21:01 +00:00
gingerBill f30755a871 Update README.md 2022-11-04 11:59:37 +00:00
gingerBill 503220e4c1 Add README.md 2022-11-04 11:59:26 +00:00
gingerBill 051814a69c Wrap parse procedures to allow for multiple return values 2022-11-04 11:59:20 +00:00
gingerBill 21843da9e3 Add //+build windows 2022-11-04 11:47:42 +00:00
gingerBill 30f49f81c1 Use slices and Odin string where possible due to struct field ordering 2022-11-04 11:44:49 +00:00
gingerBill 439f4776e4 Add cgltf to build_vendor.bat 2022-11-04 11:40:07 +00:00
gingerBill b743f56fb9 Fix +build ignore 2022-11-04 11:39:54 +00:00
gingerBill 1fc3f6cb2e Add vendor:cgltf 2022-11-04 11:39:38 +00:00
gingerBill df19c48da8 Add doc.odin 2022-11-03 13:36:00 +00:00
gingerBill f7211408fc Merge pull request #1544 from FancyKillerPanda/build_ignore
Changed `//+ignore` to `//+build ignore` and emit a warning for unknown tags
2022-11-03 12:58:26 +00:00
gingerBill 30db316e16 Merge pull request #2141 from ChuuniMage/patch-2
Add caprintf and ctprintf to fmt
2022-11-03 12:57:46 +00:00
gingerBill 8c01e952f3 Merge pull request #2072 from odin-lang/allocator-mode-alloc-non-zeroed
Add `Allocator_Mode.Alloc_Non_Zerored`
2022-11-03 12:57:23 +00:00
gingerBill 3e66b88031 Merge pull request #2147 from jaspergeer/tighten-slice-string-cast-error
fix #2095 "Suggestion: the expression may be casted to string" in response to erroneous cast to string
2022-11-03 12:47:44 +00:00
gingerBill f76316f889 Merge branch 'master' into allocator-mode-alloc-non-zeroed 2022-11-03 12:47:11 +00:00
gingerBill 32477a88ef Merge pull request #2165 from JopStro/master
Implement os open for wasi_wasm32 target
2022-11-03 12:43:55 +00:00
gingerBill e8bc576b23 Rename fnv32 and fnv64 to fnv32_no_a and fnv64_no_a 2022-11-03 11:44:19 +00:00
gingerBill 2eea6f2490 Merge pull request #2173 from Hyp-X/master
d3d12: Fixed RESOURCE_STATE_GENERIC_READ flags
2022-11-03 10:37:32 +00:00
Hyp-X 1d9d79542c d3d12: Fixed RESOURCE_STATE_GENERIC_READ flags 2022-11-03 10:49:45 +01:00
gingerBill 1a6d4c955a Add more bit_sets to direct packages 2022-11-02 23:12:43 +00:00
gingerBill 717522efe4 Correct more flags for d3d12 2022-11-02 22:45:05 +00:00
gingerBill 8d06d9c23d Merge branch 'master' of https://github.com/odin-lang/Odin 2022-11-02 16:43:36 +00:00
gingerBill 765c1546c5 Make many d3d12 flags enums into bit_set 2022-11-02 16:43:29 +00:00
Jeroen van Rijn 7ec6fd30f0 Merge pull request #2171 from Kelimion/os_read_windows
Fix os.read implementation on Windows.
2022-11-02 17:03:20 +01:00
Jeroen van Rijn 0ca773114a Fix os.read implementation on Windows. 2022-11-02 16:48:39 +01:00
gingerBill 9e1576418f Update README.md 2022-11-02 15:07:09 +00:00
gingerBill b7ea169c81 Fixed #2170 2022-11-02 11:36:49 +00:00
gingerBill 3b583cbac7 Add debug symbols for global constants of integers, bools, enums, runes, & pointers.
Variables are namespaced with `pkg::name` or `name` if built-in or the initial package for convenience.
2022-11-02 00:05:51 +00:00
gingerBill 382bd87667 Merge pull request #2169 from odin-lang/location-byval
Ad-hoc pass source code location directly by pointer without stack copy
2022-11-01 15:52:40 +00:00
Jeroen van Rijn 6cc07dc24e Merge branch 'master' of github.com:odin-lang/Odin 2022-11-01 15:49:10 +01:00
Jeroen van Rijn 01cdd22a01 Temporarily disable certain tests. 2022-11-01 15:48:27 +01:00
gingerBill 35331e6973 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-11-01 14:45:57 +00:00
gingerBill c18e98e8c5 Add extra check in add_entity_and_decl_info #2161 2022-11-01 14:45:51 +00:00
Jeroen van Rijn 3cd553565f Merge pull request #2168 from Kelimion/target_version
Add -minimum-os-version flag
2022-11-01 15:18:06 +01:00
Jeroen van Rijn 9eec9f5788 Add -minimum-os-version flag
Allow for Darwin targets to specify the minimum OS version:
e.g. -minimum-os-version:12.0.0
2022-11-01 15:04:44 +01:00
gingerBill 2b7ca2bdd6 Fix #2160 (deep subtyping through using of _) 2022-11-01 13:14:20 +00:00
gingerBill 411c0add3b Add safety check for #2161 2022-11-01 13:03:35 +00:00
JopStro 18d7ecc1a5 wasi: Add FD_FILESTAT_GET to default file open rights 2022-11-01 12:56:36 +00:00
gingerBill 4812601e78 Fix #2167 context.assertion_failure_proc = nil (context field assignments) 2022-11-01 12:56:17 +00:00
Jeroen van Rijn 2d5779b660 Add missing newline. 2022-11-01 00:47:16 +01:00
Jeroen van Rijn fd53e8b955 Merge pull request #2166 from Kelimion/clarify-define-help
Clarify -define help.
2022-11-01 00:46:17 +01:00
Jeroen van Rijn 53a030c65b Clarify -define help. 2022-11-01 00:38:54 +01:00
JopStro 91ad6b42c5 rename default_dir to current_dir 2022-10-31 21:46:47 +00:00
JopStro dad10ef800 create _yeild stub for wasi_wasm32 target to avoid compile error 2022-10-31 21:22:55 +00:00
JopStro 71eb21aab7 implement open for wasi_wasm32 target 2022-10-31 21:21:10 +00:00
Jeroen van Rijn f8228e305a Merge pull request #2164 from colrdavidson/fix-osx-read
terminate read if we read EOF
2022-10-31 14:44:40 +01:00
Colin Davidson 0e7109cab2 terminate read if we read EOF 2022-10-31 06:08:18 -07:00
gingerBill c39ef1b25c Ad-hoc pass source code location directly by pointer without stack copy 2022-10-31 00:25:53 +00:00
gingerBill 9da37ed394 Optimize #caller_location and #location to use read only data section where possible 2022-10-31 00:04:15 +00:00
gingerBill 8fa571c283 Use direct parameter value in lb_find_ident when possible 2022-10-30 22:58:44 +00:00
gingerBill 83f3ae14d5 Improve SysV ABI LLVM IR generation for development purposes 2022-10-30 22:50:24 +00:00
gingerBill 6a14c3edb4 Make raw_data an intrinsic rather a @(builtin) runtime procedure 2022-10-30 22:05:29 +00:00
Jeroen van Rijn 2cd895c50b Merge pull request #2159 from jceipek/fix-stb-darwin-lib-refs
Fix STB lib import references on `ODIN_OS == .Darwin`
2022-10-30 05:01:24 +01:00
Julian Ceipek ee89c0458f Fix STB lib import references on ODIN_OS == .Darwin 2022-10-29 22:19:01 -04:00
Jeroen van Rijn cee847a68c Merge pull request #2156 from Kelimion/remove_opt
Remove formerly deprecated `-opt` flag.
2022-10-28 21:47:31 +02:00
Jeroen van Rijn 413f96553a Remove formerly deprecated -opt flag. 2022-10-28 21:38:20 +02:00
Jeroen van Rijn 662ed4a67c Merge pull request #2154 from Kelimion/llvm-15-check
Panic if LLVM > 14.
2022-10-27 03:08:02 +02:00
Jeroen van Rijn 85a263130d Add LLVM > 14 check to main.cpp for Darwin. 2022-10-27 02:55:38 +02:00
Jeroen van Rijn d19ae37af1 Panic if LLVM > 14. 2022-10-27 02:39:18 +02:00
Jeroen van Rijn 22672a816e Merge pull request #2153 from oskarnp/fix-fmt-string-width
Fix behavior of fmt_string() to not truncate strings to width
2022-10-26 17:54:26 +02:00
Oskar Nordquist dcb873c88d Fix behavior of fmt_string() to not truncate strings to width 2022-10-26 11:21:42 -04:00
gingerBill 62ab2987b6 Change name to windows_set_file_info_times 2022-10-26 16:08:49 +01:00
gingerBill 7bcde35651 Heavily improve time handling on Windows for time.now() and os.File_Info 2022-10-26 16:05:49 +01:00
JasperGeer 4b8721a0bb check addressing mode instead 2022-10-26 10:11:10 -04:00
gingerBill 7743e34596 Fix typo 2022-10-26 15:01:35 +01:00
gingerBill 4003b76fd3 Add GetSystemTimePreciseAsFileTime 2022-10-26 15:00:25 +01:00
gingerBill c27ed1896f Merge branch 'master' of https://github.com/odin-lang/Odin 2022-10-26 13:37:40 +01:00
gingerBill 7d217269b5 Add Arena_Kind.Buffer to core:mem/virtual 2022-10-26 13:37:20 +01:00
Jeroen van Rijn a3c8882648 Merge pull request #2151 from nowheredevel/master
Fix printf typo in documentation
2022-10-26 01:53:46 +02:00
nowheredevel 4389059834 Fix printf typo in documentation 2022-10-25 19:06:15 -04:00
Jeroen van Rijn a55e90fefd Merge pull request #2149 from Kelimion/which
Detect `which` and complain if not found.
2022-10-25 16:56:42 +02:00
Jeroen van Rijn f58f922487 Detect which and complain if not found. 2022-10-25 16:45:38 +02:00
JasperGeer 1a0930f841 don't suggest u8 slice cast to string for u8 slice literal 2022-10-23 19:41:07 -04:00
gingerBill a5f8c3f692 Update many enums to bit_sets for D3D11 2022-10-23 13:17:37 +01:00
Jeroen van Rijn 92fb65cf2e Fix #defined(I). 2022-10-23 04:32:45 +02:00
Jeroen van Rijn a51943e27f Add core:math/rand.choice 2022-10-23 04:18:58 +02:00
Jeroen van Rijn 03c834e410 Merge pull request #2145 from jaspergeer/fix-scalar-cast-to-non-square-matrix
fix #2130 Assertion failure in compiler on cast of scalar to non-square matrix
2022-10-21 22:27:15 +02:00
Jasper Geer 989107094c throw type checker error when scalar cast to non-square matrix 2022-10-21 15:41:58 -04:00
Jeroen van Rijn fd8956b8f4 Merge pull request #2144 from Kelimion/glfw
Add RawMouseMotionSupported
2022-10-21 19:28:44 +02:00
Jeroen van Rijn 648e3c65ea Add RawMouseMotionSupported 2022-10-21 19:20:15 +02:00
gingerBill d5047e621d Merge pull request #2134 from jrfondren/errno-linkfix
fix core:c/libc.errno link_name for Linux and FreeBSD
2022-10-21 15:48:43 +01:00
gingerBill 8fbdef01d6 Merge pull request #2142 from jceipek/fix-objc_allocateClassPair
Fix signature for `objc_allocateClassPair` and add `objc_registerClassPair` to enable Objective-C subclassing
2022-10-21 11:08:00 +01:00
ChuuniMage 9dee943fae Update fmt.odin
Feedback regarding internal `fmt` reference addressed
2022-10-21 14:50:46 +11:00
Julian Ceipek 8ceb691cec Fix indentation 2022-10-20 21:38:43 -04:00
Julian Ceipek f26516f6fa Add objc_registerClassPair to allow subclassing 2022-10-20 21:18:11 -04:00
Julian Ceipek fda8e8a30b Use c.size_t to match C declaration more directly 2022-10-20 21:16:53 -04:00
Julian Ceipek 2242178d96 Fix signature for objc_allocateClassPair 2022-10-20 21:07:14 -04:00
ChuuniMage a459bc13dc Add caprintf and ctprintf to fmt
Formatted cstring procs to work with ubiquitous cstring APIs
2022-10-21 10:23:10 +11:00
gingerBill 53e84b7f31 Remove doubly linked list of Platform_Memory_Block fields 2022-10-19 23:39:47 +01:00
gingerBill 098f51aa80 Allow transmute to be constant for integers of the same internal endianness 2022-10-19 16:59:38 +01:00
gingerBill 765969e6a3 Revert default_resize_bytes_align logic to previous behaviour 2022-10-19 16:06:36 +01:00
gingerBill 8086c14dcc Merge branch 'master' of https://github.com/odin-lang/Odin 2022-10-18 10:28:25 +01:00
gingerBill 80ce1b7d85 Allow for N = -1 in wstring_to_utf8 2022-10-18 10:28:17 +01:00
Julian Fondren 9f55404845 fix core:c/libc.errno link_name for Linux and FreeBSD
Although the FreeBSD link matches Darwin, its EILSEQ still matches Linux.

Confirmed with the following program:

```odin
package main
import "core:c/libc"

main :: proc() {
	libc.printf("%d\n", libc.errno()^) // 0
	_ = libc.fopen("nonexistent file", "r")
	libc.printf("%d\n", libc.errno()^) // 2
}
```

on Linux:

	Odin: dev-2022-10:075040ae
	OS:   Manjaro Linux, Linux 5.10.147-1-MANJARO
	CPU:  Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
	RAM:  15953 MiB

and FreeBSD:

	Odin: dev-2022-10:075040ae
	OS:   FreeBSD: Unknown
	CPU:  Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
	RAM:  990 MiB

FreeBSD uname -r: 13.0-RELEASE
2022-10-17 22:32:10 -05:00
gingerBill 075040ae05 Update sort_private.odin 2022-10-18 00:06:21 +01:00
gingerBill aa799d6a0d Merge pull request #2124 from odin-lang/virtual-arena-unification
`core:mem/virtual` - Unify `Static_Arena` and `Growing_Arena` into `Arena`
2022-10-17 11:46:44 +01:00
gingerBill 58e607e960 Merge pull request #2128 from Lperlind/staging/better_using_blank
Fix assert in issue #1555 and improve error messages with 'using _'
2022-10-17 11:45:57 +01:00
gingerBill ff51c5ee56 Wrap intrinsics.overflow_add to safe_add 2022-10-15 12:52:07 +01:00
Lucas Perlind 73c1f08776 Improve error messages with 'using _' 2022-10-15 19:46:17 +11:00
gingerBill 412ca36230 Merge pull request #2127 from terids/vendor-vulkan-fix
Fix GetInstanceProcAddr crash
2022-10-14 12:22:08 +01:00
terids 06d1df4cae Fix GetInstanceProcAddr crash
It was trying to initialise itself with itself when calling load_proc_addresses(Instance)
Discord bug channel reference https://discord.com/channels/568138951836172421/585072813954564100/1030265964572450867
2022-10-14 02:03:57 +01:00
gingerBill 7662808bc9 Add overflow_add checks to alloc_from_memory_block 2022-10-13 12:53:33 +01:00
gingerBill d48828dd80 Add overflow check when using a growing arena 2022-10-13 12:45:17 +01:00
gingerBill b725e01cdd Add @(require_results) to many procedures 2022-10-13 11:10:16 +01:00
gingerBill 874c1f076d Merge pull request #2126 from ftphikari/master
sys/windows: add SHFileOperationW
2022-10-13 11:04:01 +01:00
hikari 2c14f0a109 sys/windows: add ITaskbarList interfaces 2022-10-13 11:19:05 +03:00
gingerBill cf4afc2e7b Inline assert condition 2022-10-12 21:26:50 +01:00
gingerBill 5ed06f7eb8 Rename constants; minor rearrange of Arena layout 2022-10-12 21:23:45 +01:00
gingerBill 765cd66b30 Clean up minimum_block_size default implicit initialization 2022-10-12 21:20:31 +01:00
gingerBill 5a8fbc230d Sanity corrections to virtual calls 2022-10-12 21:16:34 +01:00
gingerBill 5c62211f00 Inline resize logic for virtual.Arena 2022-10-12 20:44:36 +01:00
gingerBill 835b8ffa22 Update total_used for arena_static_reset_to 2022-10-12 20:30:48 +01:00
gingerBill b84108c4b5 Inline align forward offset code 2022-10-12 20:28:51 +01:00
gingerBill 6642e1fc9d Unify Static_Arena and Growing_Arena into Arena 2022-10-12 19:10:04 +01:00
hikari c909e8e4b8 sys/windows: add SHFileOperationW 2022-10-12 04:35:41 +03:00
gingerBill 9bdbb45517 Merge pull request #2110 from elusivePorpoise/master
os2/file_windows fix
2022-10-11 22:37:29 +01:00
gingerBill 1b5860e574 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-10-11 21:22:05 +01:00
gingerBill 047d45584e Fix #2016 when passing an untyped integer to a generic typeid parameter 2022-10-11 21:21:56 +01:00
elusivePorpoise 721486f875 Merge branch 'odin-lang:master' into master 2022-10-11 02:20:30 -07:00
gingerBill 29f2ecd228 Merge pull request #2101 from ftphikari/master
sys/windows: add a bunch of stuff
2022-10-10 22:08:30 +01:00
gingerBill 970ac22647 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-10-10 21:49:03 +01:00
gingerBill 419eab5059 Force call site attributes for procedures (relating to #2121 causing ABI issues for intrinsics.objc_send) 2022-10-10 21:48:56 +01:00
hikari a1935bc1f4 sys/windows: replace A with W 2022-10-10 20:40:41 +03:00
gingerBill fc06c8ed9f Merge pull request #2120 from jceipek/fix-nsapplication-shouldTerminateAfterLastWindowClosed
Fix signature for `shouldTerminateAfterLastWindowClosed` delegate proc
2022-10-10 12:01:36 +01:00
gingerBill 7952b26e8b Merge pull request #2115 from Lperlind/staging/soa_ptr_debug_fix
Generate debug info for Type_SoaPointer
2022-10-10 11:56:51 +01:00
hikari fa6cfde4b0 sys/windows: add free disk space function binding 2022-10-10 07:26:32 +03:00
Jeroen van Rijn 4c78ba2152 Fix #2122 2022-10-09 21:34:43 +02:00
Jeroen van Rijn 9870e43ac0 Merge pull request #2119 from odin-lang/revert-2118-fix-2112
Revert "Fix #2112"
2022-10-09 21:23:35 +02:00
Julian Ceipek 63086c7eaf Use NS.BOOL instead of bool 2022-10-09 14:31:26 -04:00
Julian Ceipek ef0c6fc4b3 Fix signature for shouldTerminateAfterLastWindowClosed delegate proc 2022-10-08 23:52:12 -04:00
Jeroen van Rijn 159c5311c3 Revert "Fix #2112" 2022-10-08 23:01:06 +02:00
Jeroen van Rijn b6a65fac36 Merge pull request #2118 from Kelimion/fix-2112
Fix #2112
2022-10-08 19:06:27 +02:00
Jeroen van Rijn ab7367ae47 Fix #2112 2022-10-08 19:00:05 +02:00
Jeroen van Rijn 457f509b5f Merge pull request #2117 from janivanecky/cocoa_window
Add glfw.GetCocoaWindow
2022-10-08 17:09:28 +02:00
Jan Ivanecky 7e5c063d98 Add glfw.GetCocoaWindow 2022-10-08 17:03:55 +02:00
Jeroen van Rijn dfabd0e0ad Merge pull request #2116 from janivanecky/objc_methods
Add class_getInstanceMethod, method_setImplementation bindings
2022-10-08 16:39:38 +02:00
Jan Ivanecky 141133e326 Add class_getInstanceMethod, method_setImplementation bindings 2022-10-08 16:29:49 +02:00
Lucas Perlind e188a542da llvm_backend_debug: Add debug info for soa pointer
This fixes issue #2113
2022-10-08 17:08:28 +11:00
Lucas Perlind 64f1e8b7a2 Github CI: Add test case for issue 2113 2022-10-08 17:07:29 +11:00
Jeroen van Rijn 62440df051 Merge pull request #2111 from janivanecky/ns_window_methods
Add additional NSWindow methods
2022-10-08 01:22:18 +02:00
Jan Ivanecky 5362e883f4 Add additional NSWindow methods 2022-10-08 00:47:42 +02:00
Phuk Ng Yu 8b06fd0935 os2/file_windows fix 2022-10-07 00:06:46 -07:00
hikari bb9b58b8c4 sys/windows: add some constants 2022-10-07 03:53:14 +03:00
gingerBill ee070c9bd3 Merge branch 'master' of https://github.com/odin-lang/Odin 2022-10-04 10:18:40 +01:00
gingerBill aebafdcd08 update virtual.growing_arena_bootstrap_new 2022-10-04 10:18:32 +01:00
ftphikari 2b4fce8684 Merge branch 'odin-lang:master' into master 2022-10-04 09:10:00 +03:00
Jeroen van Rijn de8f6709f7 Disable issues tests for the moment. 2022-10-04 02:07:54 +02:00
Jeroen van Rijn 683753db96 Merge pull request #2104 from matias-eduardo/patch-3
Add a few Fiber functions to windows/kernel32.odin
2022-10-04 01:52:46 +02:00
matias d13dc7eca7 Add a few Fiber functions to kernel32.odin
This is not the complete set, but a start.
2022-10-03 11:35:27 -04:00
Jeroen van Rijn e56920e445 Merge pull request #2103 from rasa-silva/darwin_version_update
Update Darwin release map
2022-10-03 16:23:56 +02:00
Ricardo Silva 1c9aad4d7b Update Darwin release map 2022-10-03 14:49:35 +01:00
hikari ce09cb0bdb sys/windows: add comctl32 2022-10-01 17:13:17 +03:00
hikari b7fd91817e Merge remote-tracking branch 'origin/master' 2022-10-01 17:12:57 +03:00
hikari a728047281 sys/windows: add a bunch of stuff 2022-10-01 17:12:23 +03:00
gingerBill 5cf473b31c Fix typo 2022-09-22 15:19:24 +01:00
gingerBill c767d55e9a Fix typo 2022-09-22 12:21:43 +01:00
gingerBill 7f601c9535 Add Allocator_Mode.Alloc_Non_Zerored 2022-09-22 12:12:57 +01:00
FancyKillerPanda e139d1cbe4 Removed //+build ignore from tests/core/math/big/test.odin. 2022-02-24 12:45:03 +11:00
FancyKillerPanda cb04116caf Emit a parser warning when encountering an unknown tag. 2022-02-24 12:27:52 +11:00
FancyKillerPanda 62ff8daa78 Changed //+ignore to //+build ignore. 2022-02-24 12:23:54 +11:00
211 changed files with 25580 additions and 10264 deletions
+15 -10
View File
@@ -38,6 +38,11 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
- name: Odin internals tests
run: |
cd tests/internal
make
timeout-minutes: 10
- name: Odin check examples/all for Linux i386
run: ./odin check examples/all -vet -strict-style -target:linux_i386
timeout-minutes: 10
@@ -87,10 +92,10 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
- name: Odin issues tests
- name: Odin internals tests
run: |
cd tests/issues
./run.sh
cd tests/internal
make
timeout-minutes: 10
- name: Odin check examples/all for Darwin arm64
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
@@ -151,6 +156,13 @@ jobs:
cd tests\vendor
call build.bat
timeout-minutes: 10
- name: Odin internals tests
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\internal
call build.bat
timeout-minutes: 10
- name: core:math/big tests
shell: cmd
run: |
@@ -158,13 +170,6 @@ jobs:
cd tests\core\math\big
call build.bat
timeout-minutes: 10
- name: Odin issues tests
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\issues
call run.bat
timeout-minutes: 10
- name: Odin check examples/all for Windows 32bits
shell: cmd
run: |
+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
+1 -1
View File
@@ -82,7 +82,7 @@ A wiki maintained by the Odin community.
#### [Odin Discord](https://discord.gg/sVBPHEv)
Get live support and talk with other odiners on the Odin Discord.
Get live support and talk with other Odin programmers on the Odin Discord.
### Articles
+18 -1
View File
@@ -2,6 +2,21 @@
setlocal EnableDelayedExpansion
where /Q cl.exe || (
set __VSCMD_ARG_NO_LOGO=1
for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
if "!VS!" equ "" (
echo ERROR: Visual Studio installation not found
exit /b 1
)
call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
)
if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported!
exit /b 1
)
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
set CURR_DATE_TIME=%%j
)
@@ -47,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
@@ -79,4 +96,4 @@ if %release_mode% EQU 0 odin run examples/demo
del *.obj > NUL 2> NUL
:end_of_build
:end_of_build
+21 -1
View File
@@ -44,7 +44,13 @@ config_darwin() {
fi
fi
LDFLAGS="$LDFLAGS -liconv -ldl"
MAX_LLVM_VERSION=("14.999.999")
if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
panic "Requirement: llvm-config must be base version smaller than 15"
fi
LDFLAGS="$LDFLAGS -liconv -ldl -framework System"
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS -lLLVM-C"
}
@@ -97,6 +103,12 @@ config_linux() {
panic "Requirement: llvm-config must be base version greater than 11"
fi
MAX_LLVM_VERSION=("14.999.999")
if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
panic "Requirement: llvm-config must be base version smaller than 15"
fi
LDFLAGS="$LDFLAGS -ldl"
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN"
@@ -134,6 +146,14 @@ run_demo() {
./odin run examples/demo/demo.odin -file
}
have_which() {
if ! which which > /dev/null 2>&1; then
panic "Could not find \`which\`"
fi
}
have_which
case $OS in
Linux)
config_linux
+7
View File
@@ -15,3 +15,10 @@ if not exist "vendor\miniaudio\lib\*.lib" (
call build.bat
popd
)
if not exist "vendor\cgltf\lib\*.lib" (
pushd vendor\cgltf\src
call build.bat
popd
)
+15 -2
View File
@@ -14,11 +14,24 @@ when ODIN_OS == .Windows {
// EDOM,
// EILSEQ
// ERANGE
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
when ODIN_OS == .Linux {
@(private="file")
@(default_calling_convention="c")
foreign libc {
@(link_name="__libc_errno_location")
@(link_name="__errno_location")
_get_errno :: proc() -> ^int ---
}
EDOM :: 33
EILSEQ :: 84
ERANGE :: 34
}
when ODIN_OS == .FreeBSD {
@(private="file")
@(default_calling_convention="c")
foreign libc {
@(link_name="__error")
_get_errno :: proc() -> ^int ---
}
+1 -1
View File
@@ -1,4 +1,4 @@
//+ignore
//+build ignore
package gzip
/*
+1 -1
View File
@@ -1,4 +1,4 @@
//+ignore
//+build ignore
package zlib
/*
+63 -18
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,
@@ -8,40 +10,54 @@ Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
}
len :: proc(a: $A/Small_Array) -> int {
len :: proc "contextless" (a: $A/Small_Array) -> int {
return a.len
}
cap :: proc(a: $A/Small_Array) -> int {
cap :: proc "contextless" (a: $A/Small_Array) -> int {
return builtin.len(a.data)
}
space :: proc(a: $A/Small_Array) -> int {
space :: proc "contextless" (a: $A/Small_Array) -> int {
return builtin.len(a.data) - a.len
}
slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
slice :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> []T {
return a.data[:a.len]
}
get :: proc(a: $A/Small_Array($N, $T), index: int) -> T {
get :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> T {
return a.data[index]
}
get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T {
get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
return &a.data[index]
}
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
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
}
resize :: proc(a: ^$A/Small_Array, length: int) {
resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
a.len = min(length, builtin.len(a.data))
}
push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
if a.len < cap(a^) {
a.data[a.len] = item
a.len += 1
@@ -50,7 +66,7 @@ push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
return false
}
push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
if a.len < cap(a^) {
a.len += 1
data := slice(a)
@@ -61,14 +77,14 @@ push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
return false
}
pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
assert(condition=(N > 0 && a.len > 0), loc=loc)
item := a.data[a.len-1]
a.len -= 1
return item
}
pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
assert(condition=(N > 0 && a.len > 0), loc=loc)
item := a.data[0]
s := slice(a)
@@ -77,7 +93,7 @@ pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
return item
}
pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
if N > 0 && a.len > 0 {
item = a.data[a.len-1]
a.len -= 1
@@ -86,31 +102,60 @@ pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
return
}
pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
if N > 0 && a.len > 0 {
item = a.data[0]
s := slice(a)
copy(s[:], s[1:])
a.len -= 1
ok = true
}
}
return
}
consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
assert(condition=a.len >= count, loc=loc)
a.len -= count
}
clear :: proc(a: ^$A/Small_Array($N, $T)) {
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)
}
push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) {
n := copy(a.data[a.len:], items[:])
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}
+11 -14
View File
@@ -257,21 +257,18 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
opt_write_start(w, opt, '{') or_return
if m != nil {
if info.generated_struct == nil {
if info.map_info == nil {
return .Unsupported_Type
}
entries := &m.entries
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
entry_size := ed.elem_size
map_cap := uintptr(runtime.map_cap(m^))
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
for bucket_index in 0..<map_cap {
if !runtime.map_hash_is_valid(hs[bucket_index]) {
continue
}
for i in 0..<entries.len {
opt_write_iteration(w, opt, i) or_return
data := uintptr(entries.data) + uintptr(i*entry_size)
key := rawptr(data + entry_type.offsets[2])
value := rawptr(data + entry_type.offsets[3])
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))
// check for string type
{
@@ -281,13 +278,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
name: string
#partial switch info in ti.variant {
case runtime.Type_Info_String:
case runtime.Type_Info_String:
switch s in a {
case string: name = s
case cstring: name = string(s)
}
opt_write_key(w, opt, name) or_return
case: return .Unsupported_Type
}
}
+3 -6
View File
@@ -399,12 +399,10 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
return UNSUPPORTED_TYPE
}
raw_map := (^mem.Raw_Map)(v.data)
if raw_map.entries.allocator.procedure == nil {
raw_map.entries.allocator = p.allocator
if raw_map.allocator.procedure == nil {
raw_map.allocator = p.allocator
}
header := runtime.__get_map_header_table_runtime(t)
elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
defer delete(elem_backing, p.allocator)
@@ -421,7 +419,6 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
return err
}
key_hash := runtime.default_hasher_string(&key, 0)
key_ptr := rawptr(&key)
key_cstr: cstring
@@ -430,7 +427,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
key_ptr = &key_cstr
}
set_ptr := runtime.__dynamic_map_set(raw_map, header, key_hash, key_ptr, map_backing_value.data)
set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
if set_ptr == nil {
delete(key, p.allocator)
}
+57 -104
View File
@@ -162,7 +162,25 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
p("Panic", message, loc)
}
// formatted printing for cstrings
caprintf :: proc(format: string, args: ..any) -> cstring {
str: strings.Builder
strings.builder_init(&str)
sbprintf(&str, format, ..args)
strings.write_byte(&str, 0)
s := strings.to_string(str)
return cstring(raw_data(s))
}
// c string with temp allocator
ctprintf :: proc(format: string, args: ..any) -> cstring {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
sbprintf(&str, format, ..args)
strings.write_byte(&str, 0)
s := strings.to_string(str)
return cstring(raw_data(s))
}
// sbprint formats using the default print settings and writes to buf
sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
@@ -240,7 +258,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
was_prev_index := false
loop: for i := 0; i < end; /**/ {
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered}
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered, n = fi.n}
prev_i := i
for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') {
@@ -529,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
@@ -660,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
}
@@ -679,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 {
@@ -726,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
}
@@ -745,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 {
@@ -849,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^
@@ -975,7 +942,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
}
}
else {
io.write_string(fi.writer, s[:fi.width], &fi.n)
io.write_string(fi.writer, s, &fi.n)
}
}
else
@@ -2051,41 +2018,27 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
m := (^mem.Raw_Map)(v.data)
if m != nil {
if info.generated_struct == nil {
if info.map_info == nil {
return
}
entries := &m.entries
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
entry_size := ed.elem_size
/*
NOTE: The layout of a `map` is as follows:
map[Key]Value
## Internal Layout
struct {
hashes: []int,
entries: [dynamic]struct{
hash: uintptr,
next: int,
key: Key,
value: Value,
},
map_cap := uintptr(runtime.map_cap(m^))
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
j := 0
for bucket_index in 0..<map_cap {
if !runtime.map_hash_is_valid(hs[bucket_index]) {
continue
}
*/
for i in 0..<entries.len {
if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(entries.data) + uintptr(i*entry_size)
if j > 0 {
io.write_string(fi.writer, ", ", &fi.n)
}
j += 1
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
key := data + entry_type.offsets[2] // key: Key
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
io.write_string(fi.writer, "=", &fi.n)
value := data + entry_type.offsets[3] // value: Value
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
}
}
+3 -3
View File
@@ -16,7 +16,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
w := io.to_writer(os.stream_from_handle(fd))
return wprintln(w=w, args=args, sep=sep)
}
// fprintf formats according to the specififed format string and writes to fd
// fprintf formats according to the specified format string and writes to fd
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
w := io.to_writer(os.stream_from_handle(fd))
return wprintf(w, fmt, ..args)
@@ -34,12 +34,12 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
// println formats using the default print settings and writes to os.stdout
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
// printf formats according to the specififed format string and writes to os.stdout
// printf formats according to the specified format string and writes to os.stdout
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
// eprint formats using the default print settings and writes to os.stderr
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
// eprintln formats using the default print settings and writes to os.stderr
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
// eprintf formats according to the specififed format string and writes to os.stderr
// eprintf formats according to the specified format string and writes to os.stderr
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
+7 -3
View File
@@ -72,8 +72,9 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
return
}
// If you have a choice, prefer fnv32a
@(optimization_mode="speed")
fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed
for b in data {
h = (h * 0x01000193) ~ u32(b)
@@ -81,15 +82,18 @@ fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
return h
}
fnv32 :: fnv32_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
// If you have a choice, prefer fnv64a
@(optimization_mode="speed")
fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed
for b in data {
h = (h * 0x100000001b3) ~ u64(b)
}
return h
}
@(optimization_mode="speed")
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed
+1 -1
View File
@@ -8,7 +8,7 @@
An example of how to use `load`.
*/
//+ignore
//+build ignore
package png
import "core:image"
+4 -1
View File
@@ -1,5 +1,5 @@
// This is purely for documentation
//+ignore
//+build ignore
package intrinsics
// Package-Related
@@ -188,6 +188,9 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
type_map_info :: proc($T: typeid/map[$K]$V) -> ^runtime.Map_Info ---
type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
+7
View File
@@ -43,6 +43,13 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
args = {la.prefix, padding, size, alignment},
location = location,
)
case .Alloc_Non_Zeroed:
logf(
level=la.level,
fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)",
args = {la.prefix, padding, size, alignment},
location = location,
)
case .Free:
if old_size != 0 {
logf(
-2
View File
@@ -25,8 +25,6 @@
TODO: Handle +/- Infinity and NaN.
*/
//+ignore
package math_big
import "core:mem"
+1 -1
View File
@@ -7,7 +7,7 @@
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
//+ignore
//+build ignore
package math_big
import "core:time"
+2
View File
@@ -0,0 +1,2 @@
// core:math/linalg implements linear algebra procedures useful for 3D spatial transformations
package linalg
+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
}
}
+337 -19
View File
@@ -114,6 +114,92 @@ exp :: proc{
exp_f64, exp_f64le, exp_f64be,
}
pow10_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(pow10_f16(f16(x))) }
pow10_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(pow10_f16(f16(x))) }
pow10_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(pow10_f32(f32(x))) }
pow10_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(pow10_f32(f32(x))) }
pow10_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(pow10_f64(f64(x))) }
pow10_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(pow10_f64(f64(x))) }
pow10 :: proc{
pow10_f16, pow10_f16le, pow10_f16be,
pow10_f32, pow10_f32le, pow10_f32be,
pow10_f64, pow10_f64le, pow10_f64be,
}
pow10_f16 :: proc "contextless" (n: f16) -> f16 {
@static pow10_pos_tab := [?]f16{
1e00, 1e01, 1e02, 1e03, 1e04,
}
@static pow10_neg_tab := [?]f16{
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07,
}
if 0 <= n && n <= 4 {
return pow10_pos_tab[uint(n)]
}
if -7 <= n && n <= 0 {
return pow10_neg_tab[uint(-n)]
}
if n > 0 {
return inf_f16(1)
}
return 0
}
pow10_f32 :: proc "contextless" (n: f32) -> f32 {
@static pow10_pos_tab := [?]f32{
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38,
}
@static pow10_neg_tab := [?]f32{
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09,
1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19,
1e-20, 1e-21, 1e-22, 1e-23, 1e-24, 1e-25, 1e-26, 1e-27, 1e-28, 1e-29,
1e-30, 1e-31, 1e-32, 1e-33, 1e-34, 1e-35, 1e-36, 1e-37, 1e-38, 1e-39,
1e-40, 1e-41, 1e-42, 1e-43, 1e-44, 1e-45,
}
if 0 <= n && n <= 38 {
return pow10_pos_tab[uint(n)]
}
if -45 <= n && n <= 0 {
return pow10_neg_tab[uint(-n)]
}
if n > 0 {
return inf_f32(1)
}
return 0
}
pow10_f64 :: proc "contextless" (n: f64) -> f64 {
@static pow10_tab := [?]f64{
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
1e30, 1e31,
}
@static pow10_pos_tab32 := [?]f64{
1e00, 1e32, 1e64, 1e96, 1e128, 1e160, 1e192, 1e224, 1e256, 1e288,
}
@static pow10_neg_tab32 := [?]f64{
1e-00, 1e-32, 1e-64, 1e-96, 1e-128, 1e-160, 1e-192, 1e-224, 1e-256, 1e-288, 1e-320,
}
if 0 <= n && n <= 308 {
return pow10_pos_tab32[uint(n)/32] * pow10_tab[uint(n)%32]
}
if -323 <= n && n <= 0 {
return pow10_neg_tab32[uint(-n)/32] / pow10_tab[uint(-n)%32]
}
if n > 0 {
return inf_f64(1)
}
return 0
}
ldexp_f64 :: proc "contextless" (val: f64, exp: int) -> f64 {
@@ -1088,7 +1174,7 @@ is_nan :: proc{
// If sign < 0, is_inf reports whether f is negative infinity.
// If sign == 0, is_inf reports whether f is either infinity.
is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool {
class := classify(abs(x))
class := classify(x)
switch {
case sign > 0:
return class == .Inf
@@ -1105,7 +1191,7 @@ is_inf_f16be :: proc "contextless" (x: f16be, sign: int = 0) -> bool {
}
is_inf_f32 :: proc "contextless" (x: f32, sign: int = 0) -> bool {
class := classify(abs(x))
class := classify(x)
switch {
case sign > 0:
return class == .Inf
@@ -1122,7 +1208,7 @@ is_inf_f32be :: proc "contextless" (x: f32be, sign: int = 0) -> bool {
}
is_inf_f64 :: proc "contextless" (x: f64, sign: int = 0) -> bool {
class := classify(abs(x))
class := classify(x)
switch {
case sign > 0:
return class == .Inf
@@ -1344,20 +1430,20 @@ atan2_f64 :: proc "contextless" (y, x: f64) -> f64 {
}
return copy_sign(PI, y)
case x == 0:
return copy_sign(PI*0.5, y)
return copy_sign(PI/2, y)
case is_inf(x, 0):
if is_inf(x, 1) {
if is_inf(y, 0) {
return copy_sign(PI*0.25, y)
return copy_sign(PI/4, y)
}
return copy_sign(0, y)
}
if is_inf(y, 0) {
return copy_sign(PI*0.75, y)
return copy_sign(3*PI/4, y)
}
return copy_sign(PI, y)
case is_inf(y, 0):
return copy_sign(PI*0.5, y)
return copy_sign(PI/2, y)
}
q := atan(y / x)
@@ -1379,34 +1465,266 @@ atan2_f64be :: proc "contextless" (y, x: f64be) -> f64be {
}
atan2 :: proc{
atan2_f16, atan2_f16le, atan2_f16be,
atan2_f32, atan2_f32le, atan2_f32be,
atan2_f64, atan2_f64le, atan2_f64be,
atan2_f64, atan2_f32, atan2_f16,
atan2_f64le, atan2_f64be,
atan2_f32le, atan2_f32be,
atan2_f16le, atan2_f16be,
}
atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
return atan2(x, 1)
}
asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
return atan2(x, sqrt(1 - x*x))
asin_f64 :: proc "contextless" (x: f64) -> f64 {
/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
pio2_hi :: 0h3FF921FB54442D18
pio2_lo :: 0h3C91A62633145C07
pS0 :: 0h3FC5555555555555
pS1 :: 0hBFD4D61203EB6F7D
pS2 :: 0h3FC9C1550E884455
pS3 :: 0hBFA48228B5688F3B
pS4 :: 0h3F49EFE07501B288
pS5 :: 0h3F023DE10DFDF709
qS1 :: 0hC0033A271C8A2D4B
qS2 :: 0h40002AE59C598AC8
qS3 :: 0hBFE6066C1B8D0159
qS4 :: 0h3FB3B8C5B12E9282
R :: #force_inline proc "contextless" (z: f64) -> f64 {
p, q: f64
p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
return p/q
}
x := x
z, r, s: f64
dwords := transmute([2]u32)x
hx := dwords[1]
ix := hx & 0x7fffffff
/* |x| >= 1 or nan */
if ix >= 0x3ff00000 {
lx := dwords[0]
if (ix-0x3ff00000 | lx) == 0 {
/* asin(1) = +-pi/2 with inexact */
return x*pio2_hi + 1e-120
}
return 0/(x-x)
}
/* |x| < 0.5 */
if ix < 0x3fe00000 {
/* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */
if ix < 0x3e500000 && ix >= 0x00100000 {
return x
}
return x + x*R(x*x)
}
/* 1 > |x| >= 0.5 */
z = (1 - abs(x))*0.5
s = sqrt(z)
r = R(z)
if ix >= 0x3fef3333 { /* if |x| > 0.975 */
x = pio2_hi-(2*(s+s*r)-pio2_lo)
} else {
f, c: f64
/* f+c = sqrt(z) */
f = s
(^u64)(&f)^ &= 0xffffffff_00000000
c = (z-f*f)/(s+f)
x = 0.5*pio2_hi - (2*s*r - (pio2_lo-2*c) - (0.5*pio2_hi-2*f))
}
return -x if hx >> 31 != 0 else x
}
asin_f64le :: proc "contextless" (x: f64le) -> f64le {
return f64le(asin_f64(f64(x)))
}
asin_f64be :: proc "contextless" (x: f64be) -> f64be {
return f64be(asin_f64(f64(x)))
}
asin_f32 :: proc "contextless" (x: f32) -> f32 {
return f32(asin_f64(f64(x)))
}
asin_f32le :: proc "contextless" (x: f32le) -> f32le {
return f32le(asin_f64(f64(x)))
}
asin_f32be :: proc "contextless" (x: f32be) -> f32be {
return f32be(asin_f64(f64(x)))
}
asin_f16 :: proc "contextless" (x: f16) -> f16 {
return f16(asin_f64(f64(x)))
}
asin_f16le :: proc "contextless" (x: f16le) -> f16le {
return f16le(asin_f64(f64(x)))
}
asin_f16be :: proc "contextless" (x: f16be) -> f16be {
return f16be(asin_f64(f64(x)))
}
asin :: proc{
asin_f64, asin_f32, asin_f16,
asin_f64le, asin_f64be,
asin_f32le, asin_f32be,
asin_f16le, asin_f16be,
}
acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
return 2 * atan2(sqrt(1 - x), sqrt(1 + x))
acos_f64 :: proc "contextless" (x: f64) -> f64 {
/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
pio2_hi :: 0h3FF921FB54442D18
pio2_lo :: 0h3C91A62633145C07
pS0 :: 0h3FC5555555555555
pS1 :: 0hBFD4D61203EB6F7D
pS2 :: 0h3FC9C1550E884455
pS3 :: 0hBFA48228B5688F3B
pS4 :: 0h3F49EFE07501B288
pS5 :: 0h3F023DE10DFDF709
qS1 :: 0hC0033A271C8A2D4B
qS2 :: 0h40002AE59C598AC8
qS3 :: 0hBFE6066C1B8D0159
qS4 :: 0h3FB3B8C5B12E9282
R :: #force_inline proc "contextless" (z: f64) -> f64 {
p, q: f64
p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
return p/q
}
z, w, s, c, df: f64
dwords := transmute([2]u32)x
hx := dwords[1]
ix := hx & 0x7fffffff
/* |x| >= 1 or nan */
if ix >= 0x3ff00000 {
lx := dwords[0]
if (ix-0x3ff00000 | lx) == 0 {
/* acos(1)=0, acos(-1)=pi */
if hx >> 31 != 0 {
return 2*pio2_hi + 1e-120
}
return 0
}
return 0/(x-x)
}
/* |x| < 0.5 */
if ix < 0x3fe00000 {
if ix <= 0x3c600000 { /* |x| < 2**-57 */
return pio2_hi + 1e-120
}
return pio2_hi - (x - (pio2_lo-x*R(x*x)))
}
/* x < -0.5 */
if hx >> 31 != 0 {
z = (1.0+x)*0.5
s = sqrt(z)
w = R(z)*s-pio2_lo
return 2*(pio2_hi - (s+w))
}
/* x > 0.5 */
z = (1.0-x)*0.5
s = sqrt(z)
df = s
(^u64)(&df)^ &= 0xffffffff_00000000
c = (z-df*df)/(s+df)
w = R(z)*s+c
return 2*(df+w)
}
acos_f64le :: proc "contextless" (x: f64le) -> f64le {
return f64le(acos_f64(f64(x)))
}
acos_f64be :: proc "contextless" (x: f64be) -> f64be {
return f64be(acos_f64(f64(x)))
}
acos_f32 :: proc "contextless" (x: f32) -> f32 {
return f32(acos_f64(f64(x)))
}
acos_f32le :: proc "contextless" (x: f32le) -> f32le {
return f32le(acos_f64(f64(x)))
}
acos_f32be :: proc "contextless" (x: f32be) -> f32be {
return f32be(acos_f64(f64(x)))
}
acos_f16 :: proc "contextless" (x: f16) -> f16 {
return f16(acos_f64(f64(x)))
}
acos_f16le :: proc "contextless" (x: f16le) -> f16le {
return f16le(acos_f64(f64(x)))
}
acos_f16be :: proc "contextless" (x: f16be) -> f16be {
return f16be(acos_f64(f64(x)))
}
acos :: proc{
acos_f64, acos_f32, acos_f16,
acos_f64le, acos_f64be,
acos_f32le, acos_f32be,
acos_f16le, acos_f16be,
}
sinh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
return (exp(x) - exp(-x))*0.5
return copy_sign(((exp(x) - exp(-x))*0.5), x)
}
cosh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
return (exp(x) + exp(-x))*0.5
return ((exp(x) + exp(-x))*0.5)
}
tanh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
t := exp(2*x)
return (t - 1) / (t + 1)
tanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
P0 :: -9.64399179425052238628e-1
P1 :: -9.92877231001918586564e1
P2 :: -1.61468768441708447952e3
Q0 :: +1.12811678491632931402e2
Q1 :: +2.23548839060100448583e3
Q2 :: +4.84406305325125486048e3
MAXLOG :: 8.8029691931113054295988e+01 // log(2**127)
x := f64(y)
z := abs(x)
switch {
case z > 0.5*MAXLOG:
if x < 0 {
return -1
}
return 1
case z >= 0.625:
s := exp(2 * z)
z = 1 - 2/(s+1)
if x < 0 {
z = -z
}
case:
if x == 0 {
return T(x)
}
s := x * x
z = x + x*s*((P0*s+P1)*s+P2)/(((s+Q0)*s+Q1)*s+Q2)
}
return T(z)
}
asinh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
+9
View File
@@ -182,3 +182,12 @@ shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
array[i], array[j] = array[j], array[i]
}
}
// Returns a random element from the given slice
choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
n := i64(len(array))
if n < 1 {
return E{}
}
return array[int63_max(n, r)]
}
+25 -17
View File
@@ -69,10 +69,22 @@ alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator :=
return runtime.mem_alloc(size, alignment, allocator, loc)
}
alloc_bytes_non_zeroed :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
return runtime.mem_alloc_non_zeroed(size, alignment, allocator, loc)
}
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return runtime.mem_free(ptr, allocator, loc)
}
free_with_size :: proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil || allocator.procedure == nil {
return nil
}
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
return err
}
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return runtime.mem_free_bytes(bytes, allocator, loc)
}
@@ -108,22 +120,20 @@ query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_locatio
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
free(raw_data(str), allocator, loc)
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return free_with_size(raw_data(str), len(str), allocator, loc)
}
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
free((^byte)(str), allocator, loc)
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return free((^byte)(str), allocator, loc)
}
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
free(raw_data(array), array.allocator, loc)
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
return free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
}
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
free(raw_data(array), allocator, loc)
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
}
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
raw := transmute(Raw_Map)m
delete_slice(raw.hashes, raw.entries.allocator, loc)
free(raw.entries.data, raw.entries.allocator, loc)
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
return runtime.map_free_dynamic(transmute(Raw_Map)m, runtime.map_info(T), loc)
}
@@ -154,8 +164,6 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
return nil, .Out_Of_Memory
}
DEFAULT_RESERVE_CAPACITY :: 16
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) {
runtime.make_slice_error_loc(loc, len)
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return
@@ -169,7 +177,7 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo
return make_aligned(T, len, align_of(E), allocator, loc)
}
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc)
}
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
@@ -184,12 +192,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
array = transmute(T)s
return
}
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1<<runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
runtime.make_map_expr_error_loc(loc, cap)
context.allocator = allocator
m: T
reserve_map(&m, cap)
reserve_map(&m, cap, loc)
return m
}
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
+61 -35
View File
@@ -59,7 +59,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
arena := cast(^Arena)allocator_data
switch mode {
case .Alloc:
case .Alloc, .Alloc_Non_Zeroed:
#no_bounds_check end := &arena.data[arena.offset]
ptr := align_forward(end, uintptr(alignment))
@@ -72,7 +72,9 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
arena.offset += total_size
arena.peak_used = max(arena.peak_used, arena.offset)
zero(ptr, size)
if mode != .Alloc_Non_Zeroed {
zero(ptr, size)
}
return byte_slice(ptr, size), nil
case .Free:
@@ -87,7 +89,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Free_All, .Resize, .Query_Features}
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
}
return nil, nil
@@ -151,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")
@@ -162,7 +164,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size := size
switch mode {
case .Alloc:
case .Alloc, .Alloc_Non_Zeroed:
size = align_forward_int(size, alignment)
switch {
@@ -170,7 +172,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
start := uintptr(raw_data(s.data))
ptr := start + uintptr(s.curr_offset)
ptr = align_forward_uintptr(ptr, uintptr(alignment))
zero(rawptr(ptr), size)
if mode != .Alloc_Non_Zeroed {
zero(rawptr(ptr), size)
}
s.prev_allocation = rawptr(ptr)
offset := int(ptr - start)
@@ -180,7 +184,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case size <= len(s.data):
start := uintptr(raw_data(s.data))
ptr := align_forward_uintptr(start, uintptr(alignment))
zero(rawptr(ptr), size)
if mode != .Alloc_Non_Zeroed {
zero(rawptr(ptr), size)
}
s.prev_allocation = rawptr(ptr)
offset := int(ptr - start)
@@ -211,6 +217,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return ptr, err
case .Free:
if old_memory == nil {
return nil, nil
}
start := uintptr(raw_data(s.data))
end := start + uintptr(len(s.data))
old_ptr := uintptr(old_memory)
@@ -266,7 +275,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
}
return nil, nil
@@ -333,7 +342,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, .Invalid_Argument
}
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
raw_alloc :: proc(s: ^Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset)
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header))
if s.curr_offset + padding + size > len(s.data) {
@@ -351,13 +360,15 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
s.peak_used = max(s.peak_used, s.curr_offset)
zero(rawptr(next_addr), size)
if zero_memory {
zero(rawptr(next_addr), size)
}
return byte_slice(rawptr(next_addr), size), nil
}
switch mode {
case .Alloc:
return raw_alloc(s, size, alignment)
case .Alloc, .Alloc_Non_Zeroed:
return raw_alloc(s, size, alignment, mode == .Alloc)
case .Free:
if old_memory == nil {
return nil, nil
@@ -392,7 +403,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Resize:
if old_memory == nil {
return raw_alloc(s, size, alignment)
return raw_alloc(s, size, alignment, true)
}
if size == 0 {
return nil, nil
@@ -418,7 +429,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
if old_offset != header.prev_offset {
data, err := raw_alloc(s, size, alignment)
data, err := raw_alloc(s, size, alignment, true)
if err == nil {
runtime.copy(data, byte_slice(old_memory, old_size))
}
@@ -439,7 +450,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
}
return nil, nil
case .Query_Info:
@@ -497,7 +508,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2)
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset)
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header))
if s.offset + padding + size > len(s.data) {
@@ -513,13 +524,15 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
s.peak_used = max(s.peak_used, s.offset)
zero(rawptr(next_addr), size)
if zero_memory {
zero(rawptr(next_addr), size)
}
return byte_slice(rawptr(next_addr), size), nil
}
switch mode {
case .Alloc:
return raw_alloc(s, size, align)
case .Alloc, .Alloc_Non_Zeroed:
return raw_alloc(s, size, align, mode == .Alloc)
case .Free:
if old_memory == nil {
return nil, nil
@@ -548,7 +561,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Resize:
if old_memory == nil {
return raw_alloc(s, size, align)
return raw_alloc(s, size, align, true)
}
if size == 0 {
return nil, nil
@@ -571,7 +584,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return byte_slice(old_memory, size), nil
}
data, err := raw_alloc(s, size, align)
data, err := raw_alloc(s, size, align, true)
if err == nil {
runtime.copy(data, byte_slice(old_memory, old_size))
}
@@ -580,7 +593,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
}
return nil, nil
@@ -623,7 +636,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
pool := (^Dynamic_Pool)(allocator_data)
switch mode {
case .Alloc:
case .Alloc, .Alloc_Non_Zeroed:
return dynamic_pool_alloc_bytes(pool, size)
case .Free:
return nil, .Mode_Not_Implemented
@@ -643,7 +656,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info}
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features, .Query_Info}
}
return nil, nil
@@ -794,6 +807,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
if size > 0 {
panic("mem: panic allocator, .Alloc called")
}
case .Alloc_Non_Zeroed:
if size > 0 {
panic("mem: panic allocator, .Alloc_Non_Zeroed called")
}
case .Resize:
if size > 0 {
panic("mem: panic allocator, .Resize called")
@@ -831,6 +848,7 @@ Tracking_Allocator_Entry :: struct {
memory: rawptr,
size: int,
alignment: int,
mode: Allocator_Mode,
err: Allocator_Error,
location: runtime.Source_Code_Location,
}
@@ -849,6 +867,10 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc
t.backing = backing_allocator
t.allocation_map.allocator = internals_allocator
t.bad_free_array.allocator = internals_allocator
if .Free_All in query_features(t.backing) {
t.clear_on_free_all = true
}
}
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
@@ -856,6 +878,13 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
delete(t.bad_free_array)
}
tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
clear(&t.allocation_map)
clear(&t.bad_free_array)
}
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
return Allocator{
data = data,
@@ -865,7 +894,7 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) {
data := (^Tracking_Allocator)(allocator_data)
if mode == .Query_Info {
info := (^Allocator_Query_Info)(old_memory)
@@ -877,21 +906,16 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
info.pointer = nil
}
return nil, nil
return
}
result: []byte
err: Allocator_Error
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
memory = old_memory,
location = loc,
})
} else {
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc)
if err != nil {
return result, err
}
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
}
result_ptr := raw_data(result)
@@ -900,10 +924,11 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
}
switch mode {
case .Alloc:
case .Alloc, .Alloc_Non_Zeroed:
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
memory = result_ptr,
size = size,
mode = mode,
alignment = alignment,
err = err,
location = loc,
@@ -921,6 +946,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
memory = result_ptr,
size = size,
mode = mode,
alignment = alignment,
err = err,
location = loc,
@@ -929,7 +955,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
}
return nil, nil
@@ -937,6 +963,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
unreachable()
}
return result, err
return
}
+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)
+2 -19
View File
@@ -1,5 +1,6 @@
package mem
import "core:builtin"
import "core:runtime"
Raw_Any :: runtime.Raw_Any
@@ -21,22 +22,4 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
return transmute(any)Raw_Any{data, id}
}
raw_array_data :: runtime.raw_array_data
raw_simd_data :: runtime.raw_simd_data
raw_string_data :: runtime.raw_string_data
raw_slice_data :: runtime.raw_slice_data
raw_dynamic_array_data :: runtime.raw_dynamic_array_data
raw_data :: runtime.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),
}
raw_data :: builtin.raw_data
+331
View File
@@ -0,0 +1,331 @@
package mem_virtual
import "core:mem"
Arena_Kind :: enum uint {
Growing = 0, // Chained memory blocks (singly linked list).
Static = 1, // Fixed reservation sized.
Buffer = 2, // Uses a fixed sized buffer.
}
Arena :: struct {
kind: Arena_Kind,
curr_block: ^Memory_Block,
total_used: uint,
total_reserved: uint,
minimum_block_size: uint,
temp_count: uint,
}
// 1 MiB should be enough to start with
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 :: mem.Gigabyte when size_of(uintptr) == 8 else 128 * mem.Megabyte
@(require_results)
arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
arena.kind = .Growing
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
arena.total_used = 0
arena.total_reserved = arena.curr_block.reserved
return
}
@(require_results)
arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEFAULT_ARENA_STATIC_COMMIT_SIZE) -> (err: Allocator_Error) {
arena.kind = .Static
arena.curr_block = memory_block_alloc(commit_size, reserved, {}) or_return
arena.total_used = 0
arena.total_reserved = arena.curr_block.reserved
return
}
@(require_results)
arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Error) {
if len(buffer) < size_of(Memory_Block) {
return .Out_Of_Memory
}
arena.kind = .Buffer
mem.zero_slice(buffer)
block_base := raw_data(buffer)
block := (^Memory_Block)(block_base)
block.base = block_base[size_of(Memory_Block):]
block.reserved = len(buffer) - size_of(Memory_Block)
block.committed = block.reserved
block.used = 0
arena.curr_block = block
arena.total_used = 0
arena.total_reserved = arena.curr_block.reserved
return
}
@(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)
size := size
if size == 0 {
return nil, nil
}
switch arena.kind {
case .Growing:
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.reserved {
size = mem.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)
new_block := memory_block_alloc(size, block_size, {}) or_return
new_block.prev = arena.curr_block
arena.curr_block = new_block
arena.total_reserved += new_block.reserved
}
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
case .Static:
if arena.curr_block == nil {
if arena.minimum_block_size == 0 {
arena.minimum_block_size = DEFAULT_ARENA_STATIC_RESERVE_SIZE
}
arena_init_static(arena=arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return
}
fallthrough
case .Buffer:
if arena.curr_block == nil {
return nil, .Out_Of_Memory
}
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
arena.total_used = arena.curr_block.used
}
return
}
arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool {
if arena.curr_block != nil {
assert(arena.kind != .Growing, "expected a non .Growing arena", loc)
prev_pos := arena.curr_block.used
arena.curr_block.used = clamp(pos, 0, arena.curr_block.reserved)
if prev_pos < pos {
mem.zero_slice(arena.curr_block.base[arena.curr_block.used:][:pos-prev_pos])
}
arena.total_used = arena.curr_block.used
return true
} else if pos == 0 {
arena.total_used = 0
return true
}
return false
}
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.curr_block = free_block.prev
memory_block_dealloc(free_block)
}
}
arena_free_all :: proc(arena: ^Arena) {
switch arena.kind {
case .Growing:
for arena.curr_block != nil {
arena_growing_free_last_memory_block(arena)
}
arena.total_reserved = 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 {
memory_block_dealloc(arena.curr_block)
}
arena.curr_block = nil
arena.total_used = 0
arena.total_reserved = 0
arena.temp_count = 0
}
arena_growing_bootstrap_new :: proc{
arena_growing_bootstrap_new_by_offset,
arena_growing_bootstrap_new_by_name,
}
arena_static_bootstrap_new :: proc{
arena_static_bootstrap_new_by_offset,
arena_static_bootstrap_new_by_name,
}
@(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
bootstrap.kind = .Growing
bootstrap.minimum_block_size = minimum_block_size
data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
ptr = (^T)(raw_data(data))
(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
return
}
@(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)
}
@(require_results)
arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Arena
bootstrap.kind = .Static
bootstrap.minimum_block_size = reserved
data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
ptr = (^T)(raw_data(data))
(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
return
}
@(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)
}
@(require_results)
arena_allocator :: proc(arena: ^Arena) -> mem.Allocator {
return mem.Allocator{arena_allocator_proc, arena}
}
arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.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)
case .Free:
err = .Mode_Not_Implemented
case .Free_All:
arena_free_all(arena)
case .Resize:
old_data := ([^]byte)(old_memory)
switch {
case old_data == nil:
return arena_alloc(arena, size, alignment)
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) or_return
if new_memory == nil {
return
}
copy(new_memory, old_data[:old_size])
return new_memory, nil
case .Query_Features:
set := (^mem.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) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
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_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
}
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)
}
-41
View File
@@ -1,41 +0,0 @@
package mem_virtual
arena_init :: proc{
static_arena_init,
growing_arena_init,
}
arena_temp_begin :: proc{
static_arena_temp_begin,
growing_arena_temp_begin,
}
arena_temp_end :: proc{
static_arena_temp_end,
growing_arena_temp_end,
}
arena_check_temp :: proc{
static_arena_check_temp,
growing_arena_check_temp,
}
arena_allocator :: proc{
static_arena_allocator,
growing_arena_allocator,
}
arena_alloc :: proc{
static_arena_alloc,
growing_arena_alloc,
}
arena_free_all :: proc{
static_arena_free_all,
growing_arena_free_all,
}
arena_destroy :: proc{
static_arena_destroy,
growing_arena_destroy,
}
-171
View File
@@ -1,171 +0,0 @@
package mem_virtual
import "core:mem"
Growing_Arena :: struct {
curr_block: ^Memory_Block,
total_used: uint,
total_reserved: uint,
minimum_block_size: uint,
temp_count: int,
}
DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
growing_arena_init :: proc(arena: ^Growing_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
arena.total_used = 0
arena.total_reserved = arena.curr_block.reserved
return
}
growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check {
alignment_offset := uint(0)
ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
mask := uintptr(alignment-1)
if ptr & mask != 0 {
alignment_offset = uint(alignment) - uint(ptr & mask)
}
return alignment_offset
}
assert(mem.is_power_of_two(uintptr(alignment)))
size := uint(0)
if arena.curr_block != nil {
size = uint(min_size) + align_forward_offset(arena, alignment)
}
if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.reserved {
size = uint(mem.align_forward_int(min_size, alignment))
arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size)
block_size := max(size, arena.minimum_block_size)
new_block := memory_block_alloc(size, block_size, {}) or_return
new_block.prev = arena.curr_block
arena.curr_block = new_block
arena.total_reserved += new_block.reserved
}
data, err = alloc_from_memory_block(arena.curr_block, int(size), alignment)
if err == nil {
arena.total_used += size
}
return
}
growing_arena_free_last_memory_block :: proc(arena: ^Growing_Arena) {
free_block := arena.curr_block
arena.curr_block = free_block.prev
memory_block_dealloc(free_block)
}
growing_arena_free_all :: proc(arena: ^Growing_Arena) {
for arena.curr_block != nil {
growing_arena_free_last_memory_block(arena)
}
arena.total_used = 0
arena.total_reserved = 0
}
growing_arena_destroy :: proc(arena: ^Growing_Arena) {
growing_arena_free_all(arena)
}
growing_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Growing_Arena
bootstrap.minimum_block_size = minimum_block_size
data := growing_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
ptr = (^T)(raw_data(data))
(^Growing_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
return
}
growing_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
return growing_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
}
growing_arena_bootstrap_new :: proc{
growing_arena_bootstrap_new_by_offset,
growing_arena_bootstrap_new_by_name,
}
growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
return mem.Allocator{growing_arena_allocator_proc, arena}
}
growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
arena := (^Growing_Arena)(allocator_data)
switch mode {
case .Alloc:
return growing_arena_alloc(arena, size, alignment)
case .Free:
err = .Mode_Not_Implemented
return
case .Free_All:
growing_arena_free_all(arena)
return
case .Resize:
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, growing_arena_allocator(arena), location)
case .Query_Features, .Query_Info:
err = .Mode_Not_Implemented
return
}
err = .Mode_Not_Implemented
return
}
Growing_Arena_Temp :: struct {
arena: ^Growing_Arena,
block: ^Memory_Block,
used: uint,
}
growing_arena_temp_begin :: proc(arena: ^Growing_Arena) -> (temp: Growing_Arena_Temp) {
temp.arena = arena
temp.block = arena.curr_block
if arena.curr_block != nil {
temp.used = arena.curr_block.used
}
arena.temp_count += 1
return
}
growing_arena_temp_end :: proc(temp: Growing_Arena_Temp, loc := #caller_location) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
for arena.curr_block != temp.block {
growing_arena_free_last_memory_block(arena)
}
if block := arena.curr_block; block != nil {
assert(block.used >= temp.used, "out of order use of growing_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 growing_arena_temp_end", loc)
arena.temp_count -= 1
}
growing_arena_check_temp :: proc(arena: ^Growing_Arena, loc := #caller_location) {
assert(arena.temp_count == 0, "Growing_Arena_Temp not been ended", loc)
}
-153
View File
@@ -1,153 +0,0 @@
package mem_virtual
import "core:mem"
Static_Arena :: struct {
block: ^Memory_Block,
total_used: uint,
total_reserved: uint,
minimum_block_size: uint,
temp_count: int,
}
STATIC_ARENA_DEFAULT_COMMIT_SIZE :: 1<<20 // 1 MiB should be enough to start with
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
STATIC_ARENA_DEFAULT_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
static_arena_init :: proc(arena: ^Static_Arena, reserved: uint, commit_size: uint = STATIC_ARENA_DEFAULT_COMMIT_SIZE) -> (err: Allocator_Error) {
arena.block = memory_block_alloc(commit_size, reserved, {}) or_return
arena.total_used = 0
arena.total_reserved = arena.block.reserved
return
}
static_arena_destroy :: proc(arena: ^Static_Arena) {
memory_block_dealloc(arena.block)
arena^ = {}
}
static_arena_alloc :: proc(arena: ^Static_Arena, size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
align_forward :: #force_inline proc "contextless" (ptr: uint, align: uint) -> uint {
mask := align-1
return (ptr + mask) &~ mask
}
if arena.block == nil {
reserve_size := max(arena.minimum_block_size, STATIC_ARENA_DEFAULT_RESERVE_SIZE)
static_arena_init(arena, reserve_size, STATIC_ARENA_DEFAULT_COMMIT_SIZE) or_return
}
MINIMUM_ALIGN :: 2*align_of(uintptr)
defer arena.total_used = arena.block.used
return alloc_from_memory_block(arena.block, size, max(MINIMUM_ALIGN, alignment))
}
static_arena_reset_to :: proc(arena: ^Static_Arena, pos: uint) -> bool {
if arena.block != nil {
prev_pos := arena.block.used
arena.block.used = clamp(pos, 0, arena.block.reserved)
if prev_pos < pos {
mem.zero_slice(arena.block.base[arena.block.used:][:pos-prev_pos])
}
return true
} else if pos == 0 {
return true
}
return false
}
static_arena_free_all :: proc(arena: ^Static_Arena) {
static_arena_reset_to(arena, 0)
}
static_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Static_Arena
bootstrap.minimum_block_size = reserved
data := static_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
ptr = (^T)(raw_data(data))
(^Static_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
return
}
static_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
return static_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
}
static_arena_bootstrap_new :: proc{
static_arena_bootstrap_new_by_offset,
static_arena_bootstrap_new_by_name,
}
static_arena_allocator :: proc(arena: ^Static_Arena) -> mem.Allocator {
return mem.Allocator{static_arena_allocator_proc, arena}
}
static_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
arena := (^Static_Arena)(allocator_data)
switch mode {
case .Alloc:
return static_arena_alloc(arena, size, alignment)
case .Free:
err = .Mode_Not_Implemented
return
case .Free_All:
static_arena_free_all(arena)
return
case .Resize:
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, static_arena_allocator(arena), location)
case .Query_Features, .Query_Info:
err = .Mode_Not_Implemented
return
}
err = .Mode_Not_Implemented
return
}
Static_Arena_Temp :: struct {
arena: ^Static_Arena,
used: uint,
}
static_arena_temp_begin :: proc(arena: ^Static_Arena) -> (temp: Static_Arena_Temp) {
temp.arena = arena
temp.used = arena.block.used if arena.block != nil else 0
arena.temp_count += 1
return
}
static_arena_temp_end :: proc(temp: Static_Arena_Temp, loc := #caller_location) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
used := arena.block.used if arena.block != nil else 0
assert(temp.used >= used, "invalid Static_Arena_Temp", loc)
static_arena_reset_to(arena, temp.used)
assert(arena.temp_count > 0, "double-use of static_arena_temp_end", loc)
arena.temp_count -= 1
}
static_arena_check_temp :: proc(arena: ^Static_Arena, loc := #caller_location) {
assert(arena.temp_count == 0, "Static_Arena_Temp not been ended", loc)
}
+18 -15
View File
@@ -1,6 +1,7 @@
package mem_virtual
import "core:mem"
import "core:intrinsics"
DEFAULT_PAGE_SIZE := uint(4096)
@@ -95,18 +96,11 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
pmblock.block.committed = committed
pmblock.block.reserved = reserved
sentinel := &global_platform_memory_block_sentinel
platform_mutex_lock()
pmblock.next = sentinel
pmblock.prev = sentinel.prev
pmblock.prev.next = pmblock
pmblock.next.prev = pmblock
platform_mutex_unlock()
return &pmblock.block, nil
}
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
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:])
@@ -134,11 +128,18 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
return nil
}
if block == nil {
return nil, .Out_Of_Memory
}
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
size := uint(min_size) + alignment_offset
size, size_ok := safe_add(min_size, alignment_offset)
if !size_ok {
err = .Out_Of_Memory
return
}
if block.used + size > block.reserved {
if to_be_used, ok := safe_add(block.used, size); !ok || to_be_used > block.reserved {
err = .Out_Of_Memory
return
}
@@ -153,12 +154,14 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
if block := (^Platform_Memory_Block)(block_to_free); block != nil {
platform_mutex_lock()
block.prev.next = block.next
block.next.prev = block.prev
platform_mutex_unlock()
platform_memory_free(block)
}
}
@(private)
safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
z, did_overflow := intrinsics.overflow_add(x, y)
return z, !did_overflow
}
-26
View File
@@ -1,13 +1,10 @@
//+private
package mem_virtual
import "core:sync"
Platform_Memory_Block :: struct {
block: Memory_Block,
committed: uint,
reserved: uint,
prev, next: ^Platform_Memory_Block,
}
platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
@@ -33,28 +30,6 @@ platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
}
}
platform_mutex_lock :: proc() {
sync.mutex_lock(&global_memory_block_mutex)
}
platform_mutex_unlock :: proc() {
sync.mutex_unlock(&global_memory_block_mutex)
}
global_memory_block_mutex: sync.Mutex
global_platform_memory_block_sentinel: Platform_Memory_Block
global_platform_memory_block_sentinel_set: bool
@(init)
platform_memory_init :: proc() {
if !global_platform_memory_block_sentinel_set {
_platform_memory_init()
global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel
global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel
global_platform_memory_block_sentinel_set = true
}
}
platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
if to_commit < block.committed {
return nil
@@ -63,7 +38,6 @@ platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_
return .Out_Of_Memory
}
commit(block, to_commit) or_return
block.committed = to_commit
return nil
+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 -4
View File
@@ -2,7 +2,6 @@ package os
import win32 "core:sys/windows"
import "core:strings"
import "core:time"
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) {
@@ -41,9 +40,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
// fi.mode |= file_type_mode(h);
}
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
windows_set_file_info_times(&fi, d)
fi.is_dir = fi.mode & File_Mode_Dir != 0
return
+4 -2
View File
@@ -162,7 +162,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
total_read: int
length := len(data)
to_read := min(win32.DWORD(length), MAX_RW)
// NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
to_read := min(i64(length), MAX_RW)
e: win32.BOOL
if is_console {
@@ -172,7 +173,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
return int(total_read), err
}
} else {
e = win32.ReadFile(handle, &data[total_read], to_read, &single_read_length, nil)
// NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
e = win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &single_read_length, nil)
}
if single_read_length <= 0 || !e {
err := Errno(win32.GetLastError())
+9 -5
View File
@@ -178,7 +178,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
// the pointer we return to the user.
//
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, mem.Allocator_Error) {
a := max(alignment, align_of(rawptr))
space := size + a - 1
@@ -187,7 +187,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
} else {
allocated_mem = heap_alloc(space+size_of(rawptr))
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
}
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
@@ -226,8 +226,8 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
}
switch mode {
case .Alloc:
return aligned_alloc(size, alignment)
case .Alloc, .Alloc_Non_Zeroed:
return aligned_alloc(size, alignment, nil, mode == .Alloc)
case .Free:
aligned_free(old_memory)
@@ -244,7 +244,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
case .Query_Features:
set := (^mem.Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Free, .Resize, .Query_Features}
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
}
return nil, nil
@@ -261,3 +261,7 @@ heap_allocator :: proc() -> mem.Allocator {
data = nil,
}
}
processor_core_count :: proc() -> int {
return _processor_core_count()
}
+1 -1
View File
@@ -130,7 +130,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
f := new(File, _file_allocator())
f.impl.allocator = _file_allocator()
f.impl.fd = rawptr(fd)
f.impl.fd = rawptr(handle)
f.impl.name = strings.clone(name, f.impl.allocator)
f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
+1 -1
View File
@@ -191,7 +191,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
}
switch mode {
case .Alloc:
case .Alloc, .Alloc_Non_Zeroed:
return aligned_alloc(size, alignment)
case .Free:
+11 -11
View File
@@ -4,17 +4,17 @@ package os2
import "core:mem"
import win32 "core:sys/windows"
heap_alloc :: proc(size: int) -> rawptr {
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size))
heap_alloc :: proc(size: int, zero_memory: bool) -> rawptr {
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
heap_resize :: proc(ptr: rawptr, new_size: int, zero_memory: bool) -> rawptr {
if new_size == 0 {
heap_free(ptr)
return nil
}
if ptr == nil {
return heap_alloc(new_size)
return heap_alloc(new_size, zero_memory)
}
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, uint(new_size))
@@ -36,16 +36,16 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
// the pointer we return to the user.
//
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
aligned_alloc :: proc(size, alignment: int, zero_memory: bool, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
a := max(alignment, align_of(rawptr))
space := size + a - 1
allocated_mem: rawptr
if old_ptr != nil {
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr), zero_memory)
} else {
allocated_mem = heap_alloc(space+size_of(rawptr))
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
}
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
@@ -72,12 +72,12 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
if p == nil {
return nil, nil
}
return aligned_alloc(new_size, new_alignment, p)
return aligned_alloc(new_size, new_alignment, true, p)
}
switch mode {
case .Alloc:
return aligned_alloc(size, alignment)
case .Alloc, .Alloc_Non_Zeroed:
return aligned_alloc(size, alignment, mode == .Alloc)
case .Free:
aligned_free(old_memory)
@@ -87,7 +87,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
case .Resize:
if old_memory == nil {
return aligned_alloc(size, alignment)
return aligned_alloc(size, alignment, true)
}
return aligned_resize(old_memory, old_size, size, alignment)
+43 -9
View File
@@ -314,6 +314,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 +334,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 +343,33 @@ 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
}
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
}
@@ -404,6 +417,9 @@ read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
if bytes_read == -1 {
return bytes_read_total, 1
}
if bytes_read == 0 {
break
}
bytes_read_total += bytes_read
}
@@ -643,9 +659,15 @@ access :: proc(path: string, mask: int) -> bool {
return _unix_access(cstr, mask) == 0
}
heap_alloc :: proc(size: int) -> rawptr {
assert(size > 0)
return _unix_calloc(1, size)
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
if size <= 0 {
return nil
}
if zero_memory {
return _unix_calloc(1, size)
} else {
return _unix_malloc(size)
}
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
@@ -750,6 +772,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__))
+2 -2
View File
@@ -18,8 +18,8 @@ current_thread_id :: proc "contextless" () -> int {
return (int) (es.ThreadGetID(es.CURRENT_THREAD));
}
heap_alloc :: proc(size: int) -> rawptr {
return es.HeapAllocate(size, false);
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
return es.HeapAllocate(size, zero_memory);
}
heap_free :: proc(ptr: rawptr) {
+24 -4
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,7 +304,7 @@ is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
get_last_error :: proc() -> int {
get_last_error :: proc "contextless" () -> int {
return __errno_location()^
}
@@ -603,9 +604,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
return true, ERROR_NONE
}
heap_alloc :: proc(size: int) -> rawptr {
assert(size >= 0)
return _unix_calloc(1, c.size_t(size))
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
if size <= 0 {
return nil
}
if zero_memory {
return _unix_calloc(1, c.size_t(size))
} else {
return _unix_malloc(c.size_t(size))
}
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
@@ -696,6 +703,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__))
+15 -4
View File
@@ -404,6 +404,7 @@ 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) ---
@@ -441,7 +442,7 @@ _get_errno :: proc(res: int) -> Errno {
}
// get errno from libc
get_last_error :: proc() -> int {
get_last_error :: proc "contextless" () -> int {
return __errno_location()^
}
@@ -755,9 +756,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
return true, ERROR_NONE
}
heap_alloc :: proc(size: int) -> rawptr {
assert(size >= 0)
return _unix_calloc(1, c.size_t(size))
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
if size <= 0 {
return nil
}
if zero_memory {
return _unix_calloc(1, c.size_t(size))
} else {
return _unix_malloc(c.size_t(size))
}
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
@@ -872,6 +879,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__))
+18 -5
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()^
}
@@ -605,9 +606,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
return true, ERROR_NONE
}
heap_alloc :: proc(size: int) -> rawptr {
assert(size >= 0)
return _unix_calloc(1, c.size_t(size))
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
if size <= 0 {
return nil
}
if zero_memory {
return _unix_calloc(1, c.size_t(size))
} else {
return _unix_malloc(c.size_t(size))
}
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
@@ -698,6 +705,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__))
@@ -705,4 +718,4 @@ _alloc_command_line_arguments :: proc() -> []string {
res[i] = string(arg)
}
return res
}
}
+37 -5
View File
@@ -24,7 +24,7 @@ O_CLOEXEC :: 0x80000
stdin: Handle = 0
stdout: Handle = 1
stderr: Handle = 2
current_dir: Handle = 3
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
iovs := wasi.ciovec_t(data)
@@ -47,7 +47,36 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
return int(n), Errno(err)
}
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
return 0, -1
oflags: wasi.oflags_t
if mode & O_CREATE == O_CREATE {
oflags += {.CREATE}
}
if mode & O_EXCL == O_EXCL {
oflags += {.EXCL}
}
if mode & O_TRUNC == O_TRUNC {
oflags += {.TRUNC}
}
rights: wasi.rights_t = {.FD_SEEK, .FD_FILESTAT_GET}
switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
case O_RDONLY: rights += {.FD_READ}
case O_WRONLY: rights += {.FD_WRITE}
case O_RDWR: rights += {.FD_READ, .FD_WRITE}
}
fdflags: wasi.fdflags_t
if mode & O_APPEND == O_APPEND {
fdflags += {.APPEND}
}
if mode & O_NONBLOCK == O_NONBLOCK {
fdflags += {.NONBLOCK}
}
if mode & O_SYNC == O_SYNC {
fdflags += {.SYNC}
}
fd, err := wasi.path_open(wasi.fd_t(current_dir),{.SYMLINK_FOLLOW},path,oflags,rights,{},fdflags)
return Handle(fd), Errno(err)
}
close :: proc(fd: Handle) -> Errno {
err := wasi.fd_close(wasi.fd_t(fd))
@@ -60,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))
@@ -72,7 +104,7 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
heap_alloc :: proc(size: int) -> rawptr {
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
return nil
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
@@ -96,4 +128,4 @@ heap_free :: proc(ptr: rawptr) {
exit :: proc "contextless" (code: int) -> ! {
runtime._cleanup_runtime_contextless()
wasi.proc_exit(wasi.exitcode_t(code))
}
}
+25 -3
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
@@ -91,8 +92,8 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
heap_alloc :: proc(size: int) -> rawptr {
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size))
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
if new_size == 0 {
@@ -126,7 +127,28 @@ 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 {
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 +236,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)
}
}
+10 -10
View File
@@ -228,6 +228,13 @@ file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HAN
return
}
@(private)
windows_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) {
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
}
@(private)
file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
@@ -235,9 +242,7 @@ file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.is_dir = fi.mode & File_Mode_Dir != 0
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
windows_set_file_info_times(&fi, d)
fi.fullpath, e = full_path_from_name(name)
fi.name = basename(fi.fullpath)
@@ -252,9 +257,7 @@ file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string)
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.is_dir = fi.mode & File_Mode_Dir != 0
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
windows_set_file_info_times(&fi, d)
fi.fullpath, e = full_path_from_name(name)
fi.name = basename(fi.fullpath)
@@ -290,10 +293,7 @@ file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HAN
fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag)
fi.is_dir = fi.mode & File_Mode_Dir != 0
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
windows_set_file_info_times(&fi, &d)
return fi, ERROR_NONE
}
+8 -8
View File
@@ -14,7 +14,7 @@ import "core:slice"
// The sole exception is if 'skip_dir' is returned as true:
// when 'skip_dir' is invoked on a directory. 'walk' skips directory contents
// when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory
Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip_dir: bool)
Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno, user_data: rawptr) -> (err: os.Errno, skip_dir: bool)
// walk walks the file tree rooted at 'root', calling 'walk_proc' for each file or directory in the tree, including 'root'
// All errors that happen visiting files and directories are filtered by walk_proc
@@ -22,28 +22,28 @@ Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno,
// NOTE: Walking large directories can be inefficient due to the lexical sort
// NOTE: walk does not follow symbolic links
// NOTE: os.File_Info uses the 'context.temp_allocator' to allocate, and will delete when it is done
walk :: proc(root: string, walk_proc: Walk_Proc) -> os.Errno {
walk :: proc(root: string, walk_proc: Walk_Proc, user_data: rawptr) -> os.Errno {
info, err := os.lstat(root, context.temp_allocator)
defer os.file_info_delete(info, context.temp_allocator)
skip_dir: bool
if err != 0 {
err, skip_dir = walk_proc(info, err)
err, skip_dir = walk_proc(info, err, user_data)
} else {
err, skip_dir = _walk(info, walk_proc)
err, skip_dir = _walk(info, walk_proc, user_data)
}
return 0 if skip_dir else err
}
@(private)
_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_dir: bool) {
_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (err: os.Errno, skip_dir: bool) {
if !info.is_dir {
if info.fullpath == "" && info.name == "" {
// ignore empty things
return
}
return walk_proc(info, 0)
return walk_proc(info, 0, user_data)
}
fis: []os.File_Info
@@ -51,14 +51,14 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_
fis, err = read_dir(info.fullpath, context.temp_allocator)
defer os.file_info_slice_delete(fis, context.temp_allocator)
err1, skip_dir = walk_proc(info, err)
err1, skip_dir = walk_proc(info, err, user_data)
if err != 0 || err1 != 0 || skip_dir {
err = err1
return
}
for fi in fis {
err, skip_dir = _walk(fi, walk_proc)
err, skip_dir = _walk(fi, walk_proc, user_data)
if err != 0 || skip_dir {
if !fi.is_dir || !skip_dir {
return
+76
View File
@@ -0,0 +1,76 @@
package reflect
import "core:runtime"
iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
if val == nil || it == nil {
return
}
ti := type_info_base(type_info_of(val.id))
#partial switch info in ti.variant {
case Type_Info_Pointer:
if ptr := (^rawptr)(val.data)^; ptr != nil {
return iterate_array(any{ptr, info.elem.id}, it)
}
case Type_Info_Array:
if it^ < info.count {
elem.data = rawptr(uintptr(val.data) + uintptr(it^ * info.elem_size))
elem.id = info.elem.id
ok = true
it^ += 1
}
case Type_Info_Slice:
array := (^runtime.Raw_Slice)(val.data)
if it^ < array.len {
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
elem.id = info.elem.id
ok = true
it^ += 1
}
case Type_Info_Dynamic_Array:
array := (^runtime.Raw_Dynamic_Array)(val.data)
if it^ < array.len {
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
elem.id = info.elem.id
ok = true
it^ += 1
}
}
return
}
iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
if val == nil || it == nil {
return
}
ti := type_info_base(type_info_of(val.id))
#partial switch info in ti.variant {
case Type_Info_Pointer:
if ptr := (^rawptr)(val.data)^; ptr != nil {
return iterate_map(any{ptr, info.elem.id}, it)
}
case Type_Info_Map:
if info.map_info == nil {
break
}
rm := (^runtime.Raw_Map)(val.data)
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(rm^, info.map_info)
for /**/ ; it^ < int(runtime.map_cap(rm^)); it^ += 1 {
if hash := hs[it^]; runtime.map_hash_is_valid(hash) {
key_ptr := runtime.map_cell_index_dynamic(ks, info.map_info.ks, uintptr(it^))
value_ptr := runtime.map_cell_index_dynamic(vs, info.map_info.vs, uintptr(it^))
key.data = rawptr(key_ptr)
value.data = rawptr(value_ptr)
key.id = info.key.id
value.id = info.value.id
ok = true
break
}
}
}
return
}
-42
View File
@@ -1,42 +0,0 @@
package reflect
import "core:runtime"
import "core:mem"
_ :: runtime
_ :: mem
Map_Entry_Info :: struct($Key, $Value: typeid) {
hash: uintptr,
key: Key,
value: Value,
}
map_entry_info_slice :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
m := m
rm := (^mem.Raw_Map)(&m)
info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
gs := type_info_base(info.generated_struct).variant.(Type_Info_Struct)
ed := type_info_base(gs.types[1]).variant.(Type_Info_Dynamic_Array)
entry_type := ed.elem.variant.(Type_Info_Struct)
key_offset := entry_type.offsets[2]
value_offset := entry_type.offsets[3]
entry_size := uintptr(ed.elem_size)
entries = make(type_of(entries), rm.entries.len)
data := uintptr(rm.entries.data)
for i in 0..<rm.entries.len {
header := (^runtime.Map_Entry_Header)(data)
hash := header.hash
key := (^K)(data + key_offset)^
value := (^V)(data + value_offset)^
entries[i] = {hash, key, value}
data += entry_size
}
return entries
}
+9 -38
View File
@@ -123,46 +123,17 @@ backing_type_kind :: proc(T: typeid) -> Type_Kind {
}
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
any_base :: proc(v: any) -> any {
v := v
@@ -273,7 +244,7 @@ length :: proc(val: any) -> int {
return (^runtime.Raw_Dynamic_Array)(val.data).len
case Type_Info_Map:
return (^runtime.Raw_Map)(val.data).entries.len
return runtime.map_len((^runtime.Raw_Map)(val.data)^)
case Type_Info_String:
if a.is_cstring {
@@ -305,7 +276,7 @@ capacity :: proc(val: any) -> int {
return (^runtime.Raw_Dynamic_Array)(val.data).cap
case Type_Info_Map:
return (^runtime.Raw_Map)(val.data).entries.cap
return runtime.map_cap((^runtime.Raw_Map)(val.data)^)
}
return 0
}
+35 -7
View File
@@ -143,11 +143,9 @@ Type_Info_Enum :: struct {
values: []Type_Info_Enum_Value,
}
Type_Info_Map :: struct {
key: ^Type_Info,
value: ^Type_Info,
generated_struct: ^Type_Info,
key_equal: Equal_Proc,
key_hasher: Hasher_Proc,
key: ^Type_Info,
value: ^Type_Info,
map_info: ^Map_Info,
}
Type_Info_Bit_Set :: struct {
elem: ^Type_Info,
@@ -303,6 +301,7 @@ Allocator_Mode :: enum byte {
Resize,
Query_Features,
Query_Info,
Alloc_Non_Zeroed,
}
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
@@ -330,6 +329,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 {
@@ -393,9 +398,32 @@ Raw_Dynamic_Array :: struct {
allocator: Allocator,
}
// The raw, type-erased representation of a map.
//
// 32-bytes on 64-bit
// 16-bytes on 32-bit
Raw_Map :: struct {
hashes: []Map_Index,
entries: Raw_Dynamic_Array,
// A single allocation spanning all keys, values, and hashes.
// {
// k: Map_Cell(K) * (capacity / ks_per_cell)
// v: Map_Cell(V) * (capacity / vs_per_cell)
// h: Map_Cell(H) * (capacity / hs_per_cell)
// }
//
// The data is allocated assuming 64-byte alignment, meaning the address is
// always a multiple of 64. This means we have 6 bits of zeros in the pointer
// to store the capacity. We can store a value as large as 2^6-1 or 63 in
// there. This conveniently is the maximum log2 capacity we can have for a map
// as Odin uses signed integers to represent capacity.
//
// Since the hashes are backed by Map_Hash, which is just a 64-bit unsigned
// integer, the cell structure for hashes is unnecessary because 64/8 is 8 and
// requires no padding, meaning it can be indexed as a regular array of
// Map_Hash directly, though for consistency sake it's written as if it were
// an array of Map_Cell(Map_Hash).
data: uintptr, // 8-bytes on 64-bits, 4-bytes on 32-bits
len: int, // 8-bytes on 64-bits, 4-bytes on 32-bits
allocator: Allocator, // 16-bytes on 64-bits, 8-bytes on 32-bits
}
Raw_Any :: struct {
+18 -92
View File
@@ -159,20 +159,7 @@ delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #cal
}
@builtin
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
Entry :: struct {
hash: uintptr,
next: int,
key: K,
value: V,
}
raw := transmute(Raw_Map)m
err := delete_slice(raw.hashes, raw.entries.allocator, loc)
err1 := mem_free_with_size(raw.entries.data, raw.entries.cap*size_of(Entry), raw.entries.allocator, loc)
if err == nil {
err = err1
}
return err
return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc)
}
@@ -244,12 +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 cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
make_map_expr_error_loc(loc, cap)
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_expr_error_loc(loc, capacity)
context.allocator = allocator
m: T
reserve_map(&m, cap)
reserve_map(&m, capacity, loc)
return m
}
@(builtin)
@@ -285,36 +272,24 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
if m == nil {
return
}
raw_map := (^Raw_Map)(m)
entries := (^Raw_Dynamic_Array)(&raw_map.entries)
entries.len = 0
for _, i in raw_map.hashes {
raw_map.hashes[i] = MAP_SENTINEL
}
map_clear_dynamic((^Raw_Map)(m), map_info(T))
}
@builtin
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
if m != nil {
h := __get_map_header_table(T)
__dynamic_map_reserve(m, h, uint(capacity), loc)
__dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc)
}
}
/*
Shrinks the capacity of a map down to the current length, or the given capacity.
If `new_cap` is negative, then `len(m)` is used.
Returns false if `cap(m) < new_cap`, or the allocator report failure.
If `len(m) < new_cap`, then `len(m)` will be left unchanged.
Shrinks the capacity of a map down to the current length.
*/
@builtin
shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool) {
if m != nil {
new_cap := new_cap if new_cap >= 0 else len(m)
return __dynamic_map_shrink(__get_map_header(m), new_cap, loc)
err := map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
did_shrink = err == nil
}
return
}
@@ -325,14 +300,10 @@ shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) ->
delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) {
if m != nil {
key := key
h := __get_map_header(m)
fr := __map_find(h, &key)
if fr.entry_index != MAP_SENTINEL {
entry := __dynamic_map_get_entry(h, fr.entry_index)
deleted_key = (^K)(uintptr(entry)+h.key_offset)^
deleted_value = (^V)(uintptr(entry)+h.value_offset)^
__dynamic_map_erase(h, fr)
old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key))
if ok {
deleted_key = (^K)(old_k)^
deleted_value = (^V)(old_v)^
}
}
return
@@ -573,10 +544,7 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
new_size := capacity * size_of(E)
allocator := a.allocator
new_data, err := allocator.procedure(
allocator.data, .Resize, new_size, align_of(E),
a.data, old_size, loc,
)
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
if new_data == nil || err != nil {
return false
}
@@ -607,10 +575,7 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
new_size := length * size_of(E)
allocator := a.allocator
new_data, err := allocator.procedure(
allocator.data, .Resize, new_size, align_of(E),
a.data, old_size, loc,
)
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
if new_data == nil || err != nil {
return false
}
@@ -650,15 +615,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 := a.allocator.procedure(
a.allocator.data,
.Resize,
new_size,
align_of(E),
a.data,
old_size,
loc,
)
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), a.allocator, loc)
if err != nil {
return
}
@@ -672,10 +629,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
@builtin
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
key, value := key, value
h := __get_map_header_table(T)
e := __dynamic_map_set(m, h, __get_map_key_hash(&key), &key, &value, loc)
return (^V)(uintptr(e) + h.value_offset)
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
}
@@ -731,34 +685,6 @@ card :: proc(s: $S/bit_set[$E; $U]) -> int {
@builtin
raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> [^]E {
return ([^]E)(a)
}
@builtin
raw_simd_data :: proc "contextless" (a: $P/^($T/#simd[$N]$E)) -> [^]E {
return ([^]E)(a)
}
@builtin
raw_slice_data :: proc "contextless" (s: $S/[]$E) -> [^]E {
ptr := (transmute(Raw_Slice)s).data
return ([^]E)(ptr)
}
@builtin
raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> [^]E {
ptr := (transmute(Raw_Dynamic_Array)s).data
return ([^]E)(ptr)
}
@builtin
raw_string_data :: proc "contextless" (s: $S/string) -> [^]u8 {
return (transmute(Raw_String)s).data
}
@builtin
raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data, raw_simd_data}
@builtin
@(disabled=ODIN_DISABLE_ASSERT)
assert :: proc(condition: bool, message := "", loc := #caller_location) {
+1 -1
View File
@@ -4,7 +4,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
switch mode {
case .Alloc:
case .Alloc, .Alloc_Non_Zeroed:
return nil, .Out_Of_Memory
case .Free:
return nil, .None
+3 -3
View File
@@ -10,8 +10,8 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
switch mode {
case .Alloc:
data, err = _windows_default_alloc(size, alignment)
case .Alloc, .Alloc_Non_Zeroed:
data, err = _windows_default_alloc(size, alignment, mode == .Alloc)
case .Free:
_windows_default_free(old_memory)
@@ -25,7 +25,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Free, .Resize, .Query_Features}
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
}
case .Query_Info:
@@ -1,6 +1,6 @@
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 {
@@ -167,7 +167,7 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
}
switch mode {
case .Alloc:
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)
@@ -181,7 +181,7 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
}
case .Query_Info:
@@ -197,4 +197,4 @@ default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator
procedure = default_temp_allocator_proc,
data = allocator,
}
}
}
File diff suppressed because it is too large Load Diff
+7
View File
@@ -145,6 +145,13 @@ mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, a
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
}
mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
if size == 0 || allocator.procedure == nil {
return nil, nil
}
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, size, alignment, nil, 0, loc)
}
mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil || allocator.procedure == nil {
return nil
+6 -6
View File
@@ -58,9 +58,9 @@ _os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #no_b
return
}
heap_alloc :: proc "contextless" (size: int) -> rawptr {
heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
HEAP_ZERO_MEMORY :: 0x00000008
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, uint(size))
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
}
heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
if new_size == 0 {
@@ -91,7 +91,7 @@ heap_free :: proc "contextless" (ptr: rawptr) {
_windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
_windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, Allocator_Error) {
if size == 0 {
_windows_default_free(old_ptr)
return nil, nil
@@ -105,7 +105,7 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
original_old_ptr := intrinsics.ptr_offset((^rawptr)(old_ptr), -1)^
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
} else {
allocated_mem = heap_alloc(space+size_of(rawptr))
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
}
aligned_mem := rawptr(intrinsics.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
@@ -122,8 +122,8 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
return byte_slice(aligned_mem, size), nil
}
_windows_default_alloc :: proc "contextless" (size, alignment: int) -> ([]byte, Allocator_Error) {
return _windows_default_alloc_or_resize(size, alignment, nil)
_windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) {
return _windows_default_alloc_or_resize(size, alignment, nil, zero_memory)
}
+1 -1
View File
@@ -12,7 +12,7 @@ objc_SEL :: ^intrinsics.objc_selector
foreign Foundation {
objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
sel_registerName :: proc "c" (name: cstring) -> objc_SEL ---
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) ---
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class ---
objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
+22 -26
View File
@@ -6,8 +6,8 @@ import "core:runtime"
_ :: intrinsics
_ :: runtime
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) {
keys = make(type_of(keys), len(m), allocator)
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K, err: runtime.Allocator_Error) {
keys = make(type_of(keys), len(m), allocator) or_return
i := 0
for key in m {
keys[i] = key
@@ -15,8 +15,8 @@ map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K)
}
return
}
map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values: []V) {
values = make(type_of(values), len(m), allocator)
map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values: []V, err: runtime.Allocator_Error) {
values = make(type_of(values), len(m), allocator) or_return
i := 0
for _, value in m {
values[i] = value
@@ -37,8 +37,8 @@ Map_Entry_Info :: struct($Key, $Value: typeid) {
}
map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V)) {
entries = make(type_of(entries), len(m), 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 {
entries[i].key = key
@@ -52,28 +52,24 @@ map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (ent
m := m
rm := (^runtime.Raw_Map)(&m)
info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map)
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
key_offset := entry_type.offsets[2]
value_offset := entry_type.offsets[3]
entry_size := uintptr(ed.elem_size)
info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
if info.map_info != nil {
entries = make(type_of(entries), len(m), allocator) or_return
entries = make(type_of(entries), rm.entries.len)
map_cap := uintptr(cap(m))
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(rm^, info.map_info)
entry_index := 0
for bucket_index in 0..<map_cap {
if hash := hs[bucket_index]; runtime.map_hash_is_valid(hash) {
key := runtime.map_cell_index_dynamic(ks, &info.map_info.ks, bucket_index)
value := runtime.map_cell_index_dynamic(vs, &info.map_info.vs, bucket_index)
entries[entry_index].hash = hash
entries[entry_index].key = (^K)(key)^
entries[entry_index].value = (^V)(value)^
data := uintptr(rm.entries.data)
for i in 0..<rm.entries.len {
header := (^runtime.Map_Entry_Header)(data)
hash := header.hash
key := (^K)(data + key_offset)^
value := (^V)(data + value_offset)^
entries[i] = {hash, key, value}
data += entry_size
entry_index += 1
}
}
}
return
}
+3 -2
View File
@@ -177,7 +177,6 @@ _quick_sort_general :: proc(data: $T/[]$E, a, b, max_depth: int, call: $P, $KIND
}
// merge sort
_stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check {
less :: #force_inline proc(a, b: E, call: P) -> bool {
when KIND == .Ordered {
@@ -190,7 +189,9 @@ _stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (O
#panic("unhandled Sort_Kind")
}
}
// insertion sort
// TODO(bill): use a different algorithm as insertion sort is O(n^2)
n := len(data)
for i in 1..<n {
for j := i; j > 0 && less(data[j], data[j-1], call); j -= 1 {
+83
View File
@@ -9,6 +9,89 @@ Decimal :: struct {
neg, trunc: bool,
}
set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
d^ = {}
if len(s) == 0 {
return
}
i := 0
switch s[i] {
case '+': i += 1
case '-': i += 1; d.neg = true
}
// digits
saw_dot := false
saw_digits := false
for ; i < len(s); i += 1 {
switch {
case s[i] == '_':
// ignore underscores
continue
case s[i] == '.':
if saw_dot {
return
}
saw_dot = true
d.decimal_point = d.count
continue
case '0' <= s[i] && s[i] <= '9':
saw_digits = true
if s[i] == '0' && d.count == 0 {
d.decimal_point -= 1
continue
}
if d.count < len(d.digits) {
d.digits[d.count] = s[i]
d.count += 1
} else if s[i] != '0' {
d.trunc = true
}
continue
}
break
}
if !saw_digits {
return
}
if !saw_dot {
d.decimal_point = d.count
}
lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A') | ch }
if i < len(s) && lower(s[i]) == 'e' {
i += 1
if i >= len(s) {
return
}
exp_sign := 1
switch s[i] {
case '+': i += 1
case '-': i += 1; exp_sign = -1
}
if i >= len(s) || s[i] < '0' || s[i] > '9' {
return
}
e := 0
for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i += 1 {
if s[i] == '_' {
// ignore underscores
continue
}
if e < 1e4 {
e = e*10 + int(s[i]) - '0'
}
}
d.decimal_point += e * exp_sign
}
return i == len(s)
}
decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
digit_zero :: proc(buf: []byte) -> int {
for _, i in buf {
+86
View File
@@ -284,3 +284,89 @@ 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)
bits |= u64((exp-info.bias) & (1<<info.expbits - 1)) << info.mantbits
if d.neg {
bits |= 1<< info.mantbits << info.expbits
}
return bits
}
set_overflow :: proc "contextless" (mant: ^u64, exp: ^int, info: ^Float_Info) -> bool {
mant^ = 0
exp^ = 1<<info.expbits - 1 + info.bias
return true
}
mant: u64
exp: int
if d.decimal_point == 0 {
mant = 0
exp = info.bias
b = end(d, mant, exp, info)
return
}
if d.decimal_point > 310 {
set_overflow(&mant, &exp, info)
b = end(d, mant, exp, info)
return
} else if d.decimal_point < -330 {
mant = 0
exp = info.bias
b = end(d, mant, exp, info)
return
}
@static power_table := [?]int{1, 3, 6, 9, 13, 16, 19, 23, 26}
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)
exp += n
}
for d.decimal_point < 0 || d.decimal_point == 0 && d.digits[0] < '5' {
n := 27 if -d.decimal_point >= len(power_table) else power_table[-d.decimal_point]
decimal.shift(d, n)
exp -= n
}
// go from [0.5, 1) to [1, 2)
exp -= 1
if exp < info.bias + 1 {
n := info.bias + 1 - exp
decimal.shift(d, n)
exp += n
}
if (exp-info.bias) >= (1<<info.expbits - 1) {
set_overflow(&mant, &exp, info)
b = end(d, mant, exp, info)
return
}
decimal.shift(d, int(1 + info.mantbits))
mant = decimal.rounded_integer(d)
if mant == 2<<info.mantbits {
mant >>= 1
exp += 1
if (exp-info.bias) >= (1<<info.expbits - 1) {
set_overflow(&mant, &exp, info)
b = end(d, mant, exp, info)
return
}
}
if mant & (1<<info.mantbits) == 0 {
exp = info.bias
}
b = end(d, mant, exp, info)
return
}
-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:]
+265 -104
View File
@@ -1,6 +1,7 @@
package strconv
import "core:unicode/utf8"
import "decimal"
parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) {
switch s {
@@ -532,6 +533,8 @@ parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, o
parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
@(private)
lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A') | ch }
@@ -566,126 +569,284 @@ parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
// assert(n == 12.34 && ok);
// ```
parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
s := str
defer if n != nil { n^ = len(str) - len(s) }
if s == "" {
common_prefix_len_ignore_case :: proc "contextless" (s, prefix: string) -> int {
n := len(prefix)
if n > len(s) {
n = len(s)
}
for i in 0..<n {
c := s[i]
if 'A' <= c && c <= 'Z' {
c += 'a' - 'A'
}
if c != prefix[i] {
return i
}
}
return n
}
check_special :: proc "contextless" (s: string) -> (f: f64, n: int, ok: bool) {
s := s
if len(s) > 0 {
sign := 1
nsign := 0
switch s[0] {
case '+', '-':
if s[0] == '-' {
sign = -1
}
nsign = 1
s = s[1:]
fallthrough
case 'i', 'I':
n := common_prefix_len_ignore_case(s, "infinity")
if 3 < n && n < 8 { // "inf" or "infinity"
n = 3
}
if n == 3 || n == 8 {
f = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
n = nsign + 3
ok = true
return
}
case 'n', 'N':
if common_prefix_len_ignore_case(s, "nan") == 3 {
f = 0h7ff80000_00000001
n = nsign + 3
ok = true
return
}
}
}
return
}
parse_components :: proc "contextless" (s: string) -> (mantissa: u64, exp: int, neg, trunc, hex: bool, i: int, ok: bool) {
if len(s) == 0 {
return
}
switch s[i] {
case '+': i += 1
case '-': i += 1; neg = true
}
base := u64(10)
MAX_MANT_DIGITS := 19
exp_char := byte('e')
// support stupid 0x1.ABp100 hex floats even if Odin doesn't
if i+2 < len(s) && s[i] == '0' && lower(s[i+1]) == 'x' {
base = 16
MAX_MANT_DIGITS = 16
i += 2
exp_char = 'p'
hex = true
}
underscores := false
saw_dot, saw_digits := false, false
nd := 0
nd_mant := 0
decimal_point := 0
loop: for ; i < len(s); i += 1 {
switch c := s[i]; true {
case c == '_':
underscores = true
continue loop
case c == '.':
if saw_dot {
break loop
}
saw_dot = true
decimal_point = nd
continue loop
case '0' <= c && c <= '9':
saw_digits = true
if c == '0' && nd == 0 {
decimal_point -= 1
continue loop
}
nd += 1
if nd_mant < MAX_MANT_DIGITS {
mantissa *= base
mantissa += u64(c - '0')
nd_mant += 1
} else if c != '0' {
trunc = true
}
continue loop
case base == 16 && 'a' <= lower(c) && lower(c) <= 'f':
saw_digits = true
nd += 1
if nd_mant < MAX_MANT_DIGITS {
MAX_MANT_DIGITS *= 16
MAX_MANT_DIGITS += int(lower(c) - 'a' + 10)
nd_mant += 1
} else {
trunc = true
}
continue loop
}
break loop
}
if !saw_digits {
return
}
if !saw_dot {
decimal_point = nd
}
if base == 16 {
decimal_point *= 4
nd_mant *= 4
}
if i < len(s) && lower(s[i]) == exp_char {
i += 1
if i >= len(s) { return }
exp_sign := 1
switch s[i] {
case '+': i += 1
case '-': i += 1; exp_sign = -1
}
if i >= len(s) || s[i] < '0' || s[i] > '9' {
return
}
e := 0
for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i += 1 {
if s[i] == '_' {
underscores = true
continue
}
if e < 1e5 {
e = e*10 + int(s[i]) - '0'
}
}
decimal_point += e * exp_sign
} else if base == 16 {
return
}
if mantissa != 0 {
exp = decimal_point - nd_mant
}
// TODO(bill): check underscore correctness
ok = true
return
}
i := 0
parse_hex :: proc(s: string, mantissa: u64, exp: int, neg, trunc: bool) -> (f64, bool) {
info := &_f64_info
sign: f64 = 1
switch s[i] {
case '-': i += 1; sign = -1
case '+': i += 1
}
mantissa, exp := mantissa, exp
for ; i < len(s); i += 1 {
r := rune(s[i])
if r == '_' {
continue
MAX_EXP := 1<<info.expbits + info.bias - 2
MIN_EXP := info.bias + 1
exp += int(info.mantbits)
for mantissa != 0 && mantissa >> (info.mantbits+2) == 0 {
mantissa <<= 1
exp -= 1
}
if trunc {
mantissa |= 1
}
v := _digit_value(r)
if v >= 10 {
if r == '.' || r == 'e' || r == 'E' { // Skip parsing NaN and Inf if it's probably a regular float
break
}
if len(s) >= 3 + i {
buf: [4]u8
copy(buf[:], s[i:][:3])
v2 := transmute(u32)buf
v2 &= 0xDFDFDFDF // Knock out lower-case bits
buf = transmute([4]u8)v2
when ODIN_ENDIAN == .Little {
if v2 == 0x464e49 { // "INF"
s = s[3+i:]
value = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
return value, len(s) == 0
} else if v2 == 0x4e414e { // "NAN"
s = s[3+i:]
return 0h7ff80000_00000001, len(s) == 0
}
} else {
if v2 == 0x494e4600 { // "\0FNI"
s = s[3+i:]
value = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
return value, len(s) == 0
} else if v2 == 0x4e414e00 { // "\0NAN"
s = s[3+i:]
return 0h7ff80000_00000001, len(s) == 0
}
}
}
break
for mantissa >> (info.mantbits+2) == 0 {
mantissa = mantissa>>1 | mantissa&1
exp += 1
}
value *= 10
value += f64(v)
// denormalize
if mantissa > 1 && exp < MIN_EXP-2 {
mantissa = mantissa>>1 | mantissa&1
exp += 1
}
round := mantissa & 3
mantissa >>= 2
round |= mantissa & 1 // round to even
exp += 2
if round == 3 {
mantissa += 1
if mantissa == 1 << (1 + info.mantbits) {
mantissa >>= 1
exp += 1
}
}
if mantissa>>info.mantbits == 0 {
// zero or denormal
exp = info.bias
}
ok := true
if exp > MAX_EXP {
// infinity or invalid
mantissa = 1<<info.mantbits
exp = MAX_EXP + 1
ok = false
}
bits := mantissa & (1<<info.mantbits - 1)
bits |= u64((exp-info.bias) & (1<<info.expbits - 1)) << info.mantbits
if neg {
bits |= 1 << info.mantbits << info.expbits
}
return transmute(f64)bits, ok
}
if i < len(s) && s[i] == '.' {
pow10: f64 = 10
i += 1
for ; i < len(s); i += 1 {
r := rune(s[i])
if r == '_' {
continue
}
nr: int
defer if n != nil { n^ = nr }
v := _digit_value(r)
if v >= 10 {
break
if value, nr, ok = check_special(str); ok {
return
}
mantissa: u64
exp: int
neg, trunc, hex: bool
mantissa, exp, neg, trunc, hex, nr = parse_components(str) or_return
if hex {
return parse_hex(str, mantissa, exp, neg, trunc)
}
trunc_block: if !trunc {
@static pow10 := [?]f64{
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
1e20, 1e21, 1e22,
}
if mantissa>>_f64_info.mantbits != 0 {
return
}
f := f64(mantissa)
if neg {
f = -f
}
switch {
case exp == 0:
return f, true
case exp > 0 && exp <= 15+22:
if exp > 22 {
f *= pow10[exp-22]
exp = 22
}
value += f64(v)/pow10
pow10 *= 10
if f > 1e15 || f < 1e-15 {
break trunc_block
}
return f * pow10[exp], true
case -22 <= exp && exp < 0:
return f / pow10[-exp], true
}
}
frac := false
scale: f64 = 1
if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
i += 1
if i < len(s) {
switch s[i] {
case '-': i += 1; frac = true
case '+': i += 1
}
exp: u32 = 0
for ; i < len(s); i += 1 {
r := rune(s[i])
if r == '_' {
continue
}
d := u32(_digit_value(r))
if d >= 10 {
break
}
exp = exp * 10 + d
}
if exp > 308 { exp = 308 }
for exp >= 50 { scale *= 1e50; exp -= 50 }
for exp >= 8 { scale *= 1e8; exp -= 8 }
for exp > 0 { scale *= 10; exp -= 1 }
}
}
s = s[i:]
if frac {
value = sign * (value/scale)
} else {
value = sign * (value*scale)
}
ok = len(s) == 0
d: decimal.Decimal
decimal.set(&d, str[:nr])
b, overflow := decimal_to_float_bits(&d, &_f64_info)
value = transmute(f64)b
ok = !overflow
return
}
+44
View File
@@ -299,6 +299,50 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
return
}
// writes a f64 value into the builder, returns the written amount of characters
write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) {
buf: [384]byte
s := strconv.append_float(buf[:], f, fmt, prec, bit_size)
// If the result starts with a `+` then unless we always want signed results,
// we skip it unless it's followed by an `I` (because of +Inf).
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
s = s[1:]
}
return write_string(b, s)
}
// writes a f16 value into the builder, returns the written amount of characters
write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) {
buf: [384]byte
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
s = s[1:]
}
return write_string(b, s)
}
// writes a f32 value into the builder, returns the written amount of characters
write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
buf: [384]byte
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
s = s[1:]
}
return write_string(b, s)
}
// writes a f64 value into the builder, returns the written amount of characters
write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
buf: [384]byte
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
s = s[1:]
}
return write_string(b, s)
}
// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
buf: [32]byte
+87 -30
View File
@@ -11,7 +11,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 +20,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 +56,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 +76,7 @@ Example:
barrier := &sync.Barrier{}
main :: proc() {
main :: proc "contextless" () {
fmt.println("Start")
THREAD_COUNT :: 4
@@ -107,7 +107,7 @@ Barrier :: struct {
thread_count: int,
}
barrier_init :: proc(b: ^Barrier, thread_count: int) {
barrier_init :: proc "contextless" (b: ^Barrier, thread_count: int) {
b.index = 0
b.generation_id = 0
b.thread_count = thread_count
@@ -115,7 +115,7 @@ 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) {
guard(&b.mutex)
local_gen := b.generation_id
b.index += 1
@@ -141,7 +141,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 +155,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 +169,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 +191,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 +221,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 +233,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 +248,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 +265,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 +282,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 +300,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 +371,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 +390,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 +402,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
}
+19 -19
View File
@@ -7,15 +7,15 @@ _Sema :: struct {
atomic: Atomic_Sema,
}
_sema_post :: proc(s: ^Sema, count := 1) {
_sema_post :: proc "contextless" (s: ^Sema, count := 1) {
atomic_sema_post(&s.impl.atomic, count)
}
_sema_wait :: proc(s: ^Sema) {
_sema_wait :: proc "contextless" (s: ^Sema) {
atomic_sema_wait(&s.impl.atomic)
}
_sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
_sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
return atomic_sema_wait_with_timeout(&s.impl.atomic, duration)
}
@@ -25,7 +25,7 @@ _Recursive_Mutex :: struct {
recursion: i32,
}
_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
_recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
tid := Futex(current_thread_id())
for {
prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
@@ -40,7 +40,7 @@ _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
}
}
_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
_recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {
m.impl.recursion -= 1
if m.impl.recursion != 0 {
return
@@ -52,7 +52,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 +70,15 @@ when ODIN_OS != .Windows {
mutex: Atomic_Mutex,
}
_mutex_lock :: proc(m: ^Mutex) {
_mutex_lock :: proc "contextless" (m: ^Mutex) {
atomic_mutex_lock(&m.impl.mutex)
}
_mutex_unlock :: proc(m: ^Mutex) {
_mutex_unlock :: proc "contextless" (m: ^Mutex) {
atomic_mutex_unlock(&m.impl.mutex)
}
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
_mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
return atomic_mutex_try_lock(&m.impl.mutex)
}
@@ -86,19 +86,19 @@ when ODIN_OS != .Windows {
cond: Atomic_Cond,
}
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
_cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
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 {
return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
}
_cond_signal :: proc(c: ^Cond) {
_cond_signal :: proc "contextless" (c: ^Cond) {
atomic_cond_signal(&c.impl.cond)
}
_cond_broadcast :: proc(c: ^Cond) {
_cond_broadcast :: proc "contextless" (c: ^Cond) {
atomic_cond_broadcast(&c.impl.cond)
}
@@ -107,27 +107,27 @@ when ODIN_OS != .Windows {
mutex: Atomic_RW_Mutex,
}
_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
_rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
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)
}
_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) {
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)
}
_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)
}
+1 -1
View File
@@ -13,7 +13,7 @@
CPU feature flags can be tested against `cpu_features`, where applicable, e.g.
`if .aes in si.aes { ... }`
*/
// +ignore
//+build ignore
package sysinfo
import "core:fmt"
+1
View File
@@ -464,6 +464,7 @@ macos_release_map: map[string]Darwin_To_Release = {
"21F2092" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
"21G72" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
"21G83" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
"21G115" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}},
}
@(private)
+30
View File
@@ -128,4 +128,34 @@ foreign advapi32 {
lpData: LPCVOID,
cbData: DWORD,
) -> LSTATUS ---
GetFileSecurityW :: proc(
lpFileName: LPCWSTR,
RequestedInformation: SECURITY_INFORMATION,
pSecurityDescriptor: PSECURITY_DESCRIPTOR,
nLength: DWORD,
lpnLengthNeeded: LPDWORD,
) -> BOOL ---
DuplicateToken :: proc(
ExistingTokenHandle: HANDLE,
ImpersonationLevel: SECURITY_IMPERSONATION_LEVEL,
DuplicateTokenHandle: PHANDLE,
) -> BOOL ---
MapGenericMask :: proc(
AccessMask: PDWORD,
GenericMapping: PGENERIC_MAPPING,
) ---
AccessCheck :: proc(
pSecurityDescriptor: PSECURITY_DESCRIPTOR,
ClientToken: HANDLE,
DesiredAccess: DWORD,
GenericMapping: PGENERIC_MAPPING,
PrivilegeSet: PPRIVILEGE_SET,
PrivilegeSetLength: LPDWORD,
GrantedAccess: LPDWORD,
AccessStatus: LPBOOL,
) -> BOOL ---
}
+9
View File
@@ -0,0 +1,9 @@
// +build windows
package sys_windows
foreign import "system:Comctl32.lib"
@(default_calling_convention="stdcall")
foreign Comctl32 {
LoadIconWithScaleDown :: proc(hinst: HINSTANCE, pszName: PCWSTR, cx: c_int, cy: c_int, phico: ^HICON) -> HRESULT ---
}
+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 ---
}
+4
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 ---
@@ -77,6 +78,9 @@ foreign gdi32 {
) -> HFONT ---
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 {
+84 -1
View File
@@ -120,6 +120,12 @@ foreign kernel32 {
bManualReset: BOOL,
lpTimerName: LPCWSTR,
) -> HANDLE ---
CreateWaitableTimerExW :: proc(
lpTimerAttributes: LPSECURITY_ATTRIBUTES,
lpTimerName: LPCWSTR,
dwFlags: DWORD,
dwDesiredAccess: DWORD,
) -> HANDLE ---
SetWaitableTimerEx :: proc(
hTimer: HANDLE,
lpDueTime: ^LARGE_INTEGER,
@@ -248,6 +254,17 @@ foreign kernel32 {
GetModuleHandleW :: proc(lpModuleName: LPCWSTR) -> HMODULE ---
GetModuleHandleA :: proc(lpModuleName: LPCSTR) -> HMODULE ---
GetSystemTimeAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
GetSystemTimePreciseAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
FileTimeToSystemTime :: proc(lpFileTime: ^FILETIME, lpSystemTime: ^SYSTEMTIME) -> BOOL ---
SystemTimeToTzSpecificLocalTime :: proc(
lpTimeZoneInformation: ^TIME_ZONE_INFORMATION,
lpUniversalTime: ^SYSTEMTIME,
lpLocalTime: ^SYSTEMTIME,
) -> BOOL ---
SystemTimeToFileTime :: proc(
lpSystemTime: ^SYSTEMTIME,
lpFileTime: LPFILETIME,
) -> BOOL ---
CreateEventW :: proc(
lpEventAttributes: LPSECURITY_ATTRIBUTES,
bManualReset: BOOL,
@@ -346,6 +363,15 @@ foreign kernel32 {
GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL ---
FreeConsole :: proc() -> BOOL ---
GetConsoleWindow :: proc() -> HWND ---
GetDiskFreeSpaceExW :: proc(
lpDirectoryName: LPCWSTR,
lpFreeBytesAvailableToCaller: PULARGE_INTEGER,
lpTotalNumberOfBytes: PULARGE_INTEGER,
lpTotalNumberOfFreeBytes: PULARGE_INTEGER,
) -> BOOL ---
GetLogicalProcessorInformation :: proc(buffer: ^SYSTEM_LOGICAL_PROCESSOR_INFORMATION, returnedLength: PDWORD) -> BOOL ---
}
@@ -963,4 +989,61 @@ DCB :: struct {
foreign kernel32 {
GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
}
}
LPFIBER_START_ROUTINE :: #type proc "stdcall" (lpFiberParameter: LPVOID)
@(default_calling_convention = "stdcall")
foreign kernel32 {
CreateFiber :: proc(dwStackSize: SIZE_T, lpStartAddress: LPFIBER_START_ROUTINE, lpParameter: LPVOID) -> LPVOID ---
DeleteFiber :: proc(lpFiber: LPVOID) ---
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,
}
+41
View File
@@ -14,4 +14,45 @@ foreign shell32 {
lpDirectory: LPCWSTR,
nShowCmd: INT,
) -> HINSTANCE ---
ShellExecuteExW :: proc(pExecInfo: ^SHELLEXECUTEINFOW) -> BOOL ---
SHCreateDirectoryExW :: proc(
hwnd: HWND,
pszPath: LPCWSTR,
psa: ^SECURITY_ATTRIBUTES,
) -> 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
+1
View File
@@ -8,4 +8,5 @@ foreign shlwapi {
PathFileExistsW :: proc(pszPath: wstring) -> BOOL ---
PathFindExtensionW :: proc(pszPath: wstring) -> wstring ---
PathFindFileNameW :: proc(pszPath: wstring) -> wstring ---
SHAutoComplete :: proc(hwndEdit: HWND, dwFlags: DWORD) -> LWSTDAPI ---
}
+393 -4
View File
@@ -20,6 +20,7 @@ DWORD :: c_ulong
DWORDLONG :: c.ulonglong
QWORD :: c.ulonglong
HANDLE :: distinct LPVOID
PHANDLE :: ^HANDLE
HINSTANCE :: HANDLE
HMODULE :: distinct HINSTANCE
HRESULT :: distinct LONG
@@ -37,12 +38,14 @@ HHOOK :: distinct HANDLE
HKEY :: distinct HANDLE
HDESK :: distinct HANDLE
HFONT :: distinct HANDLE
HRGN :: distinct HANDLE
BOOL :: distinct b32
BYTE :: distinct u8
BOOLEAN :: distinct b8
GROUP :: distinct c_uint
LARGE_INTEGER :: distinct c_longlong
ULARGE_INTEGER :: distinct c_ulonglong
PULARGE_INTEGER :: ^ULARGE_INTEGER
LONG :: c_long
UINT :: c_uint
INT :: c_int
@@ -133,12 +136,24 @@ LPWSAOVERLAPPED :: distinct rawptr
LPWSAOVERLAPPED_COMPLETION_ROUTINE :: distinct rawptr
LPCVOID :: rawptr
PACCESS_TOKEN :: PVOID
PSECURITY_DESCRIPTOR :: PVOID
PSID :: PVOID
PCLAIMS_BLOB :: PVOID
PCONDITION_VARIABLE :: ^CONDITION_VARIABLE
PLARGE_INTEGER :: ^LARGE_INTEGER
PSRWLOCK :: ^SRWLOCK
MMRESULT :: UINT
CREATE_WAITABLE_TIMER_MANUAL_RESET :: 0x00000001
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION :: 0x00000002
TIMER_QUERY_STATE :: 0x0001
TIMER_MODIFY_STATE :: 0x0002
TIMER_ALL_ACCESS :: STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | TIMER_QUERY_STATE | TIMER_MODIFY_STATE
SOCKET :: distinct uintptr // TODO
socklen_t :: c_int
ADDRESS_FAMILY :: USHORT
@@ -175,6 +190,7 @@ FILE_SHARE_DELETE: DWORD : 0x00000004
FILE_GENERIC_ALL: DWORD : 0x10000000
FILE_GENERIC_EXECUTE: DWORD : 0x20000000
FILE_GENERIC_READ: DWORD : 0x80000000
FILE_ALL_ACCESS :: STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF
FILE_ACTION_ADDED :: 0x00000001
FILE_ACTION_REMOVED :: 0x00000002
@@ -230,6 +246,20 @@ SECURITY_SQOS_PRESENT: DWORD : 0x00100000
FIONBIO: c_ulong : 0x8004667e
OWNER_SECURITY_INFORMATION :: 0x00000001
GROUP_SECURITY_INFORMATION :: 0x00000002
DACL_SECURITY_INFORMATION :: 0x00000004
SACL_SECURITY_INFORMATION :: 0x00000008
LABEL_SECURITY_INFORMATION :: 0x00000010
ATTRIBUTE_SECURITY_INFORMATION :: 0x00000020
SCOPE_SECURITY_INFORMATION :: 0x00000040
PROCESS_TRUST_LABEL_SECURITY_INFORMATION :: 0x00000080
ACCESS_FILTER_SECURITY_INFORMATION :: 0x00000100
BACKUP_SECURITY_INFORMATION :: 0x00010000
PROTECTED_DACL_SECURITY_INFORMATION :: 0x80000000
PROTECTED_SACL_SECURITY_INFORMATION :: 0x40000000
UNPROTECTED_DACL_SECURITY_INFORMATION :: 0x20000000
UNPROTECTED_SACL_SECURITY_INFORMATION :: 0x10000000
GET_FILEEX_INFO_LEVELS :: distinct i32
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
@@ -773,6 +803,61 @@ MSG :: struct {
LPMSG :: ^MSG
// WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes
HTERROR :: -2
HTTRANSPARENT :: -1
HTNOWHERE :: 0
HTCLIENT :: 1
HTCAPTION :: 2
HTSYSMENU :: 3
HTGROWBOX :: 4
HTSIZE :: HTGROWBOX
HTMENU :: 5
HTHSCROLL :: 6
HTVSCROLL :: 7
HTMINBUTTON :: 8
HTMAXBUTTON :: 9
HTLEFT :: 10
HTRIGHT :: 11
HTTOP :: 12
HTTOPLEFT :: 13
HTTOPRIGHT :: 14
HTBOTTOM :: 15
HTBOTTOMLEFT :: 16
HTBOTTOMRIGHT :: 17
HTBORDER :: 18
HTREDUCE :: HTMINBUTTON
HTZOOM :: HTMAXBUTTON
HTSIZEFIRST :: HTLEFT
HTSIZELAST :: HTBOTTOMRIGHT
HTOBJECT :: 19
HTCLOSE :: 20
HTHELP :: 21
TEXTMETRICW :: struct {
tmHeight: LONG,
tmAscent: LONG,
tmDescent: LONG,
tmInternalLeading: LONG,
tmExternalLeading: LONG,
tmAveCharWidth: LONG,
tmMaxCharWidth: LONG,
tmWeight: LONG,
tmOverhang: LONG,
tmDigitizedAspectX: LONG,
tmDigitizedAspectY: LONG,
tmFirstChar: WCHAR,
tmLastChar: WCHAR,
tmDefaultChar: WCHAR,
tmBreakChar: WCHAR,
tmItalic: BYTE,
tmUnderlined: BYTE,
tmStruckOut: BYTE,
tmPitchAndFamily: BYTE,
tmCharSet: BYTE,
}
LPTEXTMETRICW :: ^TEXTMETRICW
PAINTSTRUCT :: struct {
hdc: HDC,
fErase: BOOL,
@@ -879,6 +964,48 @@ NM_FONTCHANGED :: NM_OUTOFMEMORY-22
NM_CUSTOMTEXT :: NM_OUTOFMEMORY-23 // uses NMCUSTOMTEXT struct
NM_TVSTATEIMAGECHANGING :: NM_OUTOFMEMORY-23 // uses NMTVSTATEIMAGECHANGING struct, defined after HTREEITEM
PCZZWSTR :: ^WCHAR
SHFILEOPSTRUCTW :: struct {
hwnd: HWND,
wFunc: UINT,
pFrom: PCZZWSTR,
pTo: PCZZWSTR,
fFlags: FILEOP_FLAGS,
fAnyOperationsAborted: BOOL,
hNameMappings: LPVOID,
lpszProgressTitle: PCWSTR, // only used if FOF_SIMPLEPROGRESS
}
LPSHFILEOPSTRUCTW :: ^SHFILEOPSTRUCTW
// Shell File Operations
FO_MOVE :: 0x0001
FO_COPY :: 0x0002
FO_DELETE :: 0x0003
FO_RENAME :: 0x0004
// SHFILEOPSTRUCT.fFlags and IFileOperation::SetOperationFlags() flag values
FOF_MULTIDESTFILES :: 0x0001
FOF_CONFIRMMOUSE :: 0x0002
FOF_SILENT :: 0x0004 // don't display progress UI (confirm prompts may be displayed still)
FOF_RENAMEONCOLLISION :: 0x0008 // automatically rename the source files to avoid the collisions
FOF_NOCONFIRMATION :: 0x0010 // don't display confirmation UI, assume "yes" for cases that can be bypassed, "no" for those that can not
FOF_WANTMAPPINGHANDLE :: 0x0020 // Fill in SHFILEOPSTRUCT.hNameMappings
// Must be freed using SHFreeNameMappings
FOF_ALLOWUNDO :: 0x0040 // enable undo including Recycle behavior for IFileOperation::Delete()
FOF_FILESONLY :: 0x0080 // only operate on the files (non folders), both files and folders are assumed without this
FOF_SIMPLEPROGRESS :: 0x0100 // means don't show names of files
FOF_NOCONFIRMMKDIR :: 0x0200 // don't dispplay confirmatino UI before making any needed directories, assume "Yes" in these cases
FOF_NOERRORUI :: 0x0400 // don't put up error UI, other UI may be displayed, progress, confirmations
FOF_NOCOPYSECURITYATTRIBS :: 0x0800 // dont copy file security attributes (ACLs)
FOF_NORECURSION :: 0x1000 // don't recurse into directories for operations that would recurse
FOF_NO_CONNECTED_ELEMENTS :: 0x2000 // don't operate on connected elements ("xxx_files" folders that go with .htm files)
FOF_WANTNUKEWARNING :: 0x4000 // during delete operation, warn if object is being permanently destroyed instead of recycling (partially overrides FOF_NOCONFIRMATION)
FOF_NORECURSEREPARSE :: 0x8000 // deprecated; the operations engine always does the right thing on FolderLink objects (symlinks, reparse points, folder shortcuts)
FOF_NO_UI :: (FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR) // don't display any UI at all
FILEOP_FLAGS :: WORD
DEVMODEW :: struct {
dmDeviceName: [32]wchar_t,
dmSpecVersion: WORD,
@@ -1066,8 +1193,14 @@ WS_EX_TOPMOST : UINT : 0x0000_0008
WS_EX_TRANSPARENT : UINT : 0x0000_0020
WS_EX_WINDOWEDGE : UINT : 0x0000_0100
PBS_SMOOTH :: 0x01
PBS_VERTICAL :: 0x04
PBS_SMOOTH :: 0x01
PBS_VERTICAL :: 0x04
PBS_MARQUEE :: 0x08
PBS_SMOOTHREVERSE :: 0x10
PBST_NORMAL :: 0x0001
PBST_ERROR :: 0x0002
PBST_PAUSED :: 0x0003
QS_ALLEVENTS : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY
QS_ALLINPUT : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE
@@ -1127,6 +1260,10 @@ SWP_NOREPOSITION :: SWP_NOOWNERZORDER
SWP_DEFERERASE :: 0x2000 // same as SWP_DEFERDRAWING
SWP_ASYNCWINDOWPOS :: 0x4000 // same as SWP_CREATESPB
CSIDL_APPDATA :: 0x001a // <user name>\Application Data
CSIDL_COMMON_APPDATA :: 0x0023 // All Users\Application Data
CSIDL_PROFILE :: 0x0028 // <user name>\
HWND_TOP :: HWND( uintptr(0)) // 0
HWND_BOTTOM :: HWND( uintptr(1)) // 1
HWND_TOPMOST :: HWND(~uintptr(0)) // -1
@@ -1325,6 +1462,58 @@ WMSZ_BOTTOM :: 6
WMSZ_BOTTOMLEFT :: 7
WMSZ_BOTTOMRIGHT :: 8
// Note CLASSKEY overrides CLASSNAME
SEE_MASK_DEFAULT :: 0x00000000
SEE_MASK_CLASSNAME :: 0x00000001 // SHELLEXECUTEINFO.lpClass is valid
SEE_MASK_CLASSKEY :: 0x00000003 // SHELLEXECUTEINFO.hkeyClass is valid
// Note SEE_MASK_INVOKEIDLIST(0xC) implies SEE_MASK_IDLIST(0x04)
SEE_MASK_IDLIST :: 0x00000004 // SHELLEXECUTEINFO.lpIDList is valid
SEE_MASK_INVOKEIDLIST :: 0x0000000c // enable IContextMenu based verbs
SEE_MASK_ICON :: 0x00000010 // not used
SEE_MASK_HOTKEY :: 0x00000020 // SHELLEXECUTEINFO.dwHotKey is valid
SEE_MASK_NOCLOSEPROCESS :: 0x00000040 // SHELLEXECUTEINFO.hProcess
SEE_MASK_CONNECTNETDRV :: 0x00000080 // enables re-connecting disconnected network drives
SEE_MASK_NOASYNC :: 0x00000100 // block on the call until the invoke has completed, use for callers that exit after calling ShellExecuteEx()
SEE_MASK_FLAG_DDEWAIT :: SEE_MASK_NOASYNC // Use SEE_MASK_NOASYNC instead of SEE_MASK_FLAG_DDEWAIT as it more accuratly describes the behavior
SEE_MASK_DOENVSUBST :: 0x00000200 // indicates that SHELLEXECUTEINFO.lpFile contains env vars that should be expanded
SEE_MASK_FLAG_NO_UI :: 0x00000400 // disable UI including error messages
SEE_MASK_UNICODE :: 0x00004000
SEE_MASK_NO_CONSOLE :: 0x00008000
SEE_MASK_ASYNCOK :: 0x00100000
SEE_MASK_HMONITOR :: 0x00200000 // SHELLEXECUTEINFO.hMonitor
SEE_MASK_NOZONECHECKS :: 0x00800000
SEE_MASK_NOQUERYCLASSSTORE :: 0x01000000
SEE_MASK_WAITFORINPUTIDLE :: 0x02000000
SEE_MASK_FLAG_LOG_USAGE :: 0x04000000
// When SEE_MASK_FLAG_HINST_IS_SITE is specified SHELLEXECUTEINFO.hInstApp is used as an
// _In_ parameter and specifies a IUnknown* to be used as a site pointer. The site pointer
// is used to provide services to shell execute, the handler binding process and the verb handlers
// once they are invoked.
SEE_MASK_FLAG_HINST_IS_SITE :: 0x08000000
SHELLEXECUTEINFOW :: struct {
cbSize: DWORD, // in, required, sizeof of this structure
fMask: ULONG, // in, SEE_MASK_XXX values
hwnd: HWND, // in, optional
lpVerb: LPCWSTR, // in, optional when unspecified the default verb is choosen
lpFile: LPCWSTR, // in, either this value or lpIDList must be specified
lpParameters: LPCWSTR, // in, optional
lpDirectory: LPCWSTR, // in, optional
nShow: c.int, // in, required
hInstApp: HINSTANCE, // out when SEE_MASK_NOCLOSEPROCESS is specified
lpIDList: rawptr, // in, valid when SEE_MASK_IDLIST is specified, PCIDLIST_ABSOLUTE, for use with SEE_MASK_IDLIST & SEE_MASK_INVOKEIDLIST
lpClass: LPCWSTR, // in, valid when SEE_MASK_CLASSNAME is specified
hkeyClass: HKEY, // in, valid when SEE_MASK_CLASSKEY is specified
dwHotKey: DWORD, // in, valid when SEE_MASK_HOTKEY is specified
DUMMYUNIONNAME: struct #raw_union {
hIcon: HANDLE, // not used
hMonitor: HANDLE, // in, valid when SEE_MASK_HMONITOR specified
},
hProcess: HANDLE, // out, valid when SEE_MASK_NOCLOSEPROCESS specified
}
LPSHELLEXECUTEINFOW :: ^SHELLEXECUTEINFOW
// Key State Masks for Mouse Messages
MK_LBUTTON :: 0x0001
MK_RBUTTON :: 0x0002
@@ -1366,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
@@ -1462,6 +1670,24 @@ IDI_WARNING := IDI_EXCLAMATION
IDI_ERROR := IDI_HAND
IDI_INFORMATION := IDI_ASTERISK
IMAGE_BITMAP :: 0
IMAGE_ICON :: 1
IMAGE_CURSOR :: 2
IMAGE_ENHMETAFILE :: 3
LR_DEFAULTCOLOR :: 0x00000000
LR_MONOCHROME :: 0x00000001
LR_COLOR :: 0x00000002
LR_COPYRETURNORG :: 0x00000004
LR_COPYDELETEORG :: 0x00000008
LR_LOADFROMFILE :: 0x00000010
LR_LOADTRANSPARENT :: 0x00000020
LR_DEFAULTSIZE :: 0x00000040
LR_VGACOLOR :: 0x00000080
LR_LOADMAP3DCOLORS :: 0x00001000
LR_CREATEDIBSECTION :: 0x00002000
LR_COPYFROMRESOURCE :: 0x00004000
LR_SHARED :: 0x00008000
// DIB color table identifiers
DIB_RGB_COLORS :: 0
@@ -1774,12 +2000,15 @@ WAIT_FAILED: DWORD : 0xFFFFFFFF
PIPE_ACCESS_INBOUND: DWORD : 0x00000001
PIPE_ACCESS_OUTBOUND: DWORD : 0x00000002
PIPE_ACCESS_DUPLEX: DWORD : 0x00000003
FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000
FILE_FLAG_OVERLAPPED: DWORD : 0x40000000
PIPE_WAIT: DWORD : 0x00000000
PIPE_TYPE_BYTE: DWORD : 0x00000000
PIPE_TYPE_MESSAGE: DWORD : 0x00000004
PIPE_REJECT_REMOTE_CLIENTS: DWORD : 0x00000008
PIPE_READMODE_BYTE: DWORD : 0x00000000
PIPE_READMODE_MESSAGE: DWORD : 0x00000002
PIPE_ACCEPT_REMOTE_CLIENTS: DWORD : 0x00000000
FD_SETSIZE :: 64
@@ -1793,7 +2022,58 @@ HEAP_ZERO_MEMORY: DWORD : 0x00000008
HANDLE_FLAG_INHERIT: DWORD : 0x00000001
HANDLE_FLAG_PROTECT_FROM_CLOSE :: 0x00000002
TOKEN_READ: DWORD : 0x20008
GENERIC_MAPPING :: struct {
GenericRead: ACCESS_MASK,
GenericWrite: ACCESS_MASK,
GenericExecute: ACCESS_MASK,
GenericAll: ACCESS_MASK,
}
PGENERIC_MAPPING :: ^GENERIC_MAPPING
SECURITY_IMPERSONATION_LEVEL :: enum {
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation,
}
SECURITY_INFORMATION :: DWORD
ANYSIZE_ARRAY :: 1
LUID_AND_ATTRIBUTES :: struct {
Luid: LUID,
Attributes: DWORD,
}
PRIVILEGE_SET :: struct {
PrivilegeCount: DWORD,
Control: DWORD,
Privilege: [ANYSIZE_ARRAY]LUID_AND_ATTRIBUTES,
}
PPRIVILEGE_SET :: ^PRIVILEGE_SET
// Token Specific Access Rights.
TOKEN_ASSIGN_PRIMARY :: 0x0001
TOKEN_DUPLICATE :: 0x0002
TOKEN_IMPERSONATE :: 0x0004
TOKEN_QUERY :: 0x0008
TOKEN_QUERY_SOURCE :: 0x0010
TOKEN_ADJUST_PRIVILEGES :: 0x0020
TOKEN_ADJUST_GROUPS :: 0x0040
TOKEN_ADJUST_DEFAULT :: 0x0080
TOKEN_ADJUST_SESSIONID :: 0x0100
TOKEN_ALL_ACCESS_P :: STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY |\
TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT
TOKEN_ALL_ACCESS :: TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID
TOKEN_READ :: STANDARD_RIGHTS_READ | TOKEN_QUERY
TOKEN_WRITE :: STANDARD_RIGHTS_WRITE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT
TOKEN_EXECUTE :: STANDARD_RIGHTS_EXECUTE
TOKEN_TRUST_CONSTRAINT_MASK :: STANDARD_RIGHTS_READ | TOKEN_QUERY | TOKEN_QUERY_SOURCE
TOKEN_ACCESS_PSEUDO_HANDLE_WIN8 :: TOKEN_QUERY | TOKEN_QUERY_SOURCE
TOKEN_ACCESS_PSEUDO_HANDLE :: TOKEN_ACCESS_PSEUDO_HANDLE_WIN8
CP_ACP :: 0 // default to ANSI code page
CP_OEMCP :: 1 // default to OEM code page
@@ -2101,9 +2381,10 @@ FILETIME :: struct {
FILETIME_as_unix_nanoseconds :: proc "contextless" (ft: FILETIME) -> i64 {
t := i64(u64(ft.dwLowDateTime) | u64(ft.dwHighDateTime) << 32)
return (t - 0x019db1ded53e8000) * 100
return (t - 116444736000000000) * 100
}
OVERLAPPED :: struct {
Internal: ^c_ulong,
InternalHigh: ^c_ulong,
@@ -2777,6 +3058,16 @@ SYSTEMTIME :: struct {
milliseconds: WORD,
}
TIME_ZONE_INFORMATION :: struct {
Bias: LONG,
StandardName: [32]WCHAR,
StandardDate: SYSTEMTIME,
StandardBias: LONG,
DaylightName: [32]WCHAR,
DaylightDate: SYSTEMTIME,
DaylightBias: LONG,
}
@(private="file")
IMAGE_DOS_HEADER :: struct {
@@ -3048,12 +3339,32 @@ SHCONTF_FLATLIST :: 0x4000
SHCONTF_ENABLE_ASYNC :: 0x8000
SHCONTF_INCLUDESUPERHIDDEN :: 0x10000
SHACF_DEFAULT :: 0x00000000 // Currently (SHACF_FILESYSTEM | SHACF_URLALL)
SHACF_FILESYSTEM :: 0x00000001 // This includes the File System as well as the rest of the shell (Desktop\My Computer\Control Panel\)
SHACF_URLALL :: (SHACF_URLHISTORY | SHACF_URLMRU)
SHACF_URLHISTORY :: 0x00000002 // URLs in the User's History
SHACF_URLMRU :: 0x00000004 // URLs in the User's Recently Used list.
SHACF_USETAB :: 0x00000008 // Use the tab to move thru the autocomplete possibilities instead of to the next dialog/window control.
SHACF_FILESYS_ONLY :: 0x00000010 // This includes the File System
SHACF_FILESYS_DIRS :: 0x00000020 // Same as SHACF_FILESYS_ONLY except it only includes directories, UNC servers, and UNC server shares.
SHACF_VIRTUAL_NAMESPACE :: 0x00000040 // Also include the virtual namespace
SHACF_AUTOSUGGEST_FORCE_ON :: 0x10000000 // Ignore the registry default and force the feature on.
SHACF_AUTOSUGGEST_FORCE_OFF :: 0x20000000 // Ignore the registry default and force the feature off.
SHACF_AUTOAPPEND_FORCE_ON :: 0x40000000 // Ignore the registry default and force the feature on. (Also know as AutoComplete)
SHACF_AUTOAPPEND_FORCE_OFF :: 0x80000000 // Ignore the registry default and force the feature off. (Also know as AutoComplete)
LWSTDAPI :: HRESULT
CLSID_FileOpenDialog := &GUID{0xDC1C5A9C, 0xE88A, 0x4DDE, {0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7}}
CLSID_FileSaveDialog := &GUID{0xC0B4E2F3, 0xBA21, 0x4773, {0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B}}
CLSID_TaskbarList := &GUID{0x56FDF344, 0xFD6D, 0x11d0, {0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}}
IID_IFileDialog := &GUID{0x42F85136, 0xDB7E, 0x439C, {0x85, 0xF1, 0xE4, 0x07, 0x5D, 0x13, 0x5F, 0xC8}}
IID_IFileSaveDialog := &GUID{0x84BCCD23, 0x5FDE, 0x4CDB, {0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB}}
IID_IFileOpenDialog := &GUID{0xD57C7288, 0xD4AD, 0x4768, {0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60}}
IID_ITaskbarList := &GUID{0x56FDF342, 0xFD6D, 0x11d0, {0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}}
IID_ITaskbarList2 := &GUID{0x602D4995, 0xB13A, 0x429b, {0xA6, 0x6E, 0x19, 0x35, 0xE4, 0x4F, 0x43, 0x17}}
IID_ITaskbarList3 := &GUID{0xea1afb91, 0x9e28, 0x4b86, {0x90, 0xe9, 0x9e, 0x9f, 0x8a, 0x5e, 0xef, 0xaf}}
IModalWindow :: struct #raw_union {
#subtype IUnknown: IUnknown,
@@ -3358,6 +3669,84 @@ IFileSaveDialogVtbl :: struct {
ApplyProperties: proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem, pStore: ^IPropertyStore, hwnd: HWND, pSink: ^IFileOperationProgressSink) -> HRESULT,
}
ITaskbarList :: struct #raw_union {
#subtype IUnknown: IUnknown,
using Vtbl: ^ITaskbarListVtbl,
}
ITaskbarListVtbl :: struct {
using IUnknownVtbl: IUnknownVtbl,
HrInit: proc "stdcall" (this: ^ITaskbarList) -> HRESULT,
AddTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
DeleteTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
ActivateTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
SetActiveAlt: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
}
ITaskbarList2 :: struct #raw_union {
#subtype ITaskbarList: ITaskbarList,
using Vtbl: ^ITaskbarList2Vtbl,
}
ITaskbarList2Vtbl :: struct {
using ITaskbarListVtbl: ITaskbarListVtbl,
MarkFullscreenWindow: proc "stdcall" (this: ^ITaskbarList2, hwnd: HWND, fFullscreen: BOOL) -> HRESULT,
}
TBPFLAG :: enum c_int {
NOPROGRESS = 0,
INDETERMINATE = 0x1,
NORMAL = 0x2,
ERROR = 0x4,
PAUSED = 0x8,
}
THUMBBUTTONFLAGS :: enum c_int {
ENABLED = 0,
DISABLED = 0x1,
DISMISSONCLICK = 0x2,
NOBACKGROUND = 0x4,
HIDDEN = 0x8,
NONINTERACTIVE = 0x10,
}
THUMBBUTTONMASK :: enum c_int {
BITMAP = 0x1,
ICON = 0x2,
TOOLTIP = 0x4,
FLAGS = 0x8,
}
THUMBBUTTON :: struct {
dwMask: THUMBBUTTONMASK,
iId: UINT,
iBitmap: UINT,
hIcon: HICON,
szTip: [260]WCHAR,
dwFlags: THUMBBUTTONFLAGS,
}
LPTHUMBBUTTON :: ^THUMBBUTTON
HIMAGELIST :: ^IUnknown
ITaskbarList3 :: struct #raw_union {
#subtype ITaskbarList2: ITaskbarList2,
using Vtbl: ^ITaskbarList3Vtbl,
}
ITaskbarList3Vtbl :: struct {
using ITaskbarList2Vtbl: ITaskbarList2Vtbl,
SetProgressValue: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, ullCompleted: ULONGLONG, ullTotal: ULONGLONG) -> HRESULT,
SetProgressState: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, tbpFlags: TBPFLAG) -> HRESULT,
RegisterTab: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndMDI: HWND) -> HRESULT,
UnregisterTab: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND) -> HRESULT,
SetTabOrder: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndInsertBefore: HWND) -> HRESULT,
SetTabActive: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndMDI: HWND, dwReserved: DWORD) -> HRESULT,
ThumbBarAddButtons: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, cButtons: UINT, pButton: LPTHUMBBUTTON) -> HRESULT,
ThumbBarUpdateButtons: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, cButtons: UINT, pButton: LPTHUMBBUTTON) -> HRESULT,
ThumbBarSetImageList: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, himl: HIMAGELIST) -> HRESULT,
SetOverlayIcon: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, hIcon: HICON, pszDescription: LPCWSTR) -> HRESULT,
SetThumbnailTooltip: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, pszTip: LPCWSTR) -> HRESULT,
SetThumbnailClip: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, prcClip: ^RECT) -> HRESULT,
}
MEMORYSTATUSEX :: struct {
dwLength: DWORD,
dwMemoryLoad: DWORD,
+39
View File
@@ -78,6 +78,7 @@ foreign user32 {
LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR ---
LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
LoadImageW :: proc(hInst: HINSTANCE, name: LPCWSTR, type: UINT, cx: c_int, cy: c_int, fuLoad: UINT) -> HANDLE ---
GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
@@ -99,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 ---
@@ -121,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 ---
@@ -201,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" (
@@ -431,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
+4 -4
View File
@@ -62,19 +62,19 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstri
wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
context.allocator = allocator
if N <= 0 {
if N == 0 {
return
}
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N) if N > 0 else -1, nil, 0, nil, nil)
if n == 0 {
return
}
// If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated
// If N < 0 the call to WideCharToMultiByte assume the wide string is null terminated
// and will scan it to find the first null terminated character. The resulting string will
// also be null terminated.
// If N != -1 it assumes the wide string is not null terminated and the resulting string
// If N > 0 it assumes the wide string is not null terminated and the resulting string
// will not be null terminated.
text := make([]byte, n) or_return
+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 ---
}
+12
View File
@@ -85,3 +85,15 @@ foreign Opengl32 {
wglUseFontBitmaps :: proc(hdc: HDC, first, count, list_base: DWORD) -> BOOL ---
wglUseFontOutlines :: proc(hdc: HDC, first, count, list_base: DWORD, deviation, extrusion: f32, format: c.int, gmf: LPGLYPHMETRICSFLOAT) -> BOOL ---
}
// Used by vendor:OpenGL
// https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions#Windows
gl_set_proc_address :: proc(p: rawptr, name: cstring) {
func := wglGetProcAddress(name)
switch uintptr(func) {
case 0, 1, 2, 3, ~uintptr(0):
module := LoadLibraryW(L("opengl32.dll"))
func = GetProcAddress(module, name)
}
(^rawptr)(p)^ = func
}
+3
View File
@@ -454,6 +454,7 @@ TB_ISBUTTONENABLED :: 0x0409
TBM_CLEARTICS :: 0x0409
TTM_SETTOOLINFOA :: 0x0409
CBEM_HASEDITCHANGED :: 0x040a
PBM_SETMARQUEE :: 0x040a
RB_INSERTBANDW :: 0x040a
SB_GETRECT :: 0x040a
TB_ISBUTTONCHECKED :: 0x040a
@@ -488,10 +489,12 @@ TTM_ENUMTOOLSA :: 0x040e
SB_SETICON :: 0x040f
TBM_GETTICPOS :: 0x040f
TTM_GETCURRENTTOOLA :: 0x040f
PBM_SETSTATE :: 0x0410
RB_IDTOINDEX :: 0x0410
SB_SETTIPTEXTA :: 0x0410
TBM_GETNUMTICS :: 0x0410
TTM_WINDOWFROMPOINT :: 0x0410
PBM_GETSTATE :: 0x0411
RB_GETTOOLTIPS :: 0x0411
SB_SETTIPTEXTW :: 0x0411
TBM_GETSELSTART :: 0x0411
+413
View File
@@ -0,0 +1,413 @@
package text_edit
/*
Based off the articles by rxi:
* https://rxi.github.io/textbox_behaviour.html
* https://rxi.github.io/a_simple_undo_system.html
*/
import "core:runtime"
import "core:time"
import "core:mem"
import "core:strings"
import "core:unicode/utf8"
DEFAULT_UNDO_TIMEOUT :: 300 * time.Millisecond
State :: struct {
selection: [2]int,
line_start, line_end: int,
// initialized each "frame" with `begin`
builder: ^strings.Builder, // let the caller store the text buffer data
up_index, down_index: int, // multi-lines
// undo
undo: [dynamic]^Undo_State,
redo: [dynamic]^Undo_State,
undo_text_allocator: runtime.Allocator,
id: u64, // useful for immediate mode GUIs
// Timeout information
current_time: time.Tick,
last_edit_time: time.Tick,
undo_timeout: time.Duration,
// Set these if you want cut/copy/paste functionality
set_clipboard: proc(user_data: rawptr, text: string) -> (ok: bool),
get_clipboard: proc(user_data: rawptr) -> (text: string, ok: bool),
clipboard_user_data: rawptr,
}
Undo_State :: struct {
selection: [2]int,
len: int,
text: [0]byte, // string(us.text[:us.len]) --- requiring #no_bounds_check
}
Translation :: enum u32 {
Start,
End,
Left,
Right,
Up,
Down,
Word_Left,
Word_Right,
Word_Start,
Word_End,
Soft_Line_Start,
Soft_Line_End,
}
init :: proc(s: ^State, undo_text_allocator, undo_state_allocator: runtime.Allocator, undo_timeout := DEFAULT_UNDO_TIMEOUT) {
s.undo_timeout = undo_timeout
// Used for allocating `Undo_State`
s.undo_text_allocator = undo_text_allocator
s.undo.allocator = undo_state_allocator
s.redo.allocator = undo_state_allocator
}
destroy :: proc(s: ^State) {
undo_clear(s, &s.undo)
undo_clear(s, &s.redo)
delete(s.undo)
delete(s.redo)
s.builder = nil
}
// Call at the beginning of each frame
begin :: proc(s: ^State, id: u64, builder: ^strings.Builder) {
assert(builder != nil)
if s.id != 0 {
end(s)
}
s.id = id
s.selection = {len(builder.buf), 0}
s.builder = builder
s.current_time = time.tick_now()
if s.undo_timeout <= 0 {
s.undo_timeout = DEFAULT_UNDO_TIMEOUT
}
set_text(s, string(s.builder.buf[:]))
undo_clear(s, &s.undo)
undo_clear(s, &s.redo)
}
// Call at the end of each frame
end :: proc(s: ^State) {
s.id = 0
s.builder = nil
}
set_text :: proc(s: ^State, text: string) {
strings.builder_reset(s.builder)
strings.write_string(s.builder, text)
}
undo_state_push :: proc(s: ^State, undo: ^[dynamic]^Undo_State) {
text := string(s.builder.buf[:])
item := (^Undo_State)(mem.alloc(size_of(Undo_State) + len(text), align_of(Undo_State), s.undo_text_allocator))
item.selection = s.selection
item.len = len(text)
#no_bounds_check {
runtime.copy(item.text[:len(text)], text)
}
append(undo, item)
}
undo :: proc(s: ^State, undo, redo: ^[dynamic]^Undo_State) {
if len(undo) > 0 {
undo_state_push(s, redo)
item := pop(undo)
s.selection = item.selection
#no_bounds_check {
set_text(s, string(item.text[:item.len]))
}
free(item, s.undo_text_allocator)
}
}
undo_clear :: proc(s: ^State, undo: ^[dynamic]^Undo_State) {
for len(undo) > 0 {
item := pop(undo)
free(item, s.undo_text_allocator)
}
}
undo_check :: proc(s: ^State) {
undo_clear(s, &s.redo)
if time.tick_diff(s.last_edit_time, s.current_time) > s.undo_timeout {
undo_state_push(s, &s.undo)
}
s.last_edit_time = s.current_time
}
input_text :: proc(s: ^State, text: string) {
if len(text) == 0 {
return
}
if has_selection(s) {
selection_delete(s)
}
insert(s, s.selection[0], text)
offset := s.selection[0] + len(text)
s.selection = {offset, offset}
}
input_runes :: proc(s: ^State, text: []rune) {
if len(text) == 0 {
return
}
if has_selection(s) {
selection_delete(s)
}
offset := s.selection[0]
for r in text {
b, w := utf8.encode_rune(r)
insert(s, offset, string(b[:w]))
offset += w
}
s.selection = {offset, offset}
}
insert :: proc(s: ^State, at: int, text: string) {
undo_check(s)
inject_at(&s.builder.buf, at, text)
}
remove :: proc(s: ^State, lo, hi: int) {
undo_check(s)
remove_range(&s.builder.buf, lo, hi)
}
has_selection :: proc(s: ^State) -> bool {
return s.selection[0] != s.selection[1]
}
sorted_selection :: proc(s: ^State) -> (lo, hi: int) {
lo = min(s.selection[0], s.selection[1])
hi = max(s.selection[0], s.selection[1])
lo = clamp(lo, 0, len(s.builder.buf))
hi = clamp(hi, 0, len(s.builder.buf))
s.selection[0] = lo
s.selection[1] = hi
return
}
selection_delete :: proc(s: ^State) {
lo, hi := sorted_selection(s)
remove(s, lo, hi)
s.selection = {lo, lo}
}
translate_position :: proc(s: ^State, pos: int, t: Translation) -> int {
is_continuation_byte :: proc(b: byte) -> bool {
return b <= 0x80 && b < 0xc0
}
is_space :: proc(b: byte) -> bool {
return b == ' ' || b == '\t' || b == '\n'
}
buf := s.builder.buf[:]
pos := pos
pos = clamp(pos, 0, len(buf))
switch t {
case .Start:
pos = 0
case .End:
pos = len(buf)
case .Left:
pos -= 1
for pos >= 0 && is_continuation_byte(buf[pos]) {
pos -= 1
}
case .Right:
pos += 1
for pos < len(buf) && is_continuation_byte(buf[pos]) {
pos += 1
}
case .Up:
pos = s.up_index
case .Down:
pos = s.down_index
case .Word_Left:
for pos > 0 && is_space(buf[pos-1]) {
pos -= 1
}
for pos > 0 && !is_space(buf[pos-1]) {
pos -= 1
}
case .Word_Right:
for pos < len(buf) && !is_space(buf[pos]) {
pos += 1
}
for pos < len(buf) && is_space(buf[pos]) {
pos += 1
}
case .Word_Start:
for pos > 0 && !is_space(buf[pos-1]) {
pos -= 1
}
case .Word_End:
for pos < len(buf) && !is_space(buf[pos]) {
pos += 1
}
case .Soft_Line_Start:
pos = s.line_start
case .Soft_Line_End:
pos = s.line_end
}
return clamp(pos, 0, len(buf))
}
move_to :: proc(s: ^State, t: Translation) {
if t == .Left && has_selection(s) {
lo, _ := sorted_selection(s)
s.selection = {lo, lo}
} else if t == .Right && has_selection(s) {
_, hi := sorted_selection(s)
s.selection = {hi, hi}
} else {
pos := translate_position(s, s.selection[0], t)
s.selection = {pos, pos}
}
}
select_to :: proc(s: ^State, t: Translation) {
s.selection[0] = translate_position(s, s.selection[0], t)
}
delete_to :: proc(s: ^State, t: Translation) {
if has_selection(s) {
selection_delete(s)
} else {
lo := s.selection[0]
hi := translate_position(s, lo, t)
lo, hi = min(lo, hi), max(lo, hi)
remove(s, lo, hi)
s.selection = {lo, lo}
}
}
current_selected_text :: proc(s: ^State) -> string {
lo, hi := sorted_selection(s)
return string(s.builder.buf[lo:hi])
}
cut :: proc(s: ^State) -> bool {
if copy(s) {
selection_delete(s)
return true
}
return false
}
copy :: proc(s: ^State) -> bool {
if s.set_clipboard != nil {
return s.set_clipboard(s.clipboard_user_data, current_selected_text(s))
}
return s.set_clipboard != nil
}
paste :: proc(s: ^State) -> bool {
if s.get_clipboard != nil {
input_text(s, s.get_clipboard(s.clipboard_user_data) or_return)
}
return s.get_clipboard != nil
}
Command_Set :: distinct bit_set[Command; u32]
Command :: enum u32 {
None,
Undo,
Redo,
New_Line, // multi-lines
Cut,
Copy,
Paste,
Select_All,
Backspace,
Delete,
Delete_Word_Left,
Delete_Word_Right,
Left,
Right,
Up, // multi-lines
Down, // multi-lines
Word_Left,
Word_Right,
Start,
End,
Line_Start,
Line_End,
Select_Left,
Select_Right,
Select_Up, // multi-lines
Select_Down, // multi-lines
Select_Word_Left,
Select_Word_Right,
Select_Start,
Select_End,
Select_Line_Start,
Select_Line_End,
}
MULTILINE_COMMANDS :: Command_Set{.New_Line, .Up, .Down, .Select_Up, .Select_Down}
perform_command :: proc(s: ^State, cmd: Command) {
switch cmd {
case .None: /**/
case .Undo: undo(s, &s.undo, &s.redo)
case .Redo: undo(s, &s.redo, &s.undo)
case .New_Line: input_text(s, "\n")
case .Cut: cut(s)
case .Copy: copy(s)
case .Paste: paste(s)
case .Select_All: s.selection = {len(s.builder.buf), 0}
case .Backspace: delete_to(s, .Left)
case .Delete: delete_to(s, .Right)
case .Delete_Word_Left: delete_to(s, .Word_Left)
case .Delete_Word_Right: delete_to(s, .Word_Right)
case .Left: move_to(s, .Left)
case .Right: move_to(s, .Right)
case .Up: move_to(s, .Up)
case .Down: move_to(s, .Down)
case .Word_Left: move_to(s, .Word_Left)
case .Word_Right: move_to(s, .Word_Right)
case .Start: move_to(s, .Start)
case .End: move_to(s, .End)
case .Line_Start: move_to(s, .Soft_Line_Start)
case .Line_End: move_to(s, .Soft_Line_End)
case .Select_Left: select_to(s, .Left)
case .Select_Right: select_to(s, .Right)
case .Select_Up: select_to(s, .Up)
case .Select_Down: select_to(s, .Down)
case .Select_Word_Left: select_to(s, .Word_Left)
case .Select_Word_Right: select_to(s, .Word_Right)
case .Select_Start: select_to(s, .Start)
case .Select_End: select_to(s, .End)
case .Select_Line_Start: select_to(s, .Soft_Line_Start)
case .Select_Line_End: select_to(s, .Soft_Line_End)
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
//+ignore
//+build ignore
package i18n
/*
+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]
}
+34 -32
View File
@@ -17,7 +17,7 @@ MAX_DURATION :: Duration(1<<63 - 1)
IS_SUPPORTED :: _IS_SUPPORTED
Time :: struct {
_nsec: i64, // zero is 1970-01-01 00:00:00
_nsec: i64, // Measured in UNIX nanonseconds
}
Month :: enum int {
@@ -59,36 +59,36 @@ sleep :: proc "contextless" (d: Duration) {
_sleep(d)
}
stopwatch_start :: proc(using stopwatch: ^Stopwatch) {
stopwatch_start :: proc "contextless" (using stopwatch: ^Stopwatch) {
if !running {
_start_time = tick_now()
running = true
}
}
stopwatch_stop :: proc(using stopwatch: ^Stopwatch) {
stopwatch_stop :: proc "contextless" (using stopwatch: ^Stopwatch) {
if running {
_accumulation += tick_diff(_start_time, tick_now())
running = false
}
}
stopwatch_reset :: proc(using stopwatch: ^Stopwatch) {
stopwatch_reset :: proc "contextless" (using stopwatch: ^Stopwatch) {
_accumulation = {}
running = false
}
stopwatch_duration :: proc(using stopwatch: Stopwatch) -> Duration {
stopwatch_duration :: proc "contextless" (using stopwatch: Stopwatch) -> Duration {
if !running { return _accumulation }
return _accumulation + tick_diff(_start_time, tick_now())
}
diff :: proc(start, end: Time) -> Duration {
diff :: proc "contextless" (start, end: Time) -> Duration {
d := end._nsec - start._nsec
return Duration(d)
}
since :: proc(start: Time) -> Duration {
since :: proc "contextless" (start: Time) -> Duration {
return diff(start, now())
}
@@ -117,8 +117,8 @@ duration_hours :: proc "contextless" (d: Duration) -> f64 {
return f64(hour) + f64(nsec)/(60*60*1e9)
}
duration_round :: proc(d, m: Duration) -> Duration {
_less_than_half :: #force_inline proc(x, y: Duration) -> bool {
duration_round :: proc "contextless" (d, m: Duration) -> Duration {
_less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool {
return u64(x)+u64(x) < u64(y)
}
@@ -146,45 +146,45 @@ duration_round :: proc(d, m: Duration) -> Duration {
return MAX_DURATION
}
duration_truncate :: proc(d, m: Duration) -> Duration {
duration_truncate :: proc "contextless" (d, m: Duration) -> Duration {
return d if m <= 0 else d - d%m
}
date :: proc(t: Time) -> (year: int, month: Month, day: int) {
date :: proc "contextless" (t: Time) -> (year: int, month: Month, day: int) {
year, month, day, _ = _abs_date(_time_abs(t), true)
return
}
year :: proc(t: Time) -> (year: int) {
year :: proc "contextless" (t: Time) -> (year: int) {
year, _, _, _ = _date(t, true)
return
}
month :: proc(t: Time) -> (month: Month) {
month :: proc "contextless" (t: Time) -> (month: Month) {
_, month, _, _ = _date(t, true)
return
}
day :: proc(t: Time) -> (day: int) {
day :: proc "contextless" (t: Time) -> (day: int) {
_, _, day, _ = _date(t, true)
return
}
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
clock_from_time :: proc(t: Time) -> (hour, min, sec: int) {
clock_from_time :: proc "contextless" (t: Time) -> (hour, min, sec: int) {
return clock_from_seconds(_time_abs(t))
}
clock_from_duration :: proc(d: Duration) -> (hour, min, sec: int) {
clock_from_duration :: proc "contextless" (d: Duration) -> (hour, min, sec: int) {
return clock_from_seconds(u64(d/1e9))
}
clock_from_stopwatch :: proc(s: Stopwatch) -> (hour, min, sec: int) {
clock_from_stopwatch :: proc "contextless" (s: Stopwatch) -> (hour, min, sec: int) {
return clock_from_duration(stopwatch_duration(s))
}
clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
sec = int(nsec % SECONDS_PER_DAY)
hour = sec / SECONDS_PER_HOUR
sec -= hour * SECONDS_PER_HOUR
@@ -193,11 +193,11 @@ clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
return
}
read_cycle_counter :: proc() -> u64 {
read_cycle_counter :: proc "contextless" () -> u64 {
return u64(intrinsics.read_cycle_counter())
}
unix :: proc(sec: i64, nsec: i64) -> Time {
unix :: proc "contextless" (sec: i64, nsec: i64) -> Time {
sec, nsec := sec, nsec
if nsec < 0 || nsec >= 1e9 {
n := nsec / 1e9
@@ -208,20 +208,20 @@ unix :: proc(sec: i64, nsec: i64) -> Time {
sec -= 1
}
}
return Time{(sec*1e9 + nsec) + UNIX_TO_INTERNAL}
return Time{(sec*1e9 + nsec)}
}
to_unix_seconds :: time_to_unix
time_to_unix :: proc(t: Time) -> i64 {
time_to_unix :: proc "contextless" (t: Time) -> i64 {
return t._nsec/1e9
}
to_unix_nanoseconds :: time_to_unix_nano
time_to_unix_nano :: proc(t: Time) -> i64 {
time_to_unix_nano :: proc "contextless" (t: Time) -> i64 {
return t._nsec
}
time_add :: proc(t: Time, d: Duration) -> Time {
time_add :: proc "contextless" (t: Time, d: Duration) -> Time {
return Time{t._nsec + i64(d)}
}
@@ -231,7 +231,7 @@ time_add :: proc(t: Time, d: Duration) -> Time {
// On Windows it depends but is comparable with regular sleep in the worst case.
// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to
// tell Windows to use a more accurate timer for your process.
accurate_sleep :: proc(d: Duration) {
accurate_sleep :: proc "contextless" (d: Duration) {
to_sleep, estimate, mean, m2, count: Duration
to_sleep = d
@@ -279,19 +279,19 @@ ABSOLUTE_TO_UNIX :: -UNIX_TO_ABSOLUTE
@(private)
_date :: proc(t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
_date :: proc "contextless" (t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
year, month, day, yday = _abs_date(_time_abs(t), full)
return
}
@(private)
_time_abs :: proc(t: Time) -> u64 {
_time_abs :: proc "contextless" (t: Time) -> u64 {
return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE)
}
@(private)
_abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
_is_leap_year :: proc(year: int) -> bool {
_abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
_is_leap_year :: proc "contextless" (year: int) -> bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}
@@ -352,9 +352,11 @@ _abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, y
return
}
datetime_to_time :: proc(year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
divmod :: proc(year: int, divisor: int) -> (div: int, mod: int) {
assert(divisor > 0)
datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
divmod :: proc "contextless" (year: int, divisor: int) -> (div: int, mod: int) {
if divisor <= 0 {
intrinsics.debug_trap()
}
div = int(year / divisor)
mod = year % divisor
return
+2
View File
@@ -22,3 +22,5 @@ _tick_now :: proc "contextless" () -> Tick {
return {}
}
_yield :: proc "contextless" () {
}

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