Compare commits

..

309 Commits

Author SHA1 Message Date
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 d6cb105d5f Fix LLVM type cycle nonsense with procedure types 2022-11-23 23:32:34 +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 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
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
151 changed files with 19608 additions and 5086 deletions
+15 -10
View File
@@ -38,6 +38,11 @@ jobs:
cd tests/vendor cd tests/vendor
make make
timeout-minutes: 10 timeout-minutes: 10
- name: Odin internals tests
run: |
cd tests/internal
make
timeout-minutes: 10
- name: Odin check examples/all for Linux i386 - name: Odin check examples/all for Linux i386
run: ./odin check examples/all -vet -strict-style -target:linux_i386 run: ./odin check examples/all -vet -strict-style -target:linux_i386
timeout-minutes: 10 timeout-minutes: 10
@@ -87,10 +92,10 @@ jobs:
cd tests/vendor cd tests/vendor
make make
timeout-minutes: 10 timeout-minutes: 10
- name: Odin issues tests - name: Odin internals tests
run: | run: |
cd tests/issues cd tests/internal
./run.sh make
timeout-minutes: 10 timeout-minutes: 10
- name: Odin check examples/all for Darwin arm64 - name: Odin check examples/all for Darwin arm64
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64 run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
@@ -151,6 +156,13 @@ jobs:
cd tests\vendor cd tests\vendor
call build.bat call build.bat
timeout-minutes: 10 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 - name: core:math/big tests
shell: cmd shell: cmd
run: | run: |
@@ -158,13 +170,6 @@ jobs:
cd tests\core\math\big cd tests\core\math\big
call build.bat call build.bat
timeout-minutes: 10 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 - name: Odin check examples/all for Windows 32bits
shell: cmd shell: cmd
run: | run: |
+1 -1
View File
@@ -82,7 +82,7 @@ A wiki maintained by the Odin community.
#### [Odin Discord](https://discord.gg/sVBPHEv) #### [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 ### Articles
+15
View File
@@ -2,6 +2,21 @@
setlocal EnableDelayedExpansion 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 ( for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
set CURR_DATE_TIME=%%j set CURR_DATE_TIME=%%j
) )
+20
View File
@@ -44,6 +44,12 @@ config_darwin() {
fi fi
fi 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 -liconv -ldl" LDFLAGS="$LDFLAGS -liconv -ldl"
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS -lLLVM-C" LDFLAGS="$LDFLAGS -lLLVM-C"
@@ -97,6 +103,12 @@ config_linux() {
panic "Requirement: llvm-config must be base version greater than 11" panic "Requirement: llvm-config must be base version greater than 11"
fi 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" LDFLAGS="$LDFLAGS -ldl"
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)" CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN" 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 ./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 case $OS in
Linux) Linux)
config_linux config_linux
+7
View File
@@ -15,3 +15,10 @@ if not exist "vendor\miniaudio\lib\*.lib" (
call build.bat call build.bat
popd 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, // EDOM,
// EILSEQ // EILSEQ
// ERANGE // ERANGE
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD { when ODIN_OS == .Linux {
@(private="file") @(private="file")
@(default_calling_convention="c") @(default_calling_convention="c")
foreign libc { 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 --- _get_errno :: proc() -> ^int ---
} }
+1 -1
View File
@@ -1,4 +1,4 @@
//+ignore //+build ignore
package gzip package gzip
/* /*
+1 -1
View File
@@ -1,4 +1,4 @@
//+ignore //+build ignore
package zlib package zlib
/* /*
+17 -17
View File
@@ -8,40 +8,40 @@ 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 return a.len
} }
cap :: proc(a: $A/Small_Array) -> int { cap :: proc "contextless" (a: $A/Small_Array) -> int {
return builtin.len(a.data) 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 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] 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] 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] return &a.data[index]
} }
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) { set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
a.data[index] = item 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)) 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^) { if a.len < cap(a^) {
a.data[a.len] = item a.data[a.len] = item
a.len += 1 a.len += 1
@@ -50,7 +50,7 @@ push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
return false 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^) { if a.len < cap(a^) {
a.len += 1 a.len += 1
data := slice(a) data := slice(a)
@@ -61,14 +61,14 @@ push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
return false 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) assert(condition=(N > 0 && a.len > 0), loc=loc)
item := a.data[a.len-1] item := a.data[a.len-1]
a.len -= 1 a.len -= 1
return item 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) assert(condition=(N > 0 && a.len > 0), loc=loc)
item := a.data[0] item := a.data[0]
s := slice(a) s := slice(a)
@@ -77,7 +77,7 @@ pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
return item 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 { if N > 0 && a.len > 0 {
item = a.data[a.len-1] item = a.data[a.len-1]
a.len -= 1 a.len -= 1
@@ -86,7 +86,7 @@ pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
return 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 { if N > 0 && a.len > 0 {
item = a.data[0] item = a.data[0]
s := slice(a) s := slice(a)
@@ -97,16 +97,16 @@ pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
return 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) assert(condition=a.len >= count, loc=loc)
a.len -= count a.len -= count
} }
clear :: proc(a: ^$A/Small_Array($N, $T)) { clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
resize(a, 0) 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[:]) n := copy(a.data[a.len:], items[:])
a.len += n a.len += n
} }
+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 opt_write_start(w, opt, '{') or_return
if m != nil { if m != nil {
if info.generated_struct == nil { if info.map_info == nil {
return .Unsupported_Type return .Unsupported_Type
} }
entries := &m.entries map_cap := uintptr(runtime.map_cap(m^))
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct) ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array) for bucket_index in 0..<map_cap {
entry_type := ed.elem.variant.(runtime.Type_Info_Struct) if !runtime.map_hash_is_valid(hs[bucket_index]) {
entry_size := ed.elem_size continue
}
for i in 0..<entries.len { key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
opt_write_iteration(w, opt, i) or_return value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))
data := uintptr(entries.data) + uintptr(i*entry_size)
key := rawptr(data + entry_type.offsets[2])
value := rawptr(data + entry_type.offsets[3])
// check for string type // check for string type
{ {
@@ -281,13 +278,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
name: string name: string
#partial switch info in ti.variant { #partial switch info in ti.variant {
case runtime.Type_Info_String: case runtime.Type_Info_String:
switch s in a { switch s in a {
case string: name = s case string: name = s
case cstring: name = string(s) case cstring: name = string(s)
} }
opt_write_key(w, opt, name) or_return opt_write_key(w, opt, name) or_return
case: return .Unsupported_Type 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 return UNSUPPORTED_TYPE
} }
raw_map := (^mem.Raw_Map)(v.data) raw_map := (^mem.Raw_Map)(v.data)
if raw_map.entries.allocator.procedure == nil { if raw_map.allocator.procedure == nil {
raw_map.entries.allocator = p.allocator 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 elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
defer delete(elem_backing, p.allocator) 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 return err
} }
key_hash := runtime.default_hasher_string(&key, 0)
key_ptr := rawptr(&key) key_ptr := rawptr(&key)
key_cstr: cstring key_cstr: cstring
@@ -430,7 +427,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
key_ptr = &key_cstr 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 { if set_ptr == nil {
delete(key, p.allocator) delete(key, p.allocator)
} }
+34 -30
View File
@@ -162,7 +162,25 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
p("Panic", message, loc) 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 formats using the default print settings and writes to buf
sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string { 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 was_prev_index := false
loop: for i := 0; i < end; /**/ { 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 prev_i := i
for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') { for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') {
@@ -975,7 +993,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
} }
} }
else { else {
io.write_string(fi.writer, s[:fi.width], &fi.n) io.write_string(fi.writer, s, &fi.n)
} }
} }
else else
@@ -2051,41 +2069,27 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
m := (^mem.Raw_Map)(v.data) m := (^mem.Raw_Map)(v.data)
if m != nil { if m != nil {
if info.generated_struct == nil { if info.map_info == nil {
return return
} }
entries := &m.entries map_cap := uintptr(runtime.map_cap(m^))
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct) ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array) j := 0
entry_type := ed.elem.variant.(runtime.Type_Info_Struct) for bucket_index in 0..<map_cap {
entry_size := ed.elem_size if !runtime.map_hash_is_valid(hs[bucket_index]) {
/* continue
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,
},
} }
*/
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') fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
io.write_string(fi.writer, "=", &fi.n) 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') 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)) w := io.to_writer(os.stream_from_handle(fd))
return wprintln(w=w, args=args, sep=sep) 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 { fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
w := io.to_writer(os.stream_from_handle(fd)) w := io.to_writer(os.stream_from_handle(fd))
return wprintf(w, fmt, ..args) 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) } 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 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) } 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) } 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 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) } 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 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) } 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) } 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 return
} }
// If you have a choice, prefer fnv32a
@(optimization_mode="speed") @(optimization_mode="speed")
fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 { fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed h: u32 = seed
for b in data { for b in data {
h = (h * 0x01000193) ~ u32(b) h = (h * 0x01000193) ~ u32(b)
@@ -81,15 +82,18 @@ fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
return h 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") @(optimization_mode="speed")
fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 { fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed h: u64 = seed
for b in data { for b in data {
h = (h * 0x100000001b3) ~ u64(b) h = (h * 0x100000001b3) ~ u64(b)
} }
return h return h
} }
@(optimization_mode="speed") @(optimization_mode="speed")
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 { fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed h: u32 = seed
+1 -1
View File
@@ -8,7 +8,7 @@
An example of how to use `load`. An example of how to use `load`.
*/ */
//+ignore //+build ignore
package png package png
import "core:image" import "core:image"
+4 -1
View File
@@ -1,5 +1,5 @@
// This is purely for documentation // This is purely for documentation
//+ignore //+build ignore
package intrinsics package intrinsics
// Package-Related // 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_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_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) --- type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
constant_utf16_cstring :: proc($literal: string) -> [^]u16 --- 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}, args = {la.prefix, padding, size, alignment},
location = location, 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: case .Free:
if old_size != 0 { if old_size != 0 {
logf( logf(
-2
View File
@@ -25,8 +25,6 @@
TODO: Handle +/- Infinity and NaN. TODO: Handle +/- Infinity and NaN.
*/ */
//+ignore
package math_big package math_big
import "core:mem" 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. 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 package math_big
import "core:time" import "core:time"
+2
View File
@@ -0,0 +1,2 @@
// core:math/linalg implements linear algebra procedures useful for 3D spatial transformations
package linalg
+337 -19
View File
@@ -114,6 +114,92 @@ exp :: proc{
exp_f64, exp_f64le, exp_f64be, 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 { 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 negative infinity.
// If sign == 0, is_inf reports whether f is either infinity. // If sign == 0, is_inf reports whether f is either infinity.
is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool { is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool {
class := classify(abs(x)) class := classify(x)
switch { switch {
case sign > 0: case sign > 0:
return class == .Inf 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 { is_inf_f32 :: proc "contextless" (x: f32, sign: int = 0) -> bool {
class := classify(abs(x)) class := classify(x)
switch { switch {
case sign > 0: case sign > 0:
return class == .Inf 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 { is_inf_f64 :: proc "contextless" (x: f64, sign: int = 0) -> bool {
class := classify(abs(x)) class := classify(x)
switch { switch {
case sign > 0: case sign > 0:
return class == .Inf return class == .Inf
@@ -1344,20 +1430,20 @@ atan2_f64 :: proc "contextless" (y, x: f64) -> f64 {
} }
return copy_sign(PI, y) return copy_sign(PI, y)
case x == 0: case x == 0:
return copy_sign(PI*0.5, y) return copy_sign(PI/2, y)
case is_inf(x, 0): case is_inf(x, 0):
if is_inf(x, 1) { if is_inf(x, 1) {
if is_inf(y, 0) { if is_inf(y, 0) {
return copy_sign(PI*0.25, y) return copy_sign(PI/4, y)
} }
return copy_sign(0, y) return copy_sign(0, y)
} }
if is_inf(y, 0) { if is_inf(y, 0) {
return copy_sign(PI*0.75, y) return copy_sign(3*PI/4, y)
} }
return copy_sign(PI, y) return copy_sign(PI, y)
case is_inf(y, 0): case is_inf(y, 0):
return copy_sign(PI*0.5, y) return copy_sign(PI/2, y)
} }
q := atan(y / x) q := atan(y / x)
@@ -1379,34 +1465,266 @@ atan2_f64be :: proc "contextless" (y, x: f64be) -> f64be {
} }
atan2 :: proc{ atan2 :: proc{
atan2_f16, atan2_f16le, atan2_f16be, atan2_f64, atan2_f32, atan2_f16,
atan2_f32, atan2_f32le, atan2_f32be, atan2_f64le, atan2_f64be,
atan2_f64, atan2_f64le, atan2_f64be, atan2_f32le, atan2_f32be,
atan2_f16le, atan2_f16be,
} }
atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) { atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
return atan2(x, 1) 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) { 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) { 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) { tanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
t := exp(2*x) P0 :: -9.64399179425052238628e-1
return (t - 1) / (t + 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) { 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] 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) 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 { free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return runtime.mem_free(ptr, allocator, loc) 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 { free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return runtime.mem_free_bytes(bytes, allocator, loc) 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) { delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
free(raw_data(str), allocator, loc) return free_with_size(raw_data(str), len(str), allocator, loc)
} }
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) { delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
free((^byte)(str), allocator, loc) return free((^byte)(str), allocator, loc)
} }
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) { delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
free(raw_data(array), array.allocator, loc) 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) { delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
free(raw_data(array), allocator, loc) 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) { delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
raw := transmute(Raw_Map)m return runtime.map_free_dynamic(transmute(Raw_Map)m, runtime.map_info(T), loc)
delete_slice(raw.hashes, raw.entries.allocator, loc)
free(raw.entries.data, raw.entries.allocator, loc)
} }
@@ -154,8 +164,6 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
return nil, .Out_Of_Memory 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) { 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) runtime.make_slice_error_loc(loc, len)
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return 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) 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) { 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) { make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_dynamic_array_len_cap(T, len, len, allocator, loc) return 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 array = transmute(T)s
return 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) runtime.make_map_expr_error_loc(loc, cap)
context.allocator = allocator context.allocator = allocator
m: T m: T
reserve_map(&m, cap) reserve_map(&m, cap, loc)
return m return m
} }
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) { make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
+60 -34
View File
@@ -59,7 +59,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
arena := cast(^Arena)allocator_data arena := cast(^Arena)allocator_data
switch mode { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
#no_bounds_check end := &arena.data[arena.offset] #no_bounds_check end := &arena.data[arena.offset]
ptr := align_forward(end, uintptr(alignment)) 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.offset += total_size
arena.peak_used = max(arena.peak_used, arena.offset) 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 return byte_slice(ptr, size), nil
case .Free: case .Free:
@@ -87,7 +89,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Query_Features: case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory) set := (^Allocator_Mode_Set)(old_memory)
if set != nil { if set != nil {
set^ = {.Alloc, .Free_All, .Resize, .Query_Features} set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
} }
return nil, nil return nil, nil
@@ -162,7 +164,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size := size size := size
switch mode { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
size = align_forward_int(size, alignment) size = align_forward_int(size, alignment)
switch { switch {
@@ -170,7 +172,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
start := uintptr(raw_data(s.data)) start := uintptr(raw_data(s.data))
ptr := start + uintptr(s.curr_offset) ptr := start + uintptr(s.curr_offset)
ptr = align_forward_uintptr(ptr, uintptr(alignment)) 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) s.prev_allocation = rawptr(ptr)
offset := int(ptr - start) offset := int(ptr - start)
@@ -180,7 +184,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case size <= len(s.data): case size <= len(s.data):
start := uintptr(raw_data(s.data)) start := uintptr(raw_data(s.data))
ptr := align_forward_uintptr(start, uintptr(alignment)) 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) s.prev_allocation = rawptr(ptr)
offset := int(ptr - start) offset := int(ptr - start)
@@ -211,6 +217,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return ptr, err return ptr, err
case .Free: case .Free:
if old_memory == nil {
return nil, nil
}
start := uintptr(raw_data(s.data)) start := uintptr(raw_data(s.data))
end := start + uintptr(len(s.data)) end := start + uintptr(len(s.data))
old_ptr := uintptr(old_memory) old_ptr := uintptr(old_memory)
@@ -266,7 +275,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Query_Features: case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory) set := (^Allocator_Mode_Set)(old_memory)
if set != nil { 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 return nil, nil
@@ -333,7 +342,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, .Invalid_Argument 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) 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)) padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header))
if s.curr_offset + padding + size > len(s.data) { 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) 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 return byte_slice(rawptr(next_addr), size), nil
} }
switch mode { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
return raw_alloc(s, size, alignment) return raw_alloc(s, size, alignment, mode == .Alloc)
case .Free: case .Free:
if old_memory == nil { if old_memory == nil {
return nil, nil return nil, nil
@@ -392,7 +403,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Resize: case .Resize:
if old_memory == nil { if old_memory == nil {
return raw_alloc(s, size, alignment) return raw_alloc(s, size, alignment, true)
} }
if size == 0 { if size == 0 {
return nil, nil 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))) old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
if old_offset != header.prev_offset { if old_offset != header.prev_offset {
data, err := raw_alloc(s, size, alignment) data, err := raw_alloc(s, size, alignment, true)
if err == nil { if err == nil {
runtime.copy(data, byte_slice(old_memory, old_size)) 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: case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory) set := (^Allocator_Mode_Set)(old_memory)
if set != nil { 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 return nil, nil
case .Query_Info: 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) 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) 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)) padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header))
if s.offset + padding + size > len(s.data) { 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) 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 return byte_slice(rawptr(next_addr), size), nil
} }
switch mode { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
return raw_alloc(s, size, align) return raw_alloc(s, size, align, mode == .Alloc)
case .Free: case .Free:
if old_memory == nil { if old_memory == nil {
return nil, nil return nil, nil
@@ -548,7 +561,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Resize: case .Resize:
if old_memory == nil { if old_memory == nil {
return raw_alloc(s, size, align) return raw_alloc(s, size, align, true)
} }
if size == 0 { if size == 0 {
return nil, nil 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 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 { if err == nil {
runtime.copy(data, byte_slice(old_memory, old_size)) 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: case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory) set := (^Allocator_Mode_Set)(old_memory)
if set != nil { 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 return nil, nil
@@ -623,7 +636,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
pool := (^Dynamic_Pool)(allocator_data) pool := (^Dynamic_Pool)(allocator_data)
switch mode { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
return dynamic_pool_alloc_bytes(pool, size) return dynamic_pool_alloc_bytes(pool, size)
case .Free: case .Free:
return nil, .Mode_Not_Implemented return nil, .Mode_Not_Implemented
@@ -643,7 +656,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
case .Query_Features: case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory) set := (^Allocator_Mode_Set)(old_memory)
if set != nil { 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 return nil, nil
@@ -794,6 +807,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
if size > 0 { if size > 0 {
panic("mem: panic allocator, .Alloc called") panic("mem: panic allocator, .Alloc called")
} }
case .Alloc_Non_Zeroed:
if size > 0 {
panic("mem: panic allocator, .Alloc_Non_Zeroed called")
}
case .Resize: case .Resize:
if size > 0 { if size > 0 {
panic("mem: panic allocator, .Resize called") panic("mem: panic allocator, .Resize called")
@@ -831,6 +848,7 @@ Tracking_Allocator_Entry :: struct {
memory: rawptr, memory: rawptr,
size: int, size: int,
alignment: int, alignment: int,
mode: Allocator_Mode,
err: Allocator_Error, err: Allocator_Error,
location: runtime.Source_Code_Location, location: runtime.Source_Code_Location,
} }
@@ -849,6 +867,10 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc
t.backing = backing_allocator t.backing = backing_allocator
t.allocation_map.allocator = internals_allocator t.allocation_map.allocator = internals_allocator
t.bad_free_array.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) { tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
@@ -856,6 +878,13 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
delete(t.bad_free_array) 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 { tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
return Allocator{ return Allocator{
data = data, data = data,
@@ -865,7 +894,7 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int, 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) data := (^Tracking_Allocator)(allocator_data)
if mode == .Query_Info { if mode == .Query_Info {
info := (^Allocator_Query_Info)(old_memory) info := (^Allocator_Query_Info)(old_memory)
@@ -877,21 +906,16 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
info.pointer = nil 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 { if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{ append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
memory = old_memory, memory = old_memory,
location = loc, location = loc,
}) })
} else { } else {
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
if err != nil {
return result, err
}
} }
result_ptr := raw_data(result) result_ptr := raw_data(result)
@@ -900,10 +924,11 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
} }
switch mode { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{ data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
memory = result_ptr, memory = result_ptr,
size = size, size = size,
mode = mode,
alignment = alignment, alignment = alignment,
err = err, err = err,
location = loc, location = loc,
@@ -921,6 +946,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{ data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
memory = result_ptr, memory = result_ptr,
size = size, size = size,
mode = mode,
alignment = alignment, alignment = alignment,
err = err, err = err,
location = loc, location = loc,
@@ -929,7 +955,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
case .Query_Features: case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory) set := (^Allocator_Mode_Set)(old_memory)
if set != nil { 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 return nil, nil
@@ -937,6 +963,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
unreachable() unreachable()
} }
return result, err return
} }
+2 -6
View File
@@ -1,5 +1,6 @@
package mem package mem
import "core:builtin"
import "core:runtime" import "core:runtime"
Raw_Any :: runtime.Raw_Any Raw_Any :: runtime.Raw_Any
@@ -21,12 +22,7 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
return transmute(any)Raw_Any{data, id} return transmute(any)Raw_Any{data, id}
} }
raw_array_data :: runtime.raw_array_data raw_data :: builtin.raw_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) { Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {
+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 :: 1<<20
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: DEFAULT_ARENA_STATIC_COMMIT_SIZE
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
DEFAULT_ARENA_STATIC_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
@(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 package mem_virtual
import "core:mem" import "core:mem"
import "core:intrinsics"
DEFAULT_PAGE_SIZE := uint(4096) 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.committed = committed
pmblock.block.reserved = reserved 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 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 { calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
alignment_offset := uint(0) alignment_offset := uint(0)
ptr := uintptr(block.base[block.used:]) ptr := uintptr(block.base[block.used:])
@@ -134,11 +128,18 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
return nil return nil
} }
if block == nil {
return nil, .Out_Of_Memory
}
alignment_offset := calc_alignment_offset(block, uintptr(alignment)) 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 err = .Out_Of_Memory
return 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) { memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
if block := (^Platform_Memory_Block)(block_to_free); block != nil { 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) 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 //+private
package mem_virtual package mem_virtual
import "core:sync"
Platform_Memory_Block :: struct { Platform_Memory_Block :: struct {
block: Memory_Block, block: Memory_Block,
committed: uint, committed: uint,
reserved: 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) { 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) { platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
if to_commit < block.committed { if to_commit < block.committed {
return nil return nil
@@ -63,7 +38,6 @@ platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_
return .Out_Of_Memory return .Out_Of_Memory
} }
commit(block, to_commit) or_return commit(block, to_commit) or_return
block.committed = to_commit block.committed = to_commit
return nil return nil
+1 -4
View File
@@ -2,7 +2,6 @@ package os
import win32 "core:sys/windows" import win32 "core:sys/windows"
import "core:strings" import "core:strings"
import "core:time"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { 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) { 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.mode |= file_type_mode(h);
} }
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) windows_set_file_info_times(&fi, d)
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))
fi.is_dir = fi.mode & File_Mode_Dir != 0 fi.is_dir = fi.mode & File_Mode_Dir != 0
return return
+4 -2
View File
@@ -162,7 +162,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
total_read: int total_read: int
length := len(data) 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 e: win32.BOOL
if is_console { if is_console {
@@ -172,7 +173,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
return int(total_read), err return int(total_read), err
} }
} else { } 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 { if single_read_length <= 0 || !e {
err := Errno(win32.GetLastError()) err := Errno(win32.GetLastError())
+5 -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. // 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)) a := max(alignment, align_of(rawptr))
space := size + a - 1 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)^ 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))
} else { } 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))) 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 { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
return aligned_alloc(size, alignment) return aligned_alloc(size, alignment, nil, mode == .Alloc)
case .Free: case .Free:
aligned_free(old_memory) aligned_free(old_memory)
@@ -244,7 +244,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
case .Query_Features: case .Query_Features:
set := (^mem.Allocator_Mode_Set)(old_memory) set := (^mem.Allocator_Mode_Set)(old_memory)
if set != nil { if set != nil {
set^ = {.Alloc, .Free, .Resize, .Query_Features} set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
} }
return nil, nil return nil, nil
+1 -1
View File
@@ -130,7 +130,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
f := new(File, _file_allocator()) f := new(File, _file_allocator())
f.impl.allocator = _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.name = strings.clone(name, f.impl.allocator)
f.impl.wname = win32.utf8_to_wstring(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 { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
return aligned_alloc(size, alignment) return aligned_alloc(size, alignment)
case .Free: case .Free:
+11 -11
View File
@@ -4,17 +4,17 @@ package os2
import "core:mem" import "core:mem"
import win32 "core:sys/windows" import win32 "core:sys/windows"
heap_alloc :: proc(size: int) -> rawptr { heap_alloc :: proc(size: int, zero_memory: bool) -> rawptr {
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size)) 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 { if new_size == 0 {
heap_free(ptr) heap_free(ptr)
return nil return nil
} }
if ptr == 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)) 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. // 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)) a := max(alignment, align_of(rawptr))
space := size + a - 1 space := size + a - 1
allocated_mem: rawptr allocated_mem: rawptr
if old_ptr != nil { if old_ptr != nil {
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^ 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 { } 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))) 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 { if p == nil {
return nil, nil return nil, nil
} }
return aligned_alloc(new_size, new_alignment, p) return aligned_alloc(new_size, new_alignment, true, p)
} }
switch mode { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
return aligned_alloc(size, alignment) return aligned_alloc(size, alignment, mode == .Alloc)
case .Free: case .Free:
aligned_free(old_memory) aligned_free(old_memory)
@@ -87,7 +87,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
case .Resize: case .Resize:
if old_memory == nil { if old_memory == nil {
return aligned_alloc(size, alignment) return aligned_alloc(size, alignment, true)
} }
return aligned_resize(old_memory, old_size, size, alignment) return aligned_resize(old_memory, old_size, size, alignment)
+12 -3
View File
@@ -404,6 +404,9 @@ read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
if bytes_read == -1 { if bytes_read == -1 {
return bytes_read_total, 1 return bytes_read_total, 1
} }
if bytes_read == 0 {
break
}
bytes_read_total += bytes_read bytes_read_total += bytes_read
} }
@@ -643,9 +646,15 @@ access :: proc(path: string, mask: int) -> bool {
return _unix_access(cstr, mask) == 0 return _unix_access(cstr, mask) == 0
} }
heap_alloc :: proc(size: int) -> rawptr { heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
assert(size > 0) if size <= 0 {
return _unix_calloc(1, size) return nil
}
if zero_memory {
return _unix_calloc(1, size)
} else {
return _unix_malloc(size)
}
} }
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+2 -2
View File
@@ -18,8 +18,8 @@ current_thread_id :: proc "contextless" () -> int {
return (int) (es.ThreadGetID(es.CURRENT_THREAD)); return (int) (es.ThreadGetID(es.CURRENT_THREAD));
} }
heap_alloc :: proc(size: int) -> rawptr { heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
return es.HeapAllocate(size, false); return es.HeapAllocate(size, zero_memory);
} }
heap_free :: proc(ptr: rawptr) { heap_free :: proc(ptr: rawptr) {
+9 -3
View File
@@ -603,9 +603,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
return true, ERROR_NONE return true, ERROR_NONE
} }
heap_alloc :: proc(size: int) -> rawptr { heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
assert(size >= 0) if size <= 0 {
return _unix_calloc(1, c.size_t(size)) 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 { heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+9 -3
View File
@@ -755,9 +755,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
return true, ERROR_NONE return true, ERROR_NONE
} }
heap_alloc :: proc(size: int) -> rawptr { heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
assert(size >= 0) if size <= 0 {
return _unix_calloc(1, c.size_t(size)) 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 { heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+9 -3
View File
@@ -605,9 +605,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
return true, ERROR_NONE return true, ERROR_NONE
} }
heap_alloc :: proc(size: int) -> rawptr { heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
assert(size >= 0) if size <= 0 {
return _unix_calloc(1, c.size_t(size)) 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 { heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+33 -4
View File
@@ -24,7 +24,7 @@ O_CLOEXEC :: 0x80000
stdin: Handle = 0 stdin: Handle = 0
stdout: Handle = 1 stdout: Handle = 1
stderr: Handle = 2 stderr: Handle = 2
current_dir: Handle = 3
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
iovs := wasi.ciovec_t(data) 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) return int(n), Errno(err)
} }
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { 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 { close :: proc(fd: Handle) -> Errno {
err := wasi.fd_close(wasi.fd_t(fd)) err := wasi.fd_close(wasi.fd_t(fd))
@@ -72,7 +101,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 return nil
} }
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
@@ -96,4 +125,4 @@ heap_free :: proc(ptr: rawptr) {
exit :: proc "contextless" (code: int) -> ! { exit :: proc "contextless" (code: int) -> ! {
runtime._cleanup_runtime_contextless() runtime._cleanup_runtime_contextless()
wasi.proc_exit(wasi.exitcode_t(code)) wasi.proc_exit(wasi.exitcode_t(code))
} }
+2 -2
View File
@@ -91,8 +91,8 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
heap_alloc :: proc(size: int) -> rawptr { heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size)) 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) -> rawptr {
if new_size == 0 { if new_size == 0 {
+10 -10
View File
@@ -228,6 +228,13 @@ file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HAN
return 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) @(private)
file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) { 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) 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.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.is_dir = fi.mode & File_Mode_Dir != 0 fi.is_dir = fi.mode & File_Mode_Dir != 0
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) windows_set_file_info_times(&fi, d)
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))
fi.fullpath, e = full_path_from_name(name) fi.fullpath, e = full_path_from_name(name)
fi.name = basename(fi.fullpath) 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.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.is_dir = fi.mode & File_Mode_Dir != 0 fi.is_dir = fi.mode & File_Mode_Dir != 0
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) windows_set_file_info_times(&fi, d)
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))
fi.fullpath, e = full_path_from_name(name) fi.fullpath, e = full_path_from_name(name)
fi.name = basename(fi.fullpath) 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.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag)
fi.is_dir = fi.mode & File_Mode_Dir != 0 fi.is_dir = fi.mode & File_Mode_Dir != 0
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) windows_set_file_info_times(&fi, &d)
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))
return fi, ERROR_NONE 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: // 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 directory. 'walk' skips directory contents
// when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory // 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' // 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 // 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: Walking large directories can be inefficient due to the lexical sort
// NOTE: walk does not follow symbolic links // 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 // 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) info, err := os.lstat(root, context.temp_allocator)
defer os.file_info_delete(info, context.temp_allocator) defer os.file_info_delete(info, context.temp_allocator)
skip_dir: bool skip_dir: bool
if err != 0 { if err != 0 {
err, skip_dir = walk_proc(info, err) err, skip_dir = walk_proc(info, err, user_data)
} else { } else {
err, skip_dir = _walk(info, walk_proc) err, skip_dir = _walk(info, walk_proc, user_data)
} }
return 0 if skip_dir else err return 0 if skip_dir else err
} }
@(private) @(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.is_dir {
if info.fullpath == "" && info.name == "" { if info.fullpath == "" && info.name == "" {
// ignore empty things // ignore empty things
return return
} }
return walk_proc(info, 0) return walk_proc(info, 0, user_data)
} }
fis: []os.File_Info 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) fis, err = read_dir(info.fullpath, context.temp_allocator)
defer os.file_info_slice_delete(fis, 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 { if err != 0 || err1 != 0 || skip_dir {
err = err1 err = err1
return return
} }
for fi in fis { 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 err != 0 || skip_dir {
if !fi.is_dir || !skip_dir { if !fi.is_dir || !skip_dir {
return 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
}
+2 -2
View File
@@ -273,7 +273,7 @@ length :: proc(val: any) -> int {
return (^runtime.Raw_Dynamic_Array)(val.data).len return (^runtime.Raw_Dynamic_Array)(val.data).len
case Type_Info_Map: 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: case Type_Info_String:
if a.is_cstring { if a.is_cstring {
@@ -305,7 +305,7 @@ capacity :: proc(val: any) -> int {
return (^runtime.Raw_Dynamic_Array)(val.data).cap return (^runtime.Raw_Dynamic_Array)(val.data).cap
case Type_Info_Map: case Type_Info_Map:
return (^runtime.Raw_Map)(val.data).entries.cap return runtime.map_cap((^runtime.Raw_Map)(val.data)^)
} }
return 0 return 0
} }
+29 -7
View File
@@ -143,11 +143,9 @@ Type_Info_Enum :: struct {
values: []Type_Info_Enum_Value, values: []Type_Info_Enum_Value,
} }
Type_Info_Map :: struct { Type_Info_Map :: struct {
key: ^Type_Info, key: ^Type_Info,
value: ^Type_Info, value: ^Type_Info,
generated_struct: ^Type_Info, map_info: ^Map_Info,
key_equal: Equal_Proc,
key_hasher: Hasher_Proc,
} }
Type_Info_Bit_Set :: struct { Type_Info_Bit_Set :: struct {
elem: ^Type_Info, elem: ^Type_Info,
@@ -303,6 +301,7 @@ Allocator_Mode :: enum byte {
Resize, Resize,
Query_Features, Query_Features,
Query_Info, Query_Info,
Alloc_Non_Zeroed,
} }
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode] Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
@@ -393,9 +392,32 @@ Raw_Dynamic_Array :: struct {
allocator: Allocator, allocator: Allocator,
} }
// The raw, type-erased representation of a map.
//
// 32-bytes on 64-bit
// 16-bytes on 32-bit
Raw_Map :: struct { Raw_Map :: struct {
hashes: []Map_Index, // A single allocation spanning all keys, values, and hashes.
entries: Raw_Dynamic_Array, // {
// 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 { Raw_Any :: struct {
+18 -92
View File
@@ -159,20 +159,7 @@ delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #cal
} }
@builtin @builtin
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error { delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
Entry :: struct { return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc)
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
} }
@@ -244,12 +231,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
return return
} }
@(builtin) @(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 :: 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, cap) make_map_expr_error_loc(loc, capacity)
context.allocator = allocator context.allocator = allocator
m: T m: T
reserve_map(&m, cap) reserve_map(&m, capacity, loc)
return m return m
} }
@(builtin) @(builtin)
@@ -285,36 +272,24 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
if m == nil { if m == nil {
return return
} }
raw_map := (^Raw_Map)(m) map_clear_dynamic((^Raw_Map)(m), map_info(T))
entries := (^Raw_Dynamic_Array)(&raw_map.entries)
entries.len = 0
for _, i in raw_map.hashes {
raw_map.hashes[i] = MAP_SENTINEL
}
} }
@builtin @builtin
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) { reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
if m != nil { if m != nil {
h := __get_map_header_table(T) __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc)
__dynamic_map_reserve(m, h, uint(capacity), loc)
} }
} }
/* /*
Shrinks the capacity of a map down to the current length, or the given capacity. Shrinks the capacity of a map down to the current length.
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.
*/ */
@builtin @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 { if m != nil {
new_cap := new_cap if new_cap >= 0 else len(m) err := map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
return __dynamic_map_shrink(__get_map_header(m), new_cap, loc) did_shrink = err == nil
} }
return 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) { delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) {
if m != nil { if m != nil {
key := key key := key
h := __get_map_header(m) old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key))
fr := __map_find(h, &key) if ok {
if fr.entry_index != MAP_SENTINEL { deleted_key = (^K)(old_k)^
entry := __dynamic_map_get_entry(h, fr.entry_index) deleted_value = (^V)(old_v)^
deleted_key = (^K)(uintptr(entry)+h.key_offset)^
deleted_value = (^V)(uintptr(entry)+h.value_offset)^
__dynamic_map_erase(h, fr)
} }
} }
return return
@@ -573,10 +544,7 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
new_size := capacity * size_of(E) new_size := capacity * size_of(E)
allocator := a.allocator allocator := a.allocator
new_data, err := allocator.procedure( new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
allocator.data, .Resize, new_size, align_of(E),
a.data, old_size, loc,
)
if new_data == nil || err != nil { if new_data == nil || err != nil {
return false return false
} }
@@ -607,10 +575,7 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
new_size := length * size_of(E) new_size := length * size_of(E)
allocator := a.allocator allocator := a.allocator
new_data, err := allocator.procedure( new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
allocator.data, .Resize, new_size, align_of(E),
a.data, old_size, loc,
)
if new_data == nil || err != nil { if new_data == nil || err != nil {
return false 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) old_size := a.cap * size_of(E)
new_size := new_cap * size_of(E) new_size := new_cap * size_of(E)
new_data, err := a.allocator.procedure( new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
a.allocator.data,
.Resize,
new_size,
align_of(E),
a.data,
old_size,
loc,
)
if err != nil { if err != nil {
return return
} }
@@ -672,10 +629,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
@builtin @builtin
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) { map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
key, value := key, value key, value := key, value
h := __get_map_header_table(T) return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
e := __dynamic_map_set(m, h, __get_map_key_hash(&key), &key, &value, loc)
return (^V)(uintptr(e) + h.value_offset)
} }
@@ -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 @builtin
@(disabled=ODIN_DISABLE_ASSERT) @(disabled=ODIN_DISABLE_ASSERT)
assert :: proc(condition: bool, message := "", loc := #caller_location) { 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, size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) { old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
switch mode { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
return nil, .Out_Of_Memory return nil, .Out_Of_Memory
case .Free: case .Free:
return nil, .None return nil, .None
+3 -3
View File
@@ -10,8 +10,8 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
size, alignment: int, size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) { old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
switch mode { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
data, err = _windows_default_alloc(size, alignment) data, err = _windows_default_alloc(size, alignment, mode == .Alloc)
case .Free: case .Free:
_windows_default_free(old_memory) _windows_default_free(old_memory)
@@ -25,7 +25,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
case .Query_Features: case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory) set := (^Allocator_Mode_Set)(old_memory)
if set != nil { if set != nil {
set^ = {.Alloc, .Free, .Resize, .Query_Features} set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
} }
case .Query_Info: case .Query_Info:
@@ -167,7 +167,7 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
} }
switch mode { switch mode {
case .Alloc: case .Alloc, .Alloc_Non_Zeroed:
data, err = default_temp_allocator_alloc(s, size, alignment, loc) data, err = default_temp_allocator_alloc(s, size, alignment, loc)
case .Free: case .Free:
err = default_temp_allocator_free(s, old_memory, loc) 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: case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory) set := (^Allocator_Mode_Set)(old_memory)
if set != nil { 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: case .Query_Info:
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) 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 { mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
if ptr == nil || allocator.procedure == nil { if ptr == nil || allocator.procedure == nil {
return 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 return
} }
heap_alloc :: proc "contextless" (size: int) -> rawptr { heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
HEAP_ZERO_MEMORY :: 0x00000008 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 { heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
if new_size == 0 { 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 { if size == 0 {
_windows_default_free(old_ptr) _windows_default_free(old_ptr)
return nil, nil 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)^ original_old_ptr := intrinsics.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))
} else { } 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))) 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 return byte_slice(aligned_mem, size), nil
} }
_windows_default_alloc :: proc "contextless" (size, alignment: int) -> ([]byte, Allocator_Error) { _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) {
return _windows_default_alloc_or_resize(size, alignment, nil) 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 { foreign Foundation {
objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class --- objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
sel_registerName :: proc "c" (name: cstring) -> objc_SEL --- 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 :: 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 --- 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 _ :: intrinsics
_ :: runtime _ :: runtime
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) { 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) keys = make(type_of(keys), len(m), allocator) or_return
i := 0 i := 0
for key in m { for key in m {
keys[i] = key keys[i] = key
@@ -15,8 +15,8 @@ map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K)
} }
return return
} }
map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values: []V) { 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) values = make(type_of(values), len(m), allocator) or_return
i := 0 i := 0
for _, value in m { for _, value in m {
values[i] = value 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)) { map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V), err: runtime.Allocator) {
entries = make(type_of(entries), len(m), allocator) entries = make(type_of(entries), len(m), allocator) or_return
i := 0 i := 0
for key, value in m { for key, value in m {
entries[i].key = key entries[i].key = key
@@ -52,28 +52,24 @@ map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (ent
m := m m := m
rm := (^runtime.Raw_Map)(&m) rm := (^runtime.Raw_Map)(&m)
info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map) info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct) if info.map_info != nil {
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array) entries = make(type_of(entries), len(m), allocator) or_return
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)
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) entry_index += 1
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 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 { _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 { less :: #force_inline proc(a, b: E, call: P) -> bool {
when KIND == .Ordered { 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") #panic("unhandled Sort_Kind")
} }
} }
// insertion sort
// TODO(bill): use a different algorithm as insertion sort is O(n^2)
n := len(data) n := len(data)
for i in 1..<n { for i in 1..<n {
for j := i; j > 0 && less(data[j], data[j-1], call); j -= 1 { 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, 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 { decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
digit_zero :: proc(buf: []byte) -> int { digit_zero :: proc(buf: []byte) -> int {
for _, i in buf { 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
}
+265 -104
View File
@@ -1,6 +1,7 @@
package strconv package strconv
import "core:unicode/utf8" import "core:unicode/utf8"
import "decimal"
parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) { parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) {
switch s { 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} 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); // assert(n == 12.34 && ok);
// ``` // ```
parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) { parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
s := str common_prefix_len_ignore_case :: proc "contextless" (s, prefix: string) -> int {
defer if n != nil { n^ = len(str) - len(s) } n := len(prefix)
if s == "" { 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 return
} }
i := 0 parse_hex :: proc(s: string, mantissa: u64, exp: int, neg, trunc: bool) -> (f64, bool) {
info := &_f64_info
sign: f64 = 1 mantissa, exp := mantissa, exp
switch s[i] {
case '-': i += 1; sign = -1
case '+': i += 1
}
for ; i < len(s); i += 1 { MAX_EXP := 1<<info.expbits + info.bias - 2
r := rune(s[i]) MIN_EXP := info.bias + 1
if r == '_' { exp += int(info.mantbits)
continue
for mantissa != 0 && mantissa >> (info.mantbits+2) == 0 {
mantissa <<= 1
exp -= 1
}
if trunc {
mantissa |= 1
} }
v := _digit_value(r) for mantissa >> (info.mantbits+2) == 0 {
if v >= 10 { mantissa = mantissa>>1 | mantissa&1
if r == '.' || r == 'e' || r == 'E' { // Skip parsing NaN and Inf if it's probably a regular float exp += 1
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
} }
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 { nr: int
r := rune(s[i]) defer if n != nil { n^ = nr }
if r == '_' {
continue
}
v := _digit_value(r) if value, nr, ok = check_special(str); ok {
if v >= 10 { return
break }
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 if f > 1e15 || f < 1e-15 {
pow10 *= 10 break trunc_block
}
return f * pow10[exp], true
case -22 <= exp && exp < 0:
return f / pow10[-exp], true
} }
} }
frac := false d: decimal.Decimal
scale: f64 = 1 decimal.set(&d, str[:nr])
b, overflow := decimal_to_float_bits(&d, &_f64_info)
if i < len(s) && (s[i] == 'e' || s[i] == 'E') { value = transmute(f64)b
i += 1 ok = !overflow
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
return return
} }
+44
View File
@@ -299,6 +299,50 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
return 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 // 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) { write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
buf: [32]byte buf: [32]byte
+1 -1
View File
@@ -13,7 +13,7 @@
CPU feature flags can be tested against `cpu_features`, where applicable, e.g. CPU feature flags can be tested against `cpu_features`, where applicable, e.g.
`if .aes in si.aes { ... }` `if .aes in si.aes { ... }`
*/ */
// +ignore //+build ignore
package sysinfo package sysinfo
import "core:fmt" 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}}}, "21F2092" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
"21G72" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}}, "21G72" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
"21G83" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}}, "21G83" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
"21G115" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}},
} }
@(private) @(private)
+30
View File
@@ -128,4 +128,34 @@ foreign advapi32 {
lpData: LPCVOID, lpData: LPCVOID,
cbData: DWORD, cbData: DWORD,
) -> LSTATUS --- ) -> 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 ---
}
+1
View File
@@ -77,6 +77,7 @@ foreign gdi32 {
) -> HFONT --- ) -> HFONT ---
TextOutW :: proc(hdc: HDC, x, y: c_int, lpString: LPCWSTR, c: c_int) -> BOOL --- 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 --- GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: c_int, psizl: LPSIZE) -> BOOL ---
GetTextMetricsW :: proc(hdc: HDC, lptm: LPTEXTMETRICW) -> BOOL ---
} }
RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF { RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF {
+36 -1
View File
@@ -120,6 +120,12 @@ foreign kernel32 {
bManualReset: BOOL, bManualReset: BOOL,
lpTimerName: LPCWSTR, lpTimerName: LPCWSTR,
) -> HANDLE --- ) -> HANDLE ---
CreateWaitableTimerExW :: proc(
lpTimerAttributes: LPSECURITY_ATTRIBUTES,
lpTimerName: LPCWSTR,
dwFlags: DWORD,
dwDesiredAccess: DWORD,
) -> HANDLE ---
SetWaitableTimerEx :: proc( SetWaitableTimerEx :: proc(
hTimer: HANDLE, hTimer: HANDLE,
lpDueTime: ^LARGE_INTEGER, lpDueTime: ^LARGE_INTEGER,
@@ -248,6 +254,17 @@ foreign kernel32 {
GetModuleHandleW :: proc(lpModuleName: LPCWSTR) -> HMODULE --- GetModuleHandleW :: proc(lpModuleName: LPCWSTR) -> HMODULE ---
GetModuleHandleA :: proc(lpModuleName: LPCSTR) -> HMODULE --- GetModuleHandleA :: proc(lpModuleName: LPCSTR) -> HMODULE ---
GetSystemTimeAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) --- 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( CreateEventW :: proc(
lpEventAttributes: LPSECURITY_ATTRIBUTES, lpEventAttributes: LPSECURITY_ATTRIBUTES,
bManualReset: BOOL, bManualReset: BOOL,
@@ -346,6 +363,13 @@ foreign kernel32 {
GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL --- GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL ---
FreeConsole :: proc() -> BOOL --- FreeConsole :: proc() -> BOOL ---
GetConsoleWindow :: proc() -> HWND --- GetConsoleWindow :: proc() -> HWND ---
GetDiskFreeSpaceExW :: proc(
lpDirectoryName: LPCWSTR,
lpFreeBytesAvailableToCaller: PULARGE_INTEGER,
lpTotalNumberOfBytes: PULARGE_INTEGER,
lpTotalNumberOfFreeBytes: PULARGE_INTEGER,
) -> BOOL ---
} }
@@ -963,4 +987,15 @@ DCB :: struct {
foreign kernel32 { foreign kernel32 {
GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL --- GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
SetCommState :: 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) ---
}
+8
View File
@@ -14,4 +14,12 @@ foreign shell32 {
lpDirectory: LPCWSTR, lpDirectory: LPCWSTR,
nShowCmd: INT, nShowCmd: INT,
) -> HINSTANCE --- ) -> 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 ---
} }
+1
View File
@@ -8,4 +8,5 @@ foreign shlwapi {
PathFileExistsW :: proc(pszPath: wstring) -> BOOL --- PathFileExistsW :: proc(pszPath: wstring) -> BOOL ---
PathFindExtensionW :: proc(pszPath: wstring) -> wstring --- PathFindExtensionW :: proc(pszPath: wstring) -> wstring ---
PathFindFileNameW :: proc(pszPath: wstring) -> wstring --- PathFindFileNameW :: proc(pszPath: wstring) -> wstring ---
SHAutoComplete :: proc(hwndEdit: HWND, dwFlags: DWORD) -> LWSTDAPI ---
} }
+373 -4
View File
@@ -20,6 +20,7 @@ DWORD :: c_ulong
DWORDLONG :: c.ulonglong DWORDLONG :: c.ulonglong
QWORD :: c.ulonglong QWORD :: c.ulonglong
HANDLE :: distinct LPVOID HANDLE :: distinct LPVOID
PHANDLE :: ^HANDLE
HINSTANCE :: HANDLE HINSTANCE :: HANDLE
HMODULE :: distinct HINSTANCE HMODULE :: distinct HINSTANCE
HRESULT :: distinct LONG HRESULT :: distinct LONG
@@ -43,6 +44,7 @@ BOOLEAN :: distinct b8
GROUP :: distinct c_uint GROUP :: distinct c_uint
LARGE_INTEGER :: distinct c_longlong LARGE_INTEGER :: distinct c_longlong
ULARGE_INTEGER :: distinct c_ulonglong ULARGE_INTEGER :: distinct c_ulonglong
PULARGE_INTEGER :: ^ULARGE_INTEGER
LONG :: c_long LONG :: c_long
UINT :: c_uint UINT :: c_uint
INT :: c_int INT :: c_int
@@ -133,12 +135,24 @@ LPWSAOVERLAPPED :: distinct rawptr
LPWSAOVERLAPPED_COMPLETION_ROUTINE :: distinct rawptr LPWSAOVERLAPPED_COMPLETION_ROUTINE :: distinct rawptr
LPCVOID :: rawptr LPCVOID :: rawptr
PACCESS_TOKEN :: PVOID
PSECURITY_DESCRIPTOR :: PVOID
PSID :: PVOID
PCLAIMS_BLOB :: PVOID
PCONDITION_VARIABLE :: ^CONDITION_VARIABLE PCONDITION_VARIABLE :: ^CONDITION_VARIABLE
PLARGE_INTEGER :: ^LARGE_INTEGER PLARGE_INTEGER :: ^LARGE_INTEGER
PSRWLOCK :: ^SRWLOCK PSRWLOCK :: ^SRWLOCK
MMRESULT :: UINT 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 SOCKET :: distinct uintptr // TODO
socklen_t :: c_int socklen_t :: c_int
ADDRESS_FAMILY :: USHORT ADDRESS_FAMILY :: USHORT
@@ -175,6 +189,7 @@ FILE_SHARE_DELETE: DWORD : 0x00000004
FILE_GENERIC_ALL: DWORD : 0x10000000 FILE_GENERIC_ALL: DWORD : 0x10000000
FILE_GENERIC_EXECUTE: DWORD : 0x20000000 FILE_GENERIC_EXECUTE: DWORD : 0x20000000
FILE_GENERIC_READ: DWORD : 0x80000000 FILE_GENERIC_READ: DWORD : 0x80000000
FILE_ALL_ACCESS :: STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF
FILE_ACTION_ADDED :: 0x00000001 FILE_ACTION_ADDED :: 0x00000001
FILE_ACTION_REMOVED :: 0x00000002 FILE_ACTION_REMOVED :: 0x00000002
@@ -230,6 +245,20 @@ SECURITY_SQOS_PRESENT: DWORD : 0x00100000
FIONBIO: c_ulong : 0x8004667e 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 GET_FILEEX_INFO_LEVELS :: distinct i32
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0 GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
@@ -773,6 +802,61 @@ MSG :: struct {
LPMSG :: ^MSG 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 { PAINTSTRUCT :: struct {
hdc: HDC, hdc: HDC,
fErase: BOOL, fErase: BOOL,
@@ -879,6 +963,48 @@ NM_FONTCHANGED :: NM_OUTOFMEMORY-22
NM_CUSTOMTEXT :: NM_OUTOFMEMORY-23 // uses NMCUSTOMTEXT struct NM_CUSTOMTEXT :: NM_OUTOFMEMORY-23 // uses NMCUSTOMTEXT struct
NM_TVSTATEIMAGECHANGING :: NM_OUTOFMEMORY-23 // uses NMTVSTATEIMAGECHANGING struct, defined after HTREEITEM 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 { DEVMODEW :: struct {
dmDeviceName: [32]wchar_t, dmDeviceName: [32]wchar_t,
dmSpecVersion: WORD, dmSpecVersion: WORD,
@@ -1066,8 +1192,14 @@ WS_EX_TOPMOST : UINT : 0x0000_0008
WS_EX_TRANSPARENT : UINT : 0x0000_0020 WS_EX_TRANSPARENT : UINT : 0x0000_0020
WS_EX_WINDOWEDGE : UINT : 0x0000_0100 WS_EX_WINDOWEDGE : UINT : 0x0000_0100
PBS_SMOOTH :: 0x01 PBS_SMOOTH :: 0x01
PBS_VERTICAL :: 0x04 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_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 QS_ALLINPUT : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE
@@ -1127,6 +1259,10 @@ SWP_NOREPOSITION :: SWP_NOOWNERZORDER
SWP_DEFERERASE :: 0x2000 // same as SWP_DEFERDRAWING SWP_DEFERERASE :: 0x2000 // same as SWP_DEFERDRAWING
SWP_ASYNCWINDOWPOS :: 0x4000 // same as SWP_CREATESPB 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_TOP :: HWND( uintptr(0)) // 0
HWND_BOTTOM :: HWND( uintptr(1)) // 1 HWND_BOTTOM :: HWND( uintptr(1)) // 1
HWND_TOPMOST :: HWND(~uintptr(0)) // -1 HWND_TOPMOST :: HWND(~uintptr(0)) // -1
@@ -1325,6 +1461,58 @@ WMSZ_BOTTOM :: 6
WMSZ_BOTTOMLEFT :: 7 WMSZ_BOTTOMLEFT :: 7
WMSZ_BOTTOMRIGHT :: 8 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 // Key State Masks for Mouse Messages
MK_LBUTTON :: 0x0001 MK_LBUTTON :: 0x0001
MK_RBUTTON :: 0x0002 MK_RBUTTON :: 0x0002
@@ -1462,6 +1650,24 @@ IDI_WARNING := IDI_EXCLAMATION
IDI_ERROR := IDI_HAND IDI_ERROR := IDI_HAND
IDI_INFORMATION := IDI_ASTERISK 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 color table identifiers
DIB_RGB_COLORS :: 0 DIB_RGB_COLORS :: 0
@@ -1774,12 +1980,15 @@ WAIT_FAILED: DWORD : 0xFFFFFFFF
PIPE_ACCESS_INBOUND: DWORD : 0x00000001 PIPE_ACCESS_INBOUND: DWORD : 0x00000001
PIPE_ACCESS_OUTBOUND: DWORD : 0x00000002 PIPE_ACCESS_OUTBOUND: DWORD : 0x00000002
PIPE_ACCESS_DUPLEX: DWORD : 0x00000003
FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000 FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000
FILE_FLAG_OVERLAPPED: DWORD : 0x40000000 FILE_FLAG_OVERLAPPED: DWORD : 0x40000000
PIPE_WAIT: DWORD : 0x00000000 PIPE_WAIT: DWORD : 0x00000000
PIPE_TYPE_BYTE: DWORD : 0x00000000 PIPE_TYPE_BYTE: DWORD : 0x00000000
PIPE_TYPE_MESSAGE: DWORD : 0x00000004
PIPE_REJECT_REMOTE_CLIENTS: DWORD : 0x00000008 PIPE_REJECT_REMOTE_CLIENTS: DWORD : 0x00000008
PIPE_READMODE_BYTE: DWORD : 0x00000000 PIPE_READMODE_BYTE: DWORD : 0x00000000
PIPE_READMODE_MESSAGE: DWORD : 0x00000002
PIPE_ACCEPT_REMOTE_CLIENTS: DWORD : 0x00000000 PIPE_ACCEPT_REMOTE_CLIENTS: DWORD : 0x00000000
FD_SETSIZE :: 64 FD_SETSIZE :: 64
@@ -1793,7 +2002,58 @@ HEAP_ZERO_MEMORY: DWORD : 0x00000008
HANDLE_FLAG_INHERIT: DWORD : 0x00000001 HANDLE_FLAG_INHERIT: DWORD : 0x00000001
HANDLE_FLAG_PROTECT_FROM_CLOSE :: 0x00000002 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_ACP :: 0 // default to ANSI code page
CP_OEMCP :: 1 // default to OEM code page CP_OEMCP :: 1 // default to OEM code page
@@ -2101,9 +2361,10 @@ FILETIME :: struct {
FILETIME_as_unix_nanoseconds :: proc "contextless" (ft: FILETIME) -> i64 { FILETIME_as_unix_nanoseconds :: proc "contextless" (ft: FILETIME) -> i64 {
t := i64(u64(ft.dwLowDateTime) | u64(ft.dwHighDateTime) << 32) t := i64(u64(ft.dwLowDateTime) | u64(ft.dwHighDateTime) << 32)
return (t - 0x019db1ded53e8000) * 100 return (t - 116444736000000000) * 100
} }
OVERLAPPED :: struct { OVERLAPPED :: struct {
Internal: ^c_ulong, Internal: ^c_ulong,
InternalHigh: ^c_ulong, InternalHigh: ^c_ulong,
@@ -2777,6 +3038,16 @@ SYSTEMTIME :: struct {
milliseconds: WORD, milliseconds: WORD,
} }
TIME_ZONE_INFORMATION :: struct {
Bias: LONG,
StandardName: [32]WCHAR,
StandardDate: SYSTEMTIME,
StandardBias: LONG,
DaylightName: [32]WCHAR,
DaylightDate: SYSTEMTIME,
DaylightBias: LONG,
}
@(private="file") @(private="file")
IMAGE_DOS_HEADER :: struct { IMAGE_DOS_HEADER :: struct {
@@ -3048,12 +3319,32 @@ SHCONTF_FLATLIST :: 0x4000
SHCONTF_ENABLE_ASYNC :: 0x8000 SHCONTF_ENABLE_ASYNC :: 0x8000
SHCONTF_INCLUDESUPERHIDDEN :: 0x10000 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_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_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_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_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_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 { IModalWindow :: struct #raw_union {
#subtype IUnknown: IUnknown, #subtype IUnknown: IUnknown,
@@ -3358,6 +3649,84 @@ IFileSaveDialogVtbl :: struct {
ApplyProperties: proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem, pStore: ^IPropertyStore, hwnd: HWND, pSink: ^IFileOperationProgressSink) -> HRESULT, 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 { MEMORYSTATUSEX :: struct {
dwLength: DWORD, dwLength: DWORD,
dwMemoryLoad: DWORD, dwMemoryLoad: DWORD,
+1
View File
@@ -78,6 +78,7 @@ foreign user32 {
LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON --- LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR --- LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR ---
LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> 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 --- GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL --- GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
+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) { wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
context.allocator = allocator context.allocator = allocator
if N <= 0 { if N == 0 {
return 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 { if n == 0 {
return 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 // and will scan it to find the first null terminated character. The resulting string will
// also be null terminated. // 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. // will not be null terminated.
text := make([]byte, n) or_return text := make([]byte, n) or_return
+12
View File
@@ -85,3 +85,15 @@ foreign Opengl32 {
wglUseFontBitmaps :: proc(hdc: HDC, first, count, list_base: DWORD) -> BOOL --- 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 --- 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 TBM_CLEARTICS :: 0x0409
TTM_SETTOOLINFOA :: 0x0409 TTM_SETTOOLINFOA :: 0x0409
CBEM_HASEDITCHANGED :: 0x040a CBEM_HASEDITCHANGED :: 0x040a
PBM_SETMARQUEE :: 0x040a
RB_INSERTBANDW :: 0x040a RB_INSERTBANDW :: 0x040a
SB_GETRECT :: 0x040a SB_GETRECT :: 0x040a
TB_ISBUTTONCHECKED :: 0x040a TB_ISBUTTONCHECKED :: 0x040a
@@ -488,10 +489,12 @@ TTM_ENUMTOOLSA :: 0x040e
SB_SETICON :: 0x040f SB_SETICON :: 0x040f
TBM_GETTICPOS :: 0x040f TBM_GETTICPOS :: 0x040f
TTM_GETCURRENTTOOLA :: 0x040f TTM_GETCURRENTTOOLA :: 0x040f
PBM_SETSTATE :: 0x0410
RB_IDTOINDEX :: 0x0410 RB_IDTOINDEX :: 0x0410
SB_SETTIPTEXTA :: 0x0410 SB_SETTIPTEXTA :: 0x0410
TBM_GETNUMTICS :: 0x0410 TBM_GETNUMTICS :: 0x0410
TTM_WINDOWFROMPOINT :: 0x0410 TTM_WINDOWFROMPOINT :: 0x0410
PBM_GETSTATE :: 0x0411
RB_GETTOOLTIPS :: 0x0411 RB_GETTOOLTIPS :: 0x0411
SB_SETTIPTEXTW :: 0x0411 SB_SETTIPTEXTW :: 0x0411
TBM_GETSELSTART :: 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 package i18n
/* /*
+34 -32
View File
@@ -17,7 +17,7 @@ MAX_DURATION :: Duration(1<<63 - 1)
IS_SUPPORTED :: _IS_SUPPORTED IS_SUPPORTED :: _IS_SUPPORTED
Time :: struct { Time :: struct {
_nsec: i64, // zero is 1970-01-01 00:00:00 _nsec: i64, // Measured in UNIX nanonseconds
} }
Month :: enum int { Month :: enum int {
@@ -59,36 +59,36 @@ sleep :: proc "contextless" (d: Duration) {
_sleep(d) _sleep(d)
} }
stopwatch_start :: proc(using stopwatch: ^Stopwatch) { stopwatch_start :: proc "contextless" (using stopwatch: ^Stopwatch) {
if !running { if !running {
_start_time = tick_now() _start_time = tick_now()
running = true running = true
} }
} }
stopwatch_stop :: proc(using stopwatch: ^Stopwatch) { stopwatch_stop :: proc "contextless" (using stopwatch: ^Stopwatch) {
if running { if running {
_accumulation += tick_diff(_start_time, tick_now()) _accumulation += tick_diff(_start_time, tick_now())
running = false running = false
} }
} }
stopwatch_reset :: proc(using stopwatch: ^Stopwatch) { stopwatch_reset :: proc "contextless" (using stopwatch: ^Stopwatch) {
_accumulation = {} _accumulation = {}
running = false running = false
} }
stopwatch_duration :: proc(using stopwatch: Stopwatch) -> Duration { stopwatch_duration :: proc "contextless" (using stopwatch: Stopwatch) -> Duration {
if !running { return _accumulation } if !running { return _accumulation }
return _accumulation + tick_diff(_start_time, tick_now()) 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 d := end._nsec - start._nsec
return Duration(d) return Duration(d)
} }
since :: proc(start: Time) -> Duration { since :: proc "contextless" (start: Time) -> Duration {
return diff(start, now()) return diff(start, now())
} }
@@ -117,8 +117,8 @@ duration_hours :: proc "contextless" (d: Duration) -> f64 {
return f64(hour) + f64(nsec)/(60*60*1e9) return f64(hour) + f64(nsec)/(60*60*1e9)
} }
duration_round :: proc(d, m: Duration) -> Duration { duration_round :: proc "contextless" (d, m: Duration) -> Duration {
_less_than_half :: #force_inline proc(x, y: Duration) -> bool { _less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool {
return u64(x)+u64(x) < u64(y) return u64(x)+u64(x) < u64(y)
} }
@@ -146,45 +146,45 @@ duration_round :: proc(d, m: Duration) -> Duration {
return MAX_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 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) year, month, day, _ = _abs_date(_time_abs(t), true)
return return
} }
year :: proc(t: Time) -> (year: int) { year :: proc "contextless" (t: Time) -> (year: int) {
year, _, _, _ = _date(t, true) year, _, _, _ = _date(t, true)
return return
} }
month :: proc(t: Time) -> (month: Month) { month :: proc "contextless" (t: Time) -> (month: Month) {
_, month, _, _ = _date(t, true) _, month, _, _ = _date(t, true)
return return
} }
day :: proc(t: Time) -> (day: int) { day :: proc "contextless" (t: Time) -> (day: int) {
_, _, day, _ = _date(t, true) _, _, day, _ = _date(t, true)
return return
} }
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch } 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)) 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)) 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)) 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) sec = int(nsec % SECONDS_PER_DAY)
hour = sec / SECONDS_PER_HOUR hour = sec / SECONDS_PER_HOUR
sec -= hour * SECONDS_PER_HOUR sec -= hour * SECONDS_PER_HOUR
@@ -193,11 +193,11 @@ clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
return return
} }
read_cycle_counter :: proc() -> u64 { read_cycle_counter :: proc "contextless" () -> u64 {
return u64(intrinsics.read_cycle_counter()) 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 sec, nsec := sec, nsec
if nsec < 0 || nsec >= 1e9 { if nsec < 0 || nsec >= 1e9 {
n := nsec / 1e9 n := nsec / 1e9
@@ -208,20 +208,20 @@ unix :: proc(sec: i64, nsec: i64) -> Time {
sec -= 1 sec -= 1
} }
} }
return Time{(sec*1e9 + nsec) + UNIX_TO_INTERNAL} return Time{(sec*1e9 + nsec)}
} }
to_unix_seconds :: time_to_unix 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 return t._nsec/1e9
} }
to_unix_nanoseconds :: time_to_unix_nano 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 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)} 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. // 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 // 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. // 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, estimate, mean, m2, count: Duration
to_sleep = d to_sleep = d
@@ -279,19 +279,19 @@ ABSOLUTE_TO_UNIX :: -UNIX_TO_ABSOLUTE
@(private) @(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) year, month, day, yday = _abs_date(_time_abs(t), full)
return return
} }
@(private) @(private)
_time_abs :: proc(t: Time) -> u64 { _time_abs :: proc "contextless" (t: Time) -> u64 {
return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE) return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE)
} }
@(private) @(private)
_abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) { _abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
_is_leap_year :: proc(year: int) -> bool { _is_leap_year :: proc "contextless" (year: int) -> bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0) 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 return
} }
datetime_to_time :: proc(year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) { datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
divmod :: proc(year: int, divisor: int) -> (div: int, mod: int) { divmod :: proc "contextless" (year: int, divisor: int) -> (div: int, mod: int) {
assert(divisor > 0) if divisor <= 0 {
intrinsics.debug_trap()
}
div = int(year / divisor) div = int(year / divisor)
mod = year % divisor mod = year % divisor
return return
+2
View File
@@ -22,3 +22,5 @@ _tick_now :: proc "contextless" () -> Tick {
return {} return {}
} }
_yield :: proc "contextless" () {
}
+10 -3
View File
@@ -7,9 +7,16 @@ _IS_SUPPORTED :: true
_now :: proc "contextless" () -> Time { _now :: proc "contextless" () -> Time {
file_time: win32.FILETIME file_time: win32.FILETIME
win32.GetSystemTimeAsFileTime(&file_time)
ns := win32.FILETIME_as_unix_nanoseconds(file_time) ns: i64
return Time{_nsec=ns}
// monotonic
win32.GetSystemTimePreciseAsFileTime(&file_time)
dt := u64(transmute(u64le)file_time) // in 100ns units
ns = i64((dt - 116444736000000000) * 100) // convert to ns
return unix(0, ns)
} }
_sleep :: proc "contextless" (d: Duration) { _sleep :: proc "contextless" (d: Duration) {
+2
View File
@@ -10,6 +10,7 @@ import miniaudio "vendor:miniaudio"
import PM "vendor:portmidi" import PM "vendor:portmidi"
import rl "vendor:raylib" import rl "vendor:raylib"
import exr "vendor:OpenEXRCore" import exr "vendor:OpenEXRCore"
import cgltf "vendor:cgltf"
import SDL "vendor:sdl2" import SDL "vendor:sdl2"
import SDLNet "vendor:sdl2/net" import SDLNet "vendor:sdl2/net"
@@ -33,6 +34,7 @@ _ :: miniaudio
_ :: PM _ :: PM
_ :: rl _ :: rl
_ :: exr _ :: exr
_ :: cgltf
_ :: SDL _ :: SDL
_ :: SDLNet _ :: SDLNet
+1 -1
View File
@@ -71,7 +71,7 @@ void big_int_and (BigInt *dst, BigInt const *x, BigInt const *y);
void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y); void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y);
void big_int_xor (BigInt *dst, BigInt const *x, BigInt const *y); void big_int_xor (BigInt *dst, BigInt const *x, BigInt const *y);
void big_int_or (BigInt *dst, BigInt const *x, BigInt const *y); void big_int_or (BigInt *dst, BigInt const *x, BigInt const *y);
void big_int_not (BigInt *dst, BigInt const *x, u64 bit_count, bool is_signed); void big_int_not (BigInt *dst, BigInt const *x, i32 bit_count, bool is_signed);
void big_int_add_eq(BigInt *dst, BigInt const *x); void big_int_add_eq(BigInt *dst, BigInt const *x);
+14 -7
View File
@@ -298,17 +298,19 @@ struct BuildContext {
bool ignore_microsoft_magic; bool ignore_microsoft_magic;
bool linker_map_file; bool linker_map_file;
bool use_separate_modules; bool use_separate_modules;
bool threaded_checker; bool threaded_checker;
bool show_debug_messages; bool show_debug_messages;
bool copy_file_contents; bool copy_file_contents;
bool disallow_rtti; bool disallow_rtti;
bool use_static_map_calls;
RelocMode reloc_mode; RelocMode reloc_mode;
bool disable_red_zone; bool disable_red_zone;
u32 cmd_doc_flags; u32 cmd_doc_flags;
@@ -326,7 +328,7 @@ struct BuildContext {
BlockingMutex target_features_mutex; BlockingMutex target_features_mutex;
StringSet target_features_set; StringSet target_features_set;
String target_features_string; String target_features_string;
String minimum_os_version_string;
}; };
gb_global BuildContext build_context = {0}; gb_global BuildContext build_context = {0};
@@ -1380,6 +1382,11 @@ bool init_build_paths(String init_filename) {
produces_output_file = true; produces_output_file = true;
} }
if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) {
bc->no_dynamic_literals = true;
}
if (!produces_output_file) { if (!produces_output_file) {
// Command doesn't produce output files. We're done. // Command doesn't produce output files. We're done.
return true; return true;
+99 -2
View File
@@ -1533,10 +1533,10 @@ bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast
} }
bool is_defined = check_identifier_exists(c->scope, arg); bool is_defined = check_identifier_exists(c->scope, arg);
gb_unused(is_defined); // gb_unused(is_defined);
operand->type = t_untyped_bool; operand->type = t_untyped_bool;
operand->mode = Addressing_Constant; operand->mode = Addressing_Constant;
operand->value = exact_value_bool(false); operand->value = exact_value_bool(is_defined);
} else if (name == "config") { } else if (name == "config") {
if (ce->args.count != 2) { if (ce->args.count != 2) {
@@ -3651,6 +3651,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->mode = Addressing_NoValue; operand->mode = Addressing_NoValue;
break; break;
case BuiltinProc_raw_data:
{
Operand x = {};
check_expr(c, &x, ce->args[0]);
if (x.mode == Addressing_Invalid) {
return false;
}
if (!is_operand_value(x)) {
gbString s = expr_to_string(x.expr);
error(call, "'%.*s' expects a string, slice, dynamic array, or pointer to array type, got %s", LIT(builtin_name), s);
gb_string_free(s);
return false;
}
Type *t = base_type(x.type);
operand->mode = Addressing_Value;
operand->type = nullptr;
switch (t->kind) {
case Type_Slice:
operand->type = alloc_type_multi_pointer(t->MultiPointer.elem);
break;
case Type_DynamicArray:
operand->type = alloc_type_multi_pointer(t->DynamicArray.elem);
break;
case Type_Basic:
if (t->Basic.kind == Basic_string) {
operand->type = alloc_type_multi_pointer(t_u8);
}
break;
case Type_Pointer:
case Type_MultiPointer:
{
Type *base = base_type(type_deref(t, true));
switch (base->kind) {
case Type_Array:
case Type_EnumeratedArray:
case Type_SimdVector:
operand->type = alloc_type_multi_pointer(base_array_type(base));
break;
}
}
break;
}
if (operand->type == nullptr) {
gbString s = type_to_string(x.type);
error(call, "'%.*s' expects a string, slice, dynamic array, or pointer to array type, got %s", LIT(builtin_name), s);
gb_string_free(s);
return false;
}
}
break;
case BuiltinProc_read_cycle_counter: case BuiltinProc_read_cycle_counter:
operand->mode = Addressing_Value; operand->mode = Addressing_Value;
operand->type = t_i64; operand->type = t_i64;
@@ -4129,6 +4182,13 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false; return false;
} }
Type *elem = type_deref(ptr0.type);
if (type_size_of(elem) == 0) {
gbString str = type_to_string(ptr0.type);
error(ptr0.expr, "Expected a pointer to a non-zero sized element for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
return false;
}
} }
break; break;
@@ -5310,6 +5370,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break; break;
} }
case BuiltinProc_type_map_info:
{
Operand op = {};
Type *bt = check_type(c, ce->args[0]);
Type *type = base_type(bt);
if (type == nullptr || type == t_invalid) {
error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
if (!is_type_map(type)) {
gbString t = type_to_string(type);
error(ce->args[0], "Expected a map type for '%.*s', got %s", LIT(builtin_name), t);
gb_string_free(t);
return false;
}
add_map_key_type_dependencies(c, type);
operand->mode = Addressing_Value;
operand->type = t_map_info_ptr;
break;
}
case BuiltinProc_type_map_cell_info:
{
Operand op = {};
Type *bt = check_type(c, ce->args[0]);
Type *type = base_type(bt);
if (type == nullptr || type == t_invalid) {
error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
operand->mode = Addressing_Value;
operand->type = t_map_cell_info_ptr;
break;
}
case BuiltinProc_constant_utf16_cstring: case BuiltinProc_constant_utf16_cstring:
{ {
String value = {}; String value = {};
+5
View File
@@ -1488,6 +1488,11 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
if (!(e->flags & EntityFlag_Using)) { if (!(e->flags & EntityFlag_Using)) {
continue; continue;
} }
if (is_blank_ident(e->token)) {
error(e->token, "'using' a procedure parameter requires a non blank identifier");
break;
}
bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type); bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type);
String name = e->token.string; String name = e->token.string;
Type *t = base_type(type_deref(e->type)); Type *t = base_type(type_deref(e->type));
+152 -51
View File
@@ -285,6 +285,37 @@ void error_operand_no_value(Operand *o) {
} }
} }
void add_map_get_dependencies(CheckerContext *c) {
if (build_context.use_static_map_calls) {
add_package_dependency(c, "runtime", "map_desired_position");
add_package_dependency(c, "runtime", "map_probe_distance");
} else {
add_package_dependency(c, "runtime", "__dynamic_map_get");
}
}
void add_map_set_dependencies(CheckerContext *c) {
init_core_source_code_location(c->checker);
if (t_map_set_proc == nullptr) {
Type *map_set_args[5] = {/*map*/t_rawptr, /*hash*/t_uintptr, /*key*/t_rawptr, /*value*/t_rawptr, /*#caller_location*/t_source_code_location};
t_map_set_proc = alloc_type_proc_from_types(map_set_args, gb_count_of(map_set_args), t_rawptr, false, ProcCC_Odin);
}
if (build_context.use_static_map_calls) {
add_package_dependency(c, "runtime", "__dynamic_map_check_grow");
add_package_dependency(c, "runtime", "map_insert_hash_dynamic");
} else {
add_package_dependency(c, "runtime", "__dynamic_map_set");
}
}
void add_map_reserve_dependencies(CheckerContext *c) {
init_core_source_code_location(c->checker);
add_package_dependency(c, "runtime", "__dynamic_map_reserve");
}
void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, isize reserve_size) { void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, isize reserve_size) {
Scope *s = c->scope; Scope *s = c->scope;
@@ -580,6 +611,9 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
} }
return -1; return -1;
} }
if (operand->mode == Addressing_ProcGroup && !is_type_proc(type)) {
return -1;
}
Type *s = operand->type; Type *s = operand->type;
@@ -731,16 +765,13 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
} }
// TODO(bill): Determine which rule is a better on in practice // TODO(bill): Determine which rule is a better on in practice
#if 1 if (dst->Union.variants.count == 1) {
if (dst->Union.variants.count == 1) { Type *vt = dst->Union.variants[0];
Type *vt = dst->Union.variants[0]; i64 score = check_distance_between_types(c, operand, vt);
i64 score = check_distance_between_types(c, operand, vt); if (score >= 0) {
if (score >= 0) { return score+2;
return score+2;
}
} }
#else } else if (is_type_untyped(src)) {
// NOTE(bill): check to see you can assign to it with one of the variants?
i64 prev_lowest_score = -1; i64 prev_lowest_score = -1;
i64 lowest_score = -1; i64 lowest_score = -1;
for_array(i, dst->Union.variants) { for_array(i, dst->Union.variants) {
@@ -764,7 +795,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
return lowest_score+2; return lowest_score+2;
} }
} }
#endif }
} }
if (is_type_relative_pointer(dst)) { if (is_type_relative_pointer(dst)) {
@@ -821,11 +852,12 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
if (are_types_identical(src, dst)) { if (are_types_identical(src, dst)) {
return 5; return 5;
} }
if (dst->Matrix.row_count == dst->Matrix.column_count) {
Type *dst_elem = base_array_type(dst); Type *dst_elem = base_array_type(dst);
i64 distance = check_distance_between_types(c, operand, dst_elem); i64 distance = check_distance_between_types(c, operand, dst_elem);
if (distance >= 0) { if (distance >= 0) {
return distance + 7; return distance + 7;
}
} }
} }
@@ -971,22 +1003,24 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
} }
if (operand->mode == Addressing_ProcGroup) { if (operand->mode == Addressing_ProcGroup) {
Array<Entity *> procs = proc_group_entities(c, *operand);
bool good = false; bool good = false;
// NOTE(bill): These should be done if (type != nullptr && is_type_proc(type)) {
for_array(i, procs) { Array<Entity *> procs = proc_group_entities(c, *operand);
Type *t = base_type(procs[i]->type); // NOTE(bill): These should be done
if (t == t_invalid) { for_array(i, procs) {
continue; Type *t = base_type(procs[i]->type);
} if (t == t_invalid) {
Operand x = {}; continue;
x.mode = Addressing_Value; }
x.type = t; Operand x = {};
if (check_is_assignable_to(c, &x, type)) { x.mode = Addressing_Value;
Entity *e = procs[i]; x.type = t;
add_entity_use(c, operand->expr, e); if (check_is_assignable_to(c, &x, type)) {
good = true; Entity *e = procs[i];
break; add_entity_use(c, operand->expr, e);
good = true;
break;
}
} }
} }
@@ -1363,8 +1397,6 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
bool key = is_polymorphic_type_assignable(c, poly->Map.key, source->Map.key, true, modify_type); bool key = is_polymorphic_type_assignable(c, poly->Map.key, source->Map.key, true, modify_type);
bool value = is_polymorphic_type_assignable(c, poly->Map.value, source->Map.value, true, modify_type); bool value = is_polymorphic_type_assignable(c, poly->Map.value, source->Map.value, true, modify_type);
if (key || value) { if (key || value) {
poly->Map.entry_type = nullptr;
poly->Map.internal_type = nullptr;
poly->Map.lookup_result_type = nullptr; poly->Map.lookup_result_type = nullptr;
init_map_internal_types(poly); init_map_internal_types(poly);
return true; return true;
@@ -1477,7 +1509,7 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
Array<Entity *> procs = pge->entities; Array<Entity *> procs = pge->entities;
bool skip = false; bool skip = false;
if (type_hint != nullptr) { if (type_hint != nullptr && is_type_proc(type_hint)) {
// NOTE(bill): These should be done // NOTE(bill): These should be done
for_array(i, procs) { for_array(i, procs) {
Type *t = base_type(procs[i]->type); Type *t = base_type(procs[i]->type);
@@ -1997,7 +2029,7 @@ void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type
error_line("\tSuggestion: a string may be transmuted to %s\n", b); error_line("\tSuggestion: a string may be transmuted to %s\n", b);
error_line("\t This is an UNSAFE operation as string data is assumed to be immutable, \n"); error_line("\t This is an UNSAFE operation as string data is assumed to be immutable, \n");
error_line("\t whereas slices in general are assumed to be mutable.\n"); error_line("\t whereas slices in general are assumed to be mutable.\n");
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) { } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
error_line("\tSuggestion: the expression may be casted to %s\n", b); error_line("\tSuggestion: the expression may be casted to %s\n", b);
} }
} }
@@ -2038,7 +2070,7 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
} }
} else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) {
error_line("\tSuggestion: a string may be transmuted to %s\n", b); error_line("\tSuggestion: a string may be transmuted to %s\n", b);
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) { } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
error_line("\tSuggestion: the expression may be casted to %s\n", b); error_line("\tSuggestion: the expression may be casted to %s\n", b);
} }
} }
@@ -2916,7 +2948,12 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
// return false; // return false;
// } // }
if (is_type_untyped(o->type)) { Type *src_t = o->type;
Type *dst_t = t;
Type *src_bt = base_type(src_t);
Type *dst_bt = base_type(dst_t);
if (is_type_untyped(src_t)) {
gbString expr_str = expr_to_string(o->expr); gbString expr_str = expr_to_string(o->expr);
error(o->expr, "Cannot transmute untyped expression: '%s'", expr_str); error(o->expr, "Cannot transmute untyped expression: '%s'", expr_str);
gb_string_free(expr_str); gb_string_free(expr_str);
@@ -2925,7 +2962,6 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
return false; return false;
} }
Type *dst_bt = base_type(t);
if (dst_bt == nullptr || dst_bt == t_invalid) { if (dst_bt == nullptr || dst_bt == t_invalid) {
GB_ASSERT(global_error_collector.count != 0); GB_ASSERT(global_error_collector.count != 0);
@@ -2934,21 +2970,21 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
return false; return false;
} }
Type *src_bt = base_type(o->type);
if (src_bt == nullptr || src_bt == t_invalid) { if (src_bt == nullptr || src_bt == t_invalid) {
// NOTE(bill): this should be an error // NOTE(bill): this should be an error
GB_ASSERT(global_error_collector.count != 0); GB_ASSERT(global_error_collector.count != 0);
o->mode = Addressing_Value; o->mode = Addressing_Value;
o->expr = node; o->expr = node;
o->type = t; o->type = dst_t;
return true; return true;
} }
i64 srcz = type_size_of(o->type);
i64 dstz = type_size_of(t); i64 srcz = type_size_of(src_t);
i64 dstz = type_size_of(dst_t);
if (srcz != dstz) { if (srcz != dstz) {
gbString expr_str = expr_to_string(o->expr); gbString expr_str = expr_to_string(o->expr);
gbString type_str = type_to_string(t); gbString type_str = type_to_string(dst_t);
error(o->expr, "Cannot transmute '%s' to '%s', %lld vs %lld bytes", expr_str, type_str, srcz, dstz); error(o->expr, "Cannot transmute '%s' to '%s', %lld vs %lld bytes", expr_str, type_str, srcz, dstz);
gb_string_free(type_str); gb_string_free(type_str);
gb_string_free(expr_str); gb_string_free(expr_str);
@@ -2958,16 +2994,53 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
} }
if (build_context.vet_extra) { if (build_context.vet_extra) {
if (are_types_identical(o->type, t)) { if (are_types_identical(o->type, dst_t)) {
gbString str = type_to_string(t); gbString str = type_to_string(dst_t);
warning(o->expr, "Unneeded transmute to the same type '%s'", str); warning(o->expr, "Unneeded transmute to the same type '%s'", str);
gb_string_free(str); gb_string_free(str);
} }
} }
o->expr = node; o->expr = node;
o->type = dst_t;
if (o->mode == Addressing_Constant) {
if (are_types_identical(src_bt, dst_bt)) {
return true;
}
if (is_type_integer(src_t) && is_type_integer(dst_t)) {
if (types_have_same_internal_endian(src_t, dst_t)) {
ExactValue src_v = exact_value_to_integer(o->value);
GB_ASSERT(src_v.kind == ExactValue_Integer);
BigInt v = src_v.value_integer;
BigInt smax = {};
BigInt umax = {};
big_int_from_u64(&smax, 0);
big_int_not(&smax, &smax, cast(i32)(srcz*8 - 1), false);
big_int_from_u64(&umax, 1);
BigInt sz_in_bits = big_int_make_i64(srcz*8);
big_int_shl_eq(&umax, &sz_in_bits);
if (is_type_unsigned(src_t) && !is_type_unsigned(dst_t)) {
if (big_int_cmp(&v, &smax) >= 0) {
big_int_sub_eq(&v, &umax);
}
} else if (!is_type_unsigned(src_t) && is_type_unsigned(dst_t)) {
if (big_int_is_neg(&v)) {
big_int_add_eq(&v, &umax);
}
}
o->value.kind = ExactValue_Integer;
o->value.value_integer = v;
return true;
}
}
}
o->mode = Addressing_Value; o->mode = Addressing_Value;
o->type = t;
o->value = {}; o->value = {};
return true; return true;
} }
@@ -3204,7 +3277,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
check_assignment(c, x, yt->Map.key, str_lit("map 'not_in'")); check_assignment(c, x, yt->Map.key, str_lit("map 'not_in'"));
} }
add_package_dependency(c, "runtime", "__dynamic_map_get"); add_map_get_dependencies(c);
} else if (is_type_bit_set(rhs_type)) { } else if (is_type_bit_set(rhs_type)) {
Type *yt = base_type(rhs_type); Type *yt = base_type(rhs_type);
@@ -3296,6 +3369,24 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
error(y->expr, "built-in expression in binary expression"); error(y->expr, "built-in expression in binary expression");
return; return;
} }
if (x->mode == Addressing_ProcGroup) {
x->mode = Addressing_Invalid;
if (x->proc_group != nullptr) {
error(x->expr, "procedure group '%.*s' used in binary expression", LIT(x->proc_group->token.string));
} else {
error(x->expr, "procedure group used in binary expression");
}
return;
}
if (y->mode == Addressing_ProcGroup) {
x->mode = Addressing_Invalid;
if (x->proc_group != nullptr) {
error(y->expr, "procedure group '%.*s' used in binary expression", LIT(y->proc_group->token.string));
} else {
error(y->expr, "procedure group used in binary expression");
}
return;
}
if (token_is_shift(op.kind)) { if (token_is_shift(op.kind)) {
check_shift(c, x, y, node, type_hint); check_shift(c, x, y, node, type_hint);
@@ -5454,6 +5545,8 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
GB_ASSERT(is_type_proc(gept)); GB_ASSERT(is_type_proc(gept));
proc_type = gept; proc_type = gept;
pt = &gept->Proc; pt = &gept->Proc;
} else {
err = CallArgumentError_WrongTypes;
} }
} }
@@ -5788,6 +5881,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
Entity *proc = procs[proc_index]; Entity *proc = procs[proc_index];
Type *pt = base_type(proc->type); Type *pt = base_type(proc->type);
if (!(pt != nullptr && is_type_proc(pt))) { if (!(pt != nullptr && is_type_proc(pt))) {
proc_index++;
continue; continue;
} }
@@ -6085,6 +6179,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
} }
result_type = t_invalid; result_type = t_invalid;
} else { } else {
GB_ASSERT(valids.count == 1);
Ast *ident = operand->expr; Ast *ident = operand->expr;
while (ident->kind == Ast_SelectorExpr) { while (ident->kind == Ast_SelectorExpr) {
Ast *s = ident->SelectorExpr.selector; Ast *s = ident->SelectorExpr.selector;
@@ -6413,7 +6508,9 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
if (e->kind == Entity_TypeName) { if (e->kind == Entity_TypeName) {
if (o->mode != Addressing_Type) { if (o->mode != Addressing_Type) {
if (show_error) { if (show_error) {
error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string)); gbString expr = expr_to_string(o->expr);
error(o->expr, "Expected a type for the argument '%.*s', got %s", LIT(e->token.string), expr);
gb_string_free(expr);
} }
err = CallArgumentError_WrongTypes; err = CallArgumentError_WrongTypes;
} }
@@ -6456,6 +6553,10 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
// add_type_info_type(c, o->type); // add_type_info_type(c, o->type);
} }
if (show_error && err) {
return err;
}
{ {
bool failure = false; bool failure = false;
Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure); Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure);
@@ -8509,8 +8610,8 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
if (build_context.no_dynamic_literals && cl->elems.count) { if (build_context.no_dynamic_literals && cl->elems.count) {
error(node, "Compound literals of dynamic types have been disabled"); error(node, "Compound literals of dynamic types have been disabled");
} else { } else {
add_package_dependency(c, "runtime", "__dynamic_map_reserve"); add_map_reserve_dependencies(c);
add_package_dependency(c, "runtime", "__dynamic_map_set"); add_map_set_dependencies(c);
} }
break; break;
} }
@@ -8946,8 +9047,8 @@ ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_h
o->type = t->Map.value; o->type = t->Map.value;
o->expr = node; o->expr = node;
add_package_dependency(c, "runtime", "__dynamic_map_get"); add_map_get_dependencies(c);
add_package_dependency(c, "runtime", "__dynamic_map_set"); add_map_set_dependencies(c);
return Expr_Expr; return Expr_Expr;
} }
+5 -1
View File
@@ -584,7 +584,11 @@ void check_label(CheckerContext *ctx, Ast *label, Ast *parent) {
// Returns 'true' for 'continue', 'false' for 'return' // Returns 'true' for 'continue', 'false' for 'return'
bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, bool is_selector, Entity *e) { bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, bool is_selector, Entity *e) {
if (e == nullptr) { if (e == nullptr) {
error(us->token, "'using' applied to an unknown entity"); if (is_blank_ident(expr)) {
error(us->token, "'using' in a statement is not allowed with the blank identifier '_'");
} else {
error(us->token, "'using' applied to an unknown entity");
}
return true; return true;
} }
+32 -71
View File
@@ -1629,6 +1629,8 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
// This is just to add the error message to determine_type_from_polymorphic which // This is just to add the error message to determine_type_from_polymorphic which
// depends on valid position information // depends on valid position information
op.expr = _params; op.expr = _params;
op.mode = Addressing_Invalid;
op.type = t_invalid;
} }
if (is_type_polymorphic_type) { if (is_type_polymorphic_type) {
type = determine_type_from_polymorphic(ctx, type, op); type = determine_type_from_polymorphic(ctx, type, op);
@@ -2176,70 +2178,36 @@ Type *make_optional_ok_type(Type *value, bool typed) {
return t; return t;
} }
void init_map_entry_type(Type *type) {
GB_ASSERT(type->kind == Type_Map);
if (type->Map.entry_type != nullptr) return;
// NOTE(bill): The preload types may have not been set yet // IMPORTANT NOTE(bill): This must match the definition in dynamic_map_internal.odin
GB_ASSERT(t_map_hash != nullptr); enum : i64 {
MAP_CACHE_LINE_LOG2 = 6,
MAP_CACHE_LINE_SIZE = 1 << MAP_CACHE_LINE_LOG2
};
GB_STATIC_ASSERT(MAP_CACHE_LINE_SIZE >= 64);
void map_cell_size_and_len(Type *type, i64 *size_, i64 *len_) {
i64 elem_sz = type_size_of(type);
/* i64 len = 1;
struct { if (0 < elem_sz && elem_sz < MAP_CACHE_LINE_SIZE) {
hash: uintptr, len = MAP_CACHE_LINE_SIZE / elem_sz;
next: int,
key: Key,
value: Value,
} }
*/ i64 size = align_formula(elem_sz * len, MAP_CACHE_LINE_SIZE);
Scope *s = create_scope(nullptr, builtin_pkg->scope); if (size_) *size_ = size;
if (len_) *len_ = len;
auto fields = slice_make<Entity *>(permanent_allocator(), 4);
fields[0] = alloc_entity_field(s, make_token_ident(str_lit("hash")), t_uintptr, false, 0, EntityState_Resolved);
fields[1] = alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, 1, EntityState_Resolved);
fields[2] = alloc_entity_field(s, make_token_ident(str_lit("key")), type->Map.key, false, 2, EntityState_Resolved);
fields[3] = alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, 3, EntityState_Resolved);
Type *entry_type = alloc_type_struct();
entry_type->Struct.fields = fields;
entry_type->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count);
type_set_offsets(entry_type);
type->Map.entry_type = entry_type;
} }
void init_map_internal_types(Type *type) { void init_map_internal_types(Type *type) {
GB_ASSERT(type->kind == Type_Map); GB_ASSERT(type->kind == Type_Map);
init_map_entry_type(type); GB_ASSERT(t_allocator != nullptr);
if (type->Map.internal_type != nullptr) return; if (type->Map.lookup_result_type != nullptr) return;
Type *key = type->Map.key; Type *key = type->Map.key;
Type *value = type->Map.value; Type *value = type->Map.value;
GB_ASSERT(key != nullptr); GB_ASSERT(key != nullptr);
GB_ASSERT(value != nullptr); GB_ASSERT(value != nullptr);
Type *generated_struct_type = alloc_type_struct(); type->Map.lookup_result_type = make_optional_ok_type(value);
/*
struct {
hashes: []int;
entries: [dynamic]EntryType;
}
*/
Scope *s = create_scope(nullptr, builtin_pkg->scope);
Type *hashes_type = alloc_type_slice(t_int);
Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
auto fields = slice_make<Entity *>(permanent_allocator(), 2);
fields[0] = alloc_entity_field(s, make_token_ident(str_lit("hashes")), hashes_type, false, 0, EntityState_Resolved);
fields[1] = alloc_entity_field(s, make_token_ident(str_lit("entries")), entries_type, false, 1, EntityState_Resolved);
generated_struct_type->Struct.fields = fields;
type_set_offsets(generated_struct_type);
type->Map.internal_type = generated_struct_type;
type->Map.lookup_result_type = make_optional_ok_type(value);
} }
void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) { void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
@@ -2255,35 +2223,27 @@ void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
} }
if (is_type_simple_compare(key)) { if (is_type_simple_compare(key)) {
i64 sz = type_size_of(key); add_package_dependency(ctx, "runtime", "default_hasher");
if (1 <= sz && sz <= 16) { return;
char buf[20] = {};
gb_snprintf(buf, 20, "default_hasher%d", cast(i32)sz);
add_package_dependency(ctx, "runtime", buf);
return;
} else {
add_package_dependency(ctx, "runtime", "default_hasher_n");
return;
}
} }
if (key->kind == Type_Struct) { if (key->kind == Type_Struct) {
add_package_dependency(ctx, "runtime", "default_hasher_n"); add_package_dependency(ctx, "runtime", "default_hasher");
for_array(i, key->Struct.fields) { for_array(i, key->Struct.fields) {
Entity *field = key->Struct.fields[i]; Entity *field = key->Struct.fields[i];
add_map_key_type_dependencies(ctx, field->type); add_map_key_type_dependencies(ctx, field->type);
} }
} else if (key->kind == Type_Union) { } else if (key->kind == Type_Union) {
add_package_dependency(ctx, "runtime", "default_hasher_n"); add_package_dependency(ctx, "runtime", "default_hasher");
for_array(i, key->Union.variants) { for_array(i, key->Union.variants) {
Type *v = key->Union.variants[i]; Type *v = key->Union.variants[i];
add_map_key_type_dependencies(ctx, v); add_map_key_type_dependencies(ctx, v);
} }
} else if (key->kind == Type_EnumeratedArray) { } else if (key->kind == Type_EnumeratedArray) {
add_package_dependency(ctx, "runtime", "default_hasher_n"); add_package_dependency(ctx, "runtime", "default_hasher");
add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem); add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem);
} else if (key->kind == Type_Array) { } else if (key->kind == Type_Array) {
add_package_dependency(ctx, "runtime", "default_hasher_n"); add_package_dependency(ctx, "runtime", "default_hasher");
add_map_key_type_dependencies(ctx, key->Array.elem); add_map_key_type_dependencies(ctx, key->Array.elem);
} }
} }
@@ -2330,9 +2290,7 @@ void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
i64 row_count = check_array_count(ctx, &row, mt->row_count); i64 row_count = check_array_count(ctx, &row, mt->row_count);
i64 column_count = check_array_count(ctx, &column, mt->column_count); i64 column_count = check_array_count(ctx, &column, mt->column_count);
Type *elem = check_type_expr(ctx, mt->elem, nullptr);
Type *generic_row = nullptr; Type *generic_row = nullptr;
Type *generic_column = nullptr; Type *generic_column = nullptr;
@@ -2344,22 +2302,25 @@ void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
generic_column = column.type; generic_column = column.type;
} }
if (row_count < MATRIX_ELEMENT_COUNT_MIN && generic_row == nullptr) { if (generic_row == nullptr && row_count < MATRIX_ELEMENT_COUNT_MIN) {
gbString s = expr_to_string(row.expr); gbString s = expr_to_string(row.expr);
error(row.expr, "Invalid matrix row count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s); error(row.expr, "Invalid matrix row count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
gb_string_free(s); gb_string_free(s);
} }
if (column_count < MATRIX_ELEMENT_COUNT_MIN && generic_column == nullptr) { if (generic_column == nullptr && column_count < MATRIX_ELEMENT_COUNT_MIN) {
gbString s = expr_to_string(column.expr); gbString s = expr_to_string(column.expr);
error(column.expr, "Invalid matrix column count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s); error(column.expr, "Invalid matrix column count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
gb_string_free(s); gb_string_free(s);
} }
if (row_count*column_count > MATRIX_ELEMENT_COUNT_MAX) { if ((generic_row == nullptr && generic_column == nullptr) && row_count*column_count > MATRIX_ELEMENT_COUNT_MAX) {
i64 element_count = row_count*column_count; i64 element_count = row_count*column_count;
error(column.expr, "Matrix types are limited to a maximum of %d elements, got %lld", MATRIX_ELEMENT_COUNT_MAX, cast(long long)element_count); error(column.expr, "Matrix types are limited to a maximum of %d elements, got %lld", MATRIX_ELEMENT_COUNT_MAX, cast(long long)element_count);
} }
Type *elem = check_type_expr(ctx, mt->elem, nullptr);
if (!is_type_valid_for_matrix_elems(elem)) { if (!is_type_valid_for_matrix_elems(elem)) {
if (elem == t_typeid) { if (elem == t_typeid) {
+36 -12
View File
@@ -922,10 +922,13 @@ void init_universal(void) {
{ {
Type *equal_args[2] = {t_rawptr, t_rawptr}; Type *equal_args[2] = {t_rawptr, t_rawptr};
t_equal_proc = alloc_type_proc_from_types(equal_args, 2, t_bool, false, ProcCC_Contextless); t_equal_proc = alloc_type_proc_from_types(equal_args, gb_count_of(equal_args), t_bool, false, ProcCC_Contextless);
Type *hasher_args[2] = {t_rawptr, t_uintptr}; Type *hasher_args[2] = {t_rawptr, t_uintptr};
t_hasher_proc = alloc_type_proc_from_types(hasher_args, 2, t_uintptr, false, ProcCC_Contextless); t_hasher_proc = alloc_type_proc_from_types(hasher_args, gb_count_of(hasher_args), t_uintptr, false, ProcCC_Contextless);
Type *map_get_args[3] = {/*map*/t_rawptr, /*hash*/t_uintptr, /*key*/t_rawptr};
t_map_get_proc = alloc_type_proc_from_types(map_get_args, gb_count_of(map_get_args), t_rawptr, false, ProcCC_Contextless);
} }
// Constants // Constants
@@ -1673,7 +1676,18 @@ bool could_entity_be_lazy(Entity *e, DeclInfo *d) {
} }
void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported) { void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported) {
GB_ASSERT(identifier->kind == Ast_Ident); if (identifier == nullptr) {
// NOTE(bill): Should only happen on errors
error(e->token, "Invalid variable declaration");
return;
}
if (identifier->kind != Ast_Ident) {
// NOTE(bill): This is a safety check
gbString s = expr_to_string(identifier);
error(identifier, "A variable declaration must be an identifer, got %s", s);
gb_string_free(s);
return;
}
GB_ASSERT(e != nullptr && d != nullptr); GB_ASSERT(e != nullptr && d != nullptr);
GB_ASSERT(identifier->Ident.token.string == e->token.string); GB_ASSERT(identifier->Ident.token.string == e->token.string);
@@ -1922,7 +1936,8 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
init_map_internal_types(bt); init_map_internal_types(bt);
add_type_info_type_internal(c, bt->Map.key); add_type_info_type_internal(c, bt->Map.key);
add_type_info_type_internal(c, bt->Map.value); add_type_info_type_internal(c, bt->Map.value);
add_type_info_type_internal(c, bt->Map.internal_type); add_type_info_type_internal(c, t_uintptr); // hash value
add_type_info_type_internal(c, t_allocator);
break; break;
case Type_Tuple: case Type_Tuple:
@@ -2144,7 +2159,8 @@ void add_min_dep_type_info(Checker *c, Type *t) {
init_map_internal_types(bt); init_map_internal_types(bt);
add_min_dep_type_info(c, bt->Map.key); add_min_dep_type_info(c, bt->Map.key);
add_min_dep_type_info(c, bt->Map.value); add_min_dep_type_info(c, bt->Map.value);
add_min_dep_type_info(c, bt->Map.internal_type); add_min_dep_type_info(c, t_uintptr); // hash value
add_min_dep_type_info(c, t_allocator);
break; break;
case Type_Tuple: case Type_Tuple:
@@ -2827,16 +2843,21 @@ void init_core_source_code_location(Checker *c) {
return; return;
} }
t_source_code_location = find_core_type(c, str_lit("Source_Code_Location")); t_source_code_location = find_core_type(c, str_lit("Source_Code_Location"));
t_source_code_location_ptr = alloc_type_pointer(t_allocator); t_source_code_location_ptr = alloc_type_pointer(t_source_code_location);
} }
void init_core_map_type(Checker *c) { void init_core_map_type(Checker *c) {
if (t_map_hash != nullptr) { if (t_map_info != nullptr) {
return; return;
} }
t_map_hash = find_core_type(c, str_lit("Map_Hash")); init_mem_allocator(c);
t_map_header = find_core_type(c, str_lit("Map_Header")); t_map_info = find_core_type(c, str_lit("Map_Info"));
t_map_header_table = find_core_type(c, str_lit("Map_Header_Table")); t_map_cell_info = find_core_type(c, str_lit("Map_Cell_Info"));
t_raw_map = find_core_type(c, str_lit("Raw_Map"));
t_map_info_ptr = alloc_type_pointer(t_map_info);
t_map_cell_info_ptr = alloc_type_pointer(t_map_cell_info);
t_raw_map_ptr = alloc_type_pointer(t_raw_map);
} }
void init_preload(Checker *c) { void init_preload(Checker *c) {
@@ -3227,9 +3248,8 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
} }
DECL_ATTRIBUTE_PROC(var_decl_attribute) { DECL_ATTRIBUTE_PROC(var_decl_attribute) {
ExactValue ev = check_decl_attribute_value(c, value);
if (name == ATTRIBUTE_USER_TAG_NAME) { if (name == ATTRIBUTE_USER_TAG_NAME) {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind != ExactValue_String) { if (ev.kind != ExactValue_String) {
error(elem, "Expected a string value for '%.*s'", LIT(name)); error(elem, "Expected a string value for '%.*s'", LIT(name));
} }
@@ -3241,6 +3261,7 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
ac->is_static = true; ac->is_static = true;
return true; return true;
} else if (name == "thread_local") { } else if (name == "thread_local") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ac->init_expr_list_count > 0) { if (ac->init_expr_list_count > 0) {
error(elem, "A thread local variable declaration cannot have initialization values"); error(elem, "A thread local variable declaration cannot have initialization values");
} else if (c->foreign_context.curr_library) { } else if (c->foreign_context.curr_library) {
@@ -3315,6 +3336,7 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
} }
return true; return true;
} else if (name == "link_name") { } else if (name == "link_name") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) { if (ev.kind == ExactValue_String) {
ac->link_name = ev.value_string; ac->link_name = ev.value_string;
if (!is_foreign_name_valid(ac->link_name)) { if (!is_foreign_name_valid(ac->link_name)) {
@@ -3325,6 +3347,7 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
} }
return true; return true;
} else if (name == "link_prefix") { } else if (name == "link_prefix") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) { if (ev.kind == ExactValue_String) {
ac->link_prefix = ev.value_string; ac->link_prefix = ev.value_string;
if (!is_foreign_name_valid(ac->link_prefix)) { if (!is_foreign_name_valid(ac->link_prefix)) {
@@ -3335,6 +3358,7 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
} }
return true; return true;
} else if (name == "link_section") { } else if (name == "link_section") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) { if (ev.kind == ExactValue_String) {
ac->link_section = ev.value_string; ac->link_section = ev.value_string;
if (!is_foreign_name_valid(ac->link_section)) { if (!is_foreign_name_valid(ac->link_section)) {
+10 -2
View File
@@ -42,6 +42,8 @@ enum BuiltinProcId {
BuiltinProc_unreachable, BuiltinProc_unreachable,
BuiltinProc_raw_data,
BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
// "Intrinsics" // "Intrinsics"
@@ -275,6 +277,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_equal_proc, BuiltinProc_type_equal_proc,
BuiltinProc_type_hasher_proc, BuiltinProc_type_hasher_proc,
BuiltinProc_type_map_info,
BuiltinProc_type_map_cell_info,
BuiltinProc__type_end, BuiltinProc__type_end,
@@ -338,6 +342,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("unreachable"), 0, false, Expr_Expr, BuiltinProcPkg_builtin, /*diverging*/true}, {STR_LIT("unreachable"), 0, false, Expr_Expr, BuiltinProcPkg_builtin, /*diverging*/true},
{STR_LIT("raw_data"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
{STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE {STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE
@@ -566,8 +572,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_map_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_map_cell_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+11 -4
View File
@@ -499,6 +499,13 @@ i64 exact_value_to_i64(ExactValue v) {
} }
return 0; return 0;
} }
u64 exact_value_to_u64(ExactValue v) {
v = exact_value_to_integer(v);
if (v.kind == ExactValue_Integer) {
return big_int_to_u64(&v.value_integer);
}
return 0;
}
f64 exact_value_to_f64(ExactValue v) { f64 exact_value_to_f64(ExactValue v) {
v = exact_value_to_float(v); v = exact_value_to_float(v);
if (v.kind == ExactValue_Float) { if (v.kind == ExactValue_Float) {
@@ -945,15 +952,15 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
case ExactValue_Typeid: case ExactValue_Typeid:
switch (op) { switch (op) {
case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid); case Token_CmpEq: return x.value_typeid == y.value_typeid;
case Token_NotEq: return !are_types_identical(x.value_typeid, y.value_typeid); case Token_NotEq: return x.value_typeid != y.value_typeid;
} }
break; break;
case ExactValue_Procedure: case ExactValue_Procedure:
switch (op) { switch (op) {
case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid); case Token_CmpEq: return x.value_typeid == y.value_typeid;
case Token_NotEq: return !are_types_identical(x.value_typeid, y.value_typeid); case Token_NotEq: return x.value_typeid != y.value_typeid;
} }
break; break;
} }
+44 -19
View File
@@ -537,6 +537,22 @@ namespace lbAbiAmd64SysV {
return false; return false;
} }
bool is_llvm_type_slice_like(LLVMTypeRef type) {
if (!lb_is_type_kind(type, LLVMStructTypeKind)) {
return false;
}
if (LLVMCountStructElementTypes(type) != 2) {
return false;
}
LLVMTypeRef fields[2] = {};
LLVMGetStructElementTypes(type, fields);
if (!lb_is_type_kind(fields[0], LLVMPointerTypeKind)) {
return false;
}
return lb_is_type_kind(fields[1], LLVMIntegerTypeKind) && lb_sizeof(fields[1]) == 8;
}
lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) { lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) {
if (is_register(type)) { if (is_register(type)) {
LLVMAttributeRef attribute = nullptr; LLVMAttributeRef attribute = nullptr;
@@ -550,16 +566,25 @@ namespace lbAbiAmd64SysV {
if (is_mem_cls(cls, attribute_kind)) { if (is_mem_cls(cls, attribute_kind)) {
LLVMAttributeRef attribute = nullptr; LLVMAttributeRef attribute = nullptr;
if (attribute_kind == Amd64TypeAttribute_ByVal) { if (attribute_kind == Amd64TypeAttribute_ByVal) {
if (!is_calling_convention_odin(calling_convention)) { // if (!is_calling_convention_odin(calling_convention)) {
return lb_arg_type_indirect_byval(c, type); return lb_arg_type_indirect_byval(c, type);
} // }
attribute = nullptr; // attribute = nullptr;
} else if (attribute_kind == Amd64TypeAttribute_StructRect) { } else if (attribute_kind == Amd64TypeAttribute_StructRect) {
attribute = lb_create_enum_attribute_with_type(c, "sret", type); attribute = lb_create_enum_attribute_with_type(c, "sret", type);
} }
return lb_arg_type_indirect(type, attribute); return lb_arg_type_indirect(type, attribute);
} else { } else {
return lb_arg_type_direct(type, llreg(c, cls), nullptr, nullptr); LLVMTypeRef reg_type = nullptr;
if (is_llvm_type_slice_like(type)) {
// NOTE(bill): This is to make the ABI look closer to what the
// original code is just for slices/strings whilst still adhering
// the ABI rules for SysV
reg_type = type;
} else {
reg_type = llreg(c, cls);
}
return lb_arg_type_direct(type, reg_type, nullptr, nullptr);
} }
} }
@@ -982,13 +1007,13 @@ namespace lbAbiArm64 {
} }
return false; return false;
} }
unsigned is_homogenous_aggregate_small_enough(LLVMTypeRef *base_type_, unsigned member_count_) { unsigned is_homogenous_aggregate_small_enough(LLVMTypeRef base_type, unsigned member_count) {
return (member_count_ <= 4); return (member_count <= 4);
} }
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef type, bool return_is_defined) { lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef type, bool return_is_defined) {
LLVMTypeRef homo_base_type = {}; LLVMTypeRef homo_base_type = nullptr;
unsigned homo_member_count = 0; unsigned homo_member_count = 0;
if (!return_is_defined) { if (!return_is_defined) {
@@ -996,16 +1021,16 @@ namespace lbAbiArm64 {
} else if (is_register(type)) { } else if (is_register(type)) {
return non_struct(c, type); return non_struct(c, type);
} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) { } else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
if(is_homogenous_aggregate_small_enough(&homo_base_type, homo_member_count)) { if (is_homogenous_aggregate_small_enough(homo_base_type, homo_member_count)) {
return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr); return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
} else { } else {
//TODO(Platin): do i need to create stuff that can handle the diffrent return type? //TODO(Platin): do i need to create stuff that can handle the diffrent return type?
// else this needs a fix in llvm_backend_proc as we would need to cast it to the correct array type // else this needs a fix in llvm_backend_proc as we would need to cast it to the correct array type
//LLVMTypeRef array_type = LLVMArrayType(homo_base_type, homo_member_count); //LLVMTypeRef array_type = LLVMArrayType(homo_base_type, homo_member_count);
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type); LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
return lb_arg_type_indirect(type, attr); return lb_arg_type_indirect(type, attr);
} }
} else { } else {
i64 size = lb_sizeof(type); i64 size = lb_sizeof(type);
if (size <= 16) { if (size <= 16) {
+434 -84
View File
@@ -140,7 +140,7 @@ lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx) {
} }
lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) { lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
type = base_type(type); type = base_type(type);
GB_ASSERT(is_type_comparable(type)); GB_ASSERT(is_type_comparable(type));
@@ -157,8 +157,8 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
static u32 proc_index = 0; static u32 proc_index = 0;
char buf[16] = {}; char buf[32] = {};
isize n = gb_snprintf(buf, 16, "__$equal%u", ++proc_index); isize n = gb_snprintf(buf, 32, "__$equal%u", ++proc_index);
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str); String proc_name = make_string_c(str);
@@ -166,6 +166,9 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
map_set(&m->equal_procs, type, p); map_set(&m->equal_procs, type, p);
lb_begin_procedure_body(p); lb_begin_procedure_body(p);
lb_add_attribute_to_proc(m, p->value, "readonly");
lb_add_attribute_to_proc(m, p->value, "nounwind");
LLVMValueRef x = LLVMGetParam(p->value, 0); LLVMValueRef x = LLVMGetParam(p->value, 0);
LLVMValueRef y = LLVMGetParam(p->value, 1); LLVMValueRef y = LLVMGetParam(p->value, 1);
x = LLVMBuildPointerCast(p->builder, x, ptr_type, ""); x = LLVMBuildPointerCast(p->builder, x, ptr_type, "");
@@ -173,6 +176,8 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
lbValue lhs = {x, pt}; lbValue lhs = {x, pt};
lbValue rhs = {y, pt}; lbValue rhs = {y, pt};
lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
lb_add_proc_attribute_at_index(p, 1+1, "nonnull");
lbBlock *block_same_ptr = lb_create_block(p, "same_ptr"); lbBlock *block_same_ptr = lb_create_block(p, "same_ptr");
lbBlock *block_diff_ptr = lb_create_block(p, "diff_ptr"); lbBlock *block_diff_ptr = lb_create_block(p, "diff_ptr");
@@ -277,28 +282,20 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) { lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) {
GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type)); GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type));
i64 sz = type_size_of(type);
if (1 <= sz && sz <= 16) {
char name[20] = {};
gb_snprintf(name, 20, "default_hasher%d", cast(i32)sz);
auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = data;
args[1] = seed;
return lb_emit_runtime_call(p, name, args);
}
auto args = array_make<lbValue>(permanent_allocator(), 3); auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = data; args[0] = data;
args[1] = seed; args[1] = seed;
args[2] = lb_const_int(p->module, t_int, type_size_of(type)); args[2] = lb_const_int(p->module, t_int, type_size_of(type));
return lb_emit_runtime_call(p, "default_hasher_n", args); return lb_emit_runtime_call(p, "default_hasher", args);
} }
lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) { void lb_add_callsite_force_inline(lbProcedure *p, lbValue ret_value) {
LLVMAddCallSiteAttribute(ret_value.value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(p->module->ctx, "alwaysinline"));
}
lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
type = core_type(type); type = core_type(type);
GB_ASSERT(is_type_valid_for_keys(type)); GB_ASSERT_MSG(is_type_valid_for_keys(type), "%s", type_to_string(type));
Type *pt = alloc_type_pointer(type); Type *pt = alloc_type_pointer(type);
@@ -310,8 +307,8 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
static u32 proc_index = 0; static u32 proc_index = 0;
char buf[16] = {}; char buf[32] = {};
isize n = gb_snprintf(buf, 16, "__$hasher%u", ++proc_index); isize n = gb_snprintf(buf, 32, "__$hasher%u", ++proc_index);
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1); char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str); String proc_name = make_string_c(str);
@@ -320,16 +317,20 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
lb_begin_procedure_body(p); lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p)); defer (lb_end_procedure_body(p));
lb_add_attribute_to_proc(m, p->value, "readonly");
lb_add_attribute_to_proc(m, p->value, "nounwind");
LLVMValueRef x = LLVMGetParam(p->value, 0); LLVMValueRef x = LLVMGetParam(p->value, 0);
LLVMValueRef y = LLVMGetParam(p->value, 1); LLVMValueRef y = LLVMGetParam(p->value, 1);
lbValue data = {x, t_rawptr}; lbValue data = {x, t_rawptr};
lbValue seed = {y, t_uintptr}; lbValue seed = {y, t_uintptr};
LLVMAttributeRef nonnull_attr = lb_create_enum_attribute(m->ctx, "nonnull"); lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
LLVMAddAttributeAtIndex(p->value, 1+0, nonnull_attr); lb_add_proc_attribute_at_index(p, 1+0, "readonly");
if (is_type_simple_compare(type)) { if (is_type_simple_compare(type)) {
lbValue res = lb_simple_compare_hash(p, type, data, seed); lbValue res = lb_simple_compare_hash(p, type, data, seed);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value); LLVMBuildRet(p->builder, res.value);
return {p->value, p->type}; return {p->value, p->type};
} }
@@ -343,7 +344,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
GB_ASSERT(type->Struct.offsets != nullptr); GB_ASSERT(type->Struct.offsets != nullptr);
i64 offset = type->Struct.offsets[i]; i64 offset = type->Struct.offsets[i];
Entity *field = type->Struct.fields[i]; Entity *field = type->Struct.fields[i];
lbValue field_hasher = lb_get_hasher_proc_for_type(m, field->type); lbValue field_hasher = lb_hasher_proc_for_type(m, field->type);
lbValue ptr = lb_emit_ptr_offset(p, data, lb_const_int(m, t_uintptr, offset)); lbValue ptr = lb_emit_ptr_offset(p, data, lb_const_int(m, t_uintptr, offset));
args[0] = ptr; args[0] = ptr;
@@ -356,11 +357,12 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
if (is_type_union_maybe_pointer(type)) { if (is_type_union_maybe_pointer(type)) {
Type *v = type->Union.variants[0]; Type *v = type->Union.variants[0];
lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v); lbValue variant_hasher = lb_hasher_proc_for_type(m, v);
args[0] = data; args[0] = data;
args[1] = seed; args[1] = seed;
lbValue res = lb_emit_call(p, variant_hasher, args); lbValue res = lb_emit_call(p, variant_hasher, args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value); LLVMBuildRet(p->builder, res.value);
} }
@@ -379,7 +381,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
Type *v = type->Union.variants[i]; Type *v = type->Union.variants[i];
lbValue case_tag = lb_const_union_tag(p->module, type, v); lbValue case_tag = lb_const_union_tag(p->module, type, v);
lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v); lbValue variant_hasher = lb_hasher_proc_for_type(m, v);
args[0] = data; args[0] = data;
args[1] = seed; args[1] = seed;
@@ -397,7 +399,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
lb_addr_store(p, pres, seed); lb_addr_store(p, pres, seed);
auto args = array_make<lbValue>(permanent_allocator(), 2); auto args = array_make<lbValue>(permanent_allocator(), 2);
lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->Array.elem); lbValue elem_hasher = lb_hasher_proc_for_type(m, type->Array.elem);
auto loop_data = lb_loop_start(p, cast(isize)type->Array.count, t_i32); auto loop_data = lb_loop_start(p, cast(isize)type->Array.count, t_i32);
@@ -418,7 +420,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
lb_addr_store(p, res, seed); lb_addr_store(p, res, seed);
auto args = array_make<lbValue>(permanent_allocator(), 2); auto args = array_make<lbValue>(permanent_allocator(), 2);
lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->EnumeratedArray.elem); lbValue elem_hasher = lb_hasher_proc_for_type(m, type->EnumeratedArray.elem);
auto loop_data = lb_loop_start(p, cast(isize)type->EnumeratedArray.count, t_i32); auto loop_data = lb_loop_start(p, cast(isize)type->EnumeratedArray.count, t_i32);
@@ -439,12 +441,14 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
args[0] = data; args[0] = data;
args[1] = seed; args[1] = seed;
lbValue res = lb_emit_runtime_call(p, "default_hasher_cstring", args); lbValue res = lb_emit_runtime_call(p, "default_hasher_cstring", args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value); LLVMBuildRet(p->builder, res.value);
} else if (is_type_string(type)) { } else if (is_type_string(type)) {
auto args = array_make<lbValue>(permanent_allocator(), 2); auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = data; args[0] = data;
args[1] = seed; args[1] = seed;
lbValue res = lb_emit_runtime_call(p, "default_hasher_string", args); lbValue res = lb_emit_runtime_call(p, "default_hasher_string", args);
lb_add_callsite_force_inline(p, res);
LLVMBuildRet(p->builder, res.value); LLVMBuildRet(p->builder, res.value);
} else { } else {
GB_PANIC("Unhandled type for hasher: %s", type_to_string(type)); GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
@@ -454,6 +458,279 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
} }
lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
GB_ASSERT(build_context.use_static_map_calls);
type = base_type(type);
GB_ASSERT(type->kind == Type_Map);
lbProcedure **found = map_get(&m->map_get_procs, type);
if (found) {
GB_ASSERT(*found != nullptr);
return {(*found)->value, (*found)->type};
}
static u32 proc_index = 0;
char buf[32] = {};
isize n = gb_snprintf(buf, 32, "__$map_get-%u", ++proc_index);
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc);
map_set(&m->map_get_procs, type, p);
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
LLVMSetLinkage(p->value, LLVMInternalLinkage);
lb_add_attribute_to_proc(m, p->value, "readonly");
lb_add_attribute_to_proc(m, p->value, "nounwind");
if (build_context.ODIN_DEBUG) {
lb_add_attribute_to_proc(m, p->value, "noinline");
}
LLVMValueRef x = LLVMGetParam(p->value, 0);
LLVMValueRef y = LLVMGetParam(p->value, 1);
LLVMValueRef z = LLVMGetParam(p->value, 2);
lbValue map_ptr = {x, t_rawptr};
lbValue h = {y, t_uintptr};
lbValue key_ptr = {z, t_rawptr};
lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
lb_add_proc_attribute_at_index(p, 1+0, "noalias");
lb_add_proc_attribute_at_index(p, 1+0, "readonly");
lb_add_proc_attribute_at_index(p, 1+2, "nonnull");
lb_add_proc_attribute_at_index(p, 1+2, "noalias");
lb_add_proc_attribute_at_index(p, 1+2, "readonly");
lbBlock *loop_block = lb_create_block(p, "loop");
lbBlock *hash_block = lb_create_block(p, "hash");
lbBlock *probe_block = lb_create_block(p, "probe");
lbBlock *increment_block = lb_create_block(p, "increment");
lbBlock *hash_compare_block = lb_create_block(p, "hash_compare");
lbBlock *key_compare_block = lb_create_block(p, "key_compare");
lbBlock *value_block = lb_create_block(p, "value");
lbBlock *nil_block = lb_create_block(p, "nil");
map_ptr = lb_emit_conv(p, map_ptr, t_raw_map_ptr);
lbValue map = lb_emit_load(p, map_ptr);
lbValue length = lb_map_len(p, map);
lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, length, lb_const_nil(m, t_int)), nil_block, hash_block);
lb_start_block(p, hash_block);
key_ptr = lb_emit_conv(p, key_ptr, alloc_type_pointer(type->Map.key));
lbValue key = lb_emit_load(p, key_ptr);
lbAddr pos = lb_add_local_generated(p, t_uintptr, false);
lbAddr distance = lb_add_local_generated(p, t_uintptr, true);
lbValue capacity = lb_map_cap(p, map);
lbValue mask = lb_emit_conv(p, lb_emit_arith(p, Token_Sub, capacity, lb_const_int(m, t_int, 1), t_int), t_uintptr);
{
auto args = array_make<lbValue>(heap_allocator(), 2);
args[0] = map;
args[1] = h;
lb_addr_store(p, pos, lb_emit_runtime_call(p, "map_desired_position", args));
}
lbValue zero_uintptr = lb_const_int(m, t_uintptr, 0);
lbValue one_uintptr = lb_const_int(m, t_uintptr, 1);
lbValue ks = lb_map_data_uintptr(p, map);
lbValue vs = lb_map_cell_index_static(p, type->Map.key, ks, capacity);
lbValue hs = lb_map_cell_index_static(p, type->Map.value, vs, capacity);
ks = lb_emit_conv(p, ks, alloc_type_pointer(type->Map.key));
vs = lb_emit_conv(p, vs, alloc_type_pointer(type->Map.value));
hs = lb_emit_conv(p, hs, alloc_type_pointer(t_uintptr));
lb_emit_jump(p, loop_block);
lb_start_block(p, loop_block);
lbValue element_hash = lb_emit_load(p, lb_emit_ptr_offset(p, hs, lb_addr_load(p, pos)));
{
// if element_hash == 0 { return nil }
lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, element_hash, zero_uintptr), nil_block, probe_block);
}
lb_start_block(p, probe_block);
{
auto args = array_make<lbValue>(heap_allocator(), 3);
args[0] = map;
args[1] = element_hash;
args[2] = lb_addr_load(p, pos);
lbValue probe_distance = lb_emit_runtime_call(p, "map_probe_distance", args);
lbValue cond = lb_emit_comp(p, Token_Gt, lb_addr_load(p, distance), probe_distance);
lb_emit_if(p, cond, nil_block, hash_compare_block);
}
lb_start_block(p, hash_compare_block);
{
lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, element_hash, h), key_compare_block, increment_block);
}
lb_start_block(p, key_compare_block);
{
lbValue element_key = lb_map_cell_index_static(p, type->Map.key, ks, lb_addr_load(p, pos));
element_key = lb_emit_conv(p, element_key, ks.type);
lbValue cond = lb_emit_comp(p, Token_CmpEq, lb_emit_load(p, element_key), key);
lb_emit_if(p, cond, value_block, increment_block);
}
lb_start_block(p, value_block);
{
lbValue element_value = lb_map_cell_index_static(p, type->Map.value, vs, lb_addr_load(p, pos));
element_value = lb_emit_conv(p, element_value, t_rawptr);
LLVMBuildRet(p->builder, element_value.value);
}
lb_start_block(p, increment_block);
{
lbValue pp = lb_addr_load(p, pos);
pp = lb_emit_arith(p, Token_Add, pp, one_uintptr, t_uintptr);
pp = lb_emit_arith(p, Token_And, pp, mask, t_uintptr);
lb_addr_store(p, pos, pp);
lb_emit_increment(p, distance.addr);
}
lb_emit_jump(p, loop_block);
lb_start_block(p, nil_block);
{
lbValue res = lb_const_nil(m, t_rawptr);
LLVMBuildRet(p->builder, res.value);
}
return {p->value, p->type};
}
void lb_debug_print(lbProcedure *p, String const &str) {
auto args = array_make<lbValue>(heap_allocator(), 1);
args[0] = lb_const_string(p->module, str);
lb_emit_runtime_call(p, "print_string", args);
}
lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
GB_ASSERT(build_context.use_static_map_calls);
type = base_type(type);
GB_ASSERT(type->kind == Type_Map);
lbProcedure **found = map_get(&m->map_set_procs, type);
if (found) {
GB_ASSERT(*found != nullptr);
return {(*found)->value, (*found)->type};
}
static u32 proc_index = 0;
char buf[32] = {};
isize n = gb_snprintf(buf, 32, "__$map_set-%u", ++proc_index);
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_set_proc);
map_set(&m->map_set_procs, type, p);
lb_begin_procedure_body(p);
defer (lb_end_procedure_body(p));
LLVMSetLinkage(p->value, LLVMInternalLinkage);
lb_add_attribute_to_proc(m, p->value, "nounwind");
if (build_context.ODIN_DEBUG) {
lb_add_attribute_to_proc(m, p->value, "noinline");
}
lbValue map_ptr = {LLVMGetParam(p->value, 0), t_rawptr};
lbValue hash = {LLVMGetParam(p->value, 1), t_uintptr};
lbValue key_ptr = {LLVMGetParam(p->value, 2), t_rawptr};
lbValue value_ptr = {LLVMGetParam(p->value, 3), t_rawptr};
lbValue location_ptr = {LLVMGetParam(p->value, 4), t_source_code_location_ptr};
map_ptr = lb_emit_conv(p, map_ptr, alloc_type_pointer(type));
key_ptr = lb_emit_conv(p, key_ptr, alloc_type_pointer(type->Map.key));
lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
lb_add_proc_attribute_at_index(p, 1+0, "noalias");
lb_add_proc_attribute_at_index(p, 1+2, "nonnull");
if (!are_types_identical(type->Map.key, type->Map.value)) {
lb_add_proc_attribute_at_index(p, 1+2, "noalias");
}
lb_add_proc_attribute_at_index(p, 1+2, "readonly");
lb_add_proc_attribute_at_index(p, 1+3, "nonnull");
if (!are_types_identical(type->Map.key, type->Map.value)) {
lb_add_proc_attribute_at_index(p, 1+3, "noalias");
}
lb_add_proc_attribute_at_index(p, 1+3, "readonly");
lb_add_proc_attribute_at_index(p, 1+4, "nonnull");
lb_add_proc_attribute_at_index(p, 1+4, "noalias");
lb_add_proc_attribute_at_index(p, 1+4, "readonly");
////
lbValue found_ptr = {};
{
lbValue map_get_proc = lb_map_get_proc_for_type(m, type);
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = hash;
args[2] = key_ptr;
found_ptr = lb_emit_call(p, map_get_proc, args);
}
lbBlock *found_block = lb_create_block(p, "found");
lbBlock *check_grow_block = lb_create_block(p, "check-grow");
lbBlock *grow_fail_block = lb_create_block(p, "grow-fail");
lbBlock *insert_block = lb_create_block(p, "insert");
lb_emit_if(p, lb_emit_comp_against_nil(p, Token_NotEq, found_ptr), found_block, check_grow_block);
lb_start_block(p, found_block);
{
lb_mem_copy_non_overlapping(p, found_ptr, value_ptr, lb_const_int(m, t_int, type_size_of(type->Map.value)));
LLVMBuildRet(p->builder, lb_emit_conv(p, found_ptr, t_rawptr).value);
}
lb_start_block(p, check_grow_block);
lbValue map_info = lb_gen_map_info_ptr(p->module, type);
{
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = map_info;
args[2] = lb_emit_load(p, location_ptr);
lbValue grow_err = lb_emit_runtime_call(p, "__dynamic_map_check_grow", args);
lb_emit_if(p, lb_emit_comp_against_nil(p, Token_NotEq, grow_err), grow_fail_block, insert_block);
lb_start_block(p, grow_fail_block);
LLVMBuildRet(p->builder, LLVMConstNull(lb_type(m, t_rawptr)));
}
lb_start_block(p, insert_block);
{
auto args = array_make<lbValue>(permanent_allocator(), 5);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = map_info;
args[2] = hash;
args[3] = lb_emit_conv(p, key_ptr, t_uintptr);
args[4] = lb_emit_conv(p, value_ptr, t_uintptr);
lbValue result = lb_emit_runtime_call(p, "map_insert_hash_dynamic", args);
lb_emit_increment(p, lb_map_len_ptr(p, map_ptr));
LLVMBuildRet(p->builder, lb_emit_conv(p, result, t_rawptr).value);
}
return {p->value, p->type};
}
lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) { lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, expr); lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, expr);
if (found) { if (found) {
@@ -500,51 +777,60 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A
return value; return value;
} }
lbValue lb_gen_map_header_table_internal(lbProcedure *p, Type *map_type) {
lbModule *m = p->module;
lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type) {
lbAddr *found = map_get(&m->map_cell_info_map, type);
if (found) {
return found->addr;
}
i64 size = 0, len = 0;
map_cell_size_and_len(type, &size, &len);
LLVMValueRef const_values[4] = {};
const_values[0] = lb_const_int(m, t_uintptr, type_size_of(type)).value;
const_values[1] = lb_const_int(m, t_uintptr, type_align_of(type)).value;
const_values[2] = lb_const_int(m, t_uintptr, size).value;
const_values[3] = lb_const_int(m, t_uintptr, len).value;
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_cell_info, const_values, gb_count_of(const_values));
lbValue res = {llvm_res, t_map_cell_info};
lbAddr addr = lb_add_global_generated(m, t_map_cell_info, res, nullptr);
lb_make_global_private_const(addr);
map_set(&m->map_cell_info_map, type, addr);
return addr.addr;
}
lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type) {
map_type = base_type(map_type); map_type = base_type(map_type);
GB_ASSERT(map_type->kind == Type_Map); GB_ASSERT(map_type->kind == Type_Map);
lbAddr *found = map_get(&m->map_header_table_map, map_type); lbAddr *found = map_get(&m->map_info_map, map_type);
if (found) { if (found) {
return lb_addr_load(p, *found); return found->addr;
} }
GB_ASSERT(map_type->Map.entry_type->kind == Type_Struct); GB_ASSERT(t_map_info != nullptr);
i64 entry_size = type_size_of (map_type->Map.entry_type); GB_ASSERT(t_map_cell_info != nullptr);
i64 entry_align = type_align_of (map_type->Map.entry_type);
i64 key_offset = type_offset_of(map_type->Map.entry_type, 2); LLVMValueRef key_cell_info = lb_gen_map_cell_info_ptr(m, map_type->Map.key).value;
i64 key_size = type_size_of (map_type->Map.key); LLVMValueRef value_cell_info = lb_gen_map_cell_info_ptr(m, map_type->Map.value).value;
i64 value_offset = type_offset_of(map_type->Map.entry_type, 3); LLVMValueRef const_values[4] = {};
i64 value_size = type_size_of (map_type->Map.value); const_values[0] = key_cell_info;
const_values[1] = value_cell_info;
const_values[2] = lb_hasher_proc_for_type(m, map_type->Map.key).value;
const_values[3] = lb_equal_proc_for_type(m, map_type->Map.key).value;
Type *key_type = map_type->Map.key; LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_info, const_values, gb_count_of(const_values));
Type *val_type = map_type->Map.value; lbValue res = {llvm_res, t_map_info};
gb_unused(val_type);
Type *st = base_type(t_map_header_table); lbAddr addr = lb_add_global_generated(m, t_map_info, res, nullptr);
GB_ASSERT(st->Struct.fields.count == 7);
LLVMValueRef const_values[7] = {};
const_values[0] = lb_get_equal_proc_for_type(m, key_type) .value;
const_values[1] = lb_const_int(m, t_int, entry_size) .value;
const_values[2] = lb_const_int(m, t_int, entry_align) .value;
const_values[3] = lb_const_int(m, t_uintptr, key_offset) .value;
const_values[4] = lb_const_int(m, t_int, key_size) .value;
const_values[5] = lb_const_int(m, t_uintptr, value_offset).value;
const_values[6] = lb_const_int(m, t_int, value_size) .value;
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_header_table, const_values, gb_count_of(const_values));
lbValue res = {llvm_res, t_map_header_table};
lbAddr addr = lb_add_global_generated(m, t_map_header_table, res, nullptr);
lb_make_global_private_const(addr); lb_make_global_private_const(addr);
map_set(&m->map_header_table_map, map_type, addr); map_set(&m->map_info_map, map_type, addr);
return lb_addr_load(p, addr); return addr.addr;
} }
lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) { lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) {
@@ -602,7 +888,7 @@ lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue
lbValue hashed_key = lb_const_hash(p->module, key, key_type); lbValue hashed_key = lb_const_hash(p->module, key, key_type);
if (hashed_key.value == nullptr) { if (hashed_key.value == nullptr) {
lbValue hasher = lb_get_hasher_proc_for_type(p->module, key_type); lbValue hasher = lb_hasher_proc_for_type(p->module, key_type);
auto args = array_make<lbValue>(permanent_allocator(), 2); auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = key_ptr; args[0] = key_ptr;
@@ -615,42 +901,68 @@ lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue
lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key) { lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key) {
Type *map_type = base_type(type_deref(map_ptr.type)); Type *map_type = base_type(type_deref(map_ptr.type));
GB_ASSERT(map_type->kind == Type_Map);
lbValue ptr = {};
lbValue key_ptr = {}; lbValue key_ptr = {};
auto args = array_make<lbValue>(permanent_allocator(), 4); lbValue hash = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = lb_gen_map_header_table_internal(p, map_type);
args[2] = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr);
args[3] = key_ptr;
lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args); if (build_context.use_static_map_calls) {
lbValue map_get_proc = lb_map_get_proc_for_type(p->module, map_type);
auto args = array_make<lbValue>(permanent_allocator(), 3);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = hash;
args[2] = key_ptr;
ptr = lb_emit_call(p, map_get_proc, args);
} else {
auto args = array_make<lbValue>(permanent_allocator(), 4);
args[0] = lb_emit_transmute(p, map_ptr, t_raw_map_ptr);
args[1] = lb_gen_map_info_ptr(p->module, map_type);
args[2] = hash;
args[3] = key_ptr;
ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
}
return lb_emit_conv(p, ptr, alloc_type_pointer(map_type->Map.value)); return lb_emit_conv(p, ptr, alloc_type_pointer(map_type->Map.value));
} }
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type, void lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_ptr, Type *map_type,
lbValue const &map_key, lbValue const &map_value, Ast *node) { lbValue const &map_key, lbValue const &map_value, Ast *node) {
map_type = base_type(map_type); map_type = base_type(map_type);
GB_ASSERT(map_type->kind == Type_Map); GB_ASSERT(map_type->kind == Type_Map);
lbValue key_ptr = {}; lbValue key_ptr = {};
lbValue key_hash = lb_gen_map_key_hash(p, map_key, map_type->Map.key, &key_ptr); lbValue hash = lb_gen_map_key_hash(p, map_key, map_type->Map.key, &key_ptr);
lbValue v = lb_emit_conv(p, map_value, map_type->Map.value); lbValue v = lb_emit_conv(p, map_value, map_type->Map.value);
lbValue value_ptr = lb_address_from_load_or_generate_local(p, v);
lbAddr value_addr = lb_add_local_generated(p, v.type, false); if (build_context.use_static_map_calls) {
lb_addr_store(p, value_addr, v); lbValue map_set_proc = lb_map_set_proc_for_type(p->module, map_type);
auto args = array_make<lbValue>(permanent_allocator(), 6); auto args = array_make<lbValue>(permanent_allocator(), 5);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr); args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = lb_gen_map_header_table_internal(p, map_type); args[1] = hash;
args[2] = key_hash; args[2] = lb_emit_conv(p, key_ptr, t_rawptr);
args[3] = key_ptr; args[3] = lb_emit_conv(p, value_ptr, t_rawptr);
args[4] = lb_emit_conv(p, value_addr.addr, t_rawptr); args[4] = lb_emit_source_code_location_as_global(p, node);
args[5] = lb_emit_source_code_location(p, node);
lb_emit_runtime_call(p, "__dynamic_map_set", args); lb_emit_call(p, map_set_proc, args);
} else {
auto args = array_make<lbValue>(permanent_allocator(), 6);
args[0] = lb_emit_conv(p, map_ptr, t_raw_map_ptr);
args[1] = lb_gen_map_info_ptr(p->module, map_type);
args[2] = hash;
args[3] = lb_emit_conv(p, key_ptr, t_rawptr);
args[4] = lb_emit_conv(p, value_ptr, t_rawptr);
args[5] = lb_emit_source_code_location_as_global(p, node);
lb_emit_runtime_call(p, "__dynamic_map_set", args);
}
} }
void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) { lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) {
GB_ASSERT(!build_context.no_dynamic_literals); GB_ASSERT(!build_context.no_dynamic_literals);
String proc_name = {}; String proc_name = {};
@@ -660,10 +972,10 @@ void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const
auto args = array_make<lbValue>(permanent_allocator(), 4); auto args = array_make<lbValue>(permanent_allocator(), 4);
args[0] = lb_emit_conv(p, map_ptr, t_rawptr); args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = lb_gen_map_header_table_internal(p, type_deref(map_ptr.type)); args[1] = lb_gen_map_info_ptr(p->module, type_deref(map_ptr.type));
args[2] = lb_const_int(p->module, t_int, capacity); args[2] = lb_const_int(p->module, t_uint, capacity);
args[3] = lb_emit_source_code_location(p, proc_name, pos); args[3] = lb_emit_source_code_location_as_global(p, proc_name, pos);
lb_emit_runtime_call(p, "__dynamic_map_reserve", args); return lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
} }
@@ -688,6 +1000,8 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) {
p->is_startup = true; p->is_startup = true;
LLVMSetLinkage(p->value, LLVMInternalLinkage); LLVMSetLinkage(p->value, LLVMInternalLinkage);
lb_add_attribute_to_proc(m, p->value, "nounwind");
lb_begin_procedure_body(p); lb_begin_procedure_body(p);
lb_setup_type_info_data(p); lb_setup_type_info_data(p);
@@ -712,6 +1026,7 @@ lbProcedure *lb_create_objc_names(lbModule *main_module) {
} }
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl); Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type); lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type);
lb_add_attribute_to_proc(p->module, p->value, "nounwind");
p->is_startup = true; p->is_startup = true;
return p; return p;
} }
@@ -1198,6 +1513,14 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) {
lbProcedure *p = m->hasher_procs.entries[i].value; lbProcedure *p = m->hasher_procs.entries[i].value;
lb_run_function_pass_manager(default_function_pass_manager, p); lb_run_function_pass_manager(default_function_pass_manager, p);
} }
for_array(i, m->map_get_procs.entries) {
lbProcedure *p = m->map_get_procs.entries[i].value;
lb_run_function_pass_manager(default_function_pass_manager, p);
}
for_array(i, m->map_set_procs.entries) {
lbProcedure *p = m->map_set_procs.entries[i].value;
lb_run_function_pass_manager(default_function_pass_manager, p);
}
return 0; return 0;
} }
@@ -1736,6 +2059,13 @@ void lb_generate_code(lbGenerator *gen) {
lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, objc_names, global_variables); lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, objc_names, global_variables);
gb_unused(startup_runtime); gb_unused(startup_runtime);
if (build_context.ODIN_DEBUG) {
for_array(i, builtin_pkg->scope->elements.entries) {
Entity *e = builtin_pkg->scope->elements.entries[i].value;
add_debug_info_for_global_constant_from_entity(gen, e);
}
}
TIME_SECTION("LLVM Global Procedures and Types"); TIME_SECTION("LLVM Global Procedures and Types");
for_array(i, info->entities) { for_array(i, info->entities) {
Entity *e = info->entities[i]; Entity *e = info->entities[i];
@@ -1759,6 +2089,11 @@ void lb_generate_code(lbGenerator *gen) {
case Entity_TypeName: case Entity_TypeName:
case Entity_Procedure: case Entity_Procedure:
break; break;
case Entity_Constant:
if (build_context.ODIN_DEBUG) {
add_debug_info_for_global_constant_from_entity(gen, e);
}
break;
} }
bool polymorphic_struct = false; bool polymorphic_struct = false;
@@ -1820,6 +2155,21 @@ void lb_generate_code(lbGenerator *gen) {
lb_finalize_objc_names(objc_names); lb_finalize_objc_names(objc_names);
if (build_context.ODIN_DEBUG) { if (build_context.ODIN_DEBUG) {
TIME_SECTION("LLVM Debug Info for global constant value declarations");
{
// lbModule *m = default_module;
}
// if (gen->modules.entries.count == 1) {
// } else {
// for_array(j, gen->modules.entries) {
// lbModule *m = gen->modules.entries[j].value;
// if (m->debug_builder != nullptr) {
// }
// }
// }
TIME_SECTION("LLVM Debug Info Complete Types and Finalize"); TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
for_array(j, gen->modules.entries) { for_array(j, gen->modules.entries) {
lbModule *m = gen->modules.entries[j].value; lbModule *m = gen->modules.entries[j].value;
+12 -8
View File
@@ -144,6 +144,8 @@ struct lbModule {
PtrMap<Type *, lbProcedure *> equal_procs; PtrMap<Type *, lbProcedure *> equal_procs;
PtrMap<Type *, lbProcedure *> hasher_procs; PtrMap<Type *, lbProcedure *> hasher_procs;
PtrMap<Type *, lbProcedure *> map_get_procs;
PtrMap<Type *, lbProcedure *> map_set_procs;
u32 nested_type_name_guid; u32 nested_type_name_guid;
@@ -160,7 +162,8 @@ struct lbModule {
StringMap<lbAddr> objc_classes; StringMap<lbAddr> objc_classes;
StringMap<lbAddr> objc_selectors; StringMap<lbAddr> objc_selectors;
PtrMap<Type *, lbAddr> map_header_table_map; PtrMap<Type *, lbAddr> map_cell_info_map; // address of runtime.Map_Info
PtrMap<Type *, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
}; };
struct lbGenerator { struct lbGenerator {
@@ -298,6 +301,7 @@ struct lbProcedure {
lbBlock * entry_block; lbBlock * entry_block;
lbBlock * curr_block; lbBlock * curr_block;
lbTargetList * target_list; lbTargetList * target_list;
PtrMap<Entity *, lbValue> direct_parameters;
Ast *curr_stmt; Ast *curr_stmt;
@@ -421,8 +425,6 @@ lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da);
lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da); lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da);
lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da); lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da);
lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da); lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da);
lbValue lb_map_entries(lbProcedure *p, lbValue value);
lbValue lb_map_entries_ptr(lbProcedure *p, lbValue value);
lbValue lb_map_len(lbProcedure *p, lbValue value); lbValue lb_map_len(lbProcedure *p, lbValue value);
lbValue lb_map_cap(lbProcedure *p, lbValue value); lbValue lb_map_cap(lbProcedure *p, lbValue value);
lbValue lb_soa_struct_len(lbProcedure *p, lbValue value); lbValue lb_soa_struct_len(lbProcedure *p, lbValue value);
@@ -446,22 +448,24 @@ String lb_get_const_string(lbModule *m, lbValue value);
lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true); lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id); lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_); lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_);
lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type);
lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type);
lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key); lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key);
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node); void lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node);
void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos); lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos);
lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e); lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e);
lbValue lb_find_value_from_entity(lbModule *m, Entity *e); lbValue lb_find_value_from_entity(lbModule *m, Entity *e);
void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value); void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value); lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos); lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos);
lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos); lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type); lbValue lb_equal_proc_for_type(lbModule *m, Type *type);
lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type); lbValue lb_hasher_proc_for_type(lbModule *m, Type *type);
lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t); lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
LLVMMetadataRef lb_debug_type(lbModule *m, Type *type); LLVMMetadataRef lb_debug_type(lbModule *m, Type *type);
+29 -3
View File
@@ -256,7 +256,7 @@ lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t) {
return lb_const_value(m, t, tv.value); return lb_const_value(m, t, tv.value);
} }
lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos) { lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos) {
lbModule *m = p->module; lbModule *m = p->module;
LLVMValueRef fields[4] = {}; LLVMValueRef fields[4] = {};
@@ -271,7 +271,7 @@ lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, To
return res; return res;
} }
lbValue lb_emit_source_code_location(lbProcedure *p, Ast *node) { lbValue lb_emit_source_code_location_const(lbProcedure *p, Ast *node) {
String proc_name = {}; String proc_name = {};
if (p->entity) { if (p->entity) {
proc_name = p->entity->token.string; proc_name = p->entity->token.string;
@@ -280,9 +280,35 @@ lbValue lb_emit_source_code_location(lbProcedure *p, Ast *node) {
if (node) { if (node) {
pos = ast_token(node).pos; pos = ast_token(node).pos;
} }
return lb_emit_source_code_location(p, proc_name, pos); return lb_emit_source_code_location_const(p, proc_name, pos);
} }
lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos) {
lbValue loc = lb_emit_source_code_location_const(p, procedure, pos);
lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
lb_make_global_private_const(addr);
return addr.addr;
}
lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, Ast *node) {
lbValue loc = lb_emit_source_code_location_const(p, node);
lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
lb_make_global_private_const(addr);
return addr.addr;
}
lbValue lb_emit_source_code_location_as_global(lbProcedure *p, String const &procedure, TokenPos const &pos) {
return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, procedure, pos));
}
lbValue lb_emit_source_code_location_as_global(lbProcedure *p, Ast *node) {
return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, node));
}
LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local) { LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local) {
bool is_local = allow_local && m->curr_procedure != nullptr; bool is_local = allow_local && m->curr_procedure != nullptr;
bool is_const = true; bool is_const = true;
+167 -10
View File
@@ -293,6 +293,8 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
case Type_Named: case Type_Named:
GB_PANIC("Type_Named should be handled in lb_debug_type separately"); GB_PANIC("Type_Named should be handled in lb_debug_type separately");
case Type_SoaPointer:
return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->SoaPointer.elem), word_bits, word_bits, 0, nullptr, 0);
case Type_Pointer: case Type_Pointer:
return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->Pointer.elem), word_bits, word_bits, 0, nullptr, 0); return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->Pointer.elem), word_bits, word_bits, 0, nullptr, 0);
case Type_MultiPointer: case Type_MultiPointer:
@@ -567,14 +569,43 @@ LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
case Type_Struct: case Type_Struct:
case Type_Union: case Type_Union:
case Type_BitSet: case Type_BitSet:
LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType( {
m->debug_builder, tag, name_text, name_len, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0 LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
); m->debug_builder, tag, name_text, name_len, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0
idt.metadata = temp_forward_decl; );
idt.metadata = temp_forward_decl;
array_add(&m->debug_incomplete_types, idt); array_add(&m->debug_incomplete_types, idt);
lb_set_llvm_metadata(m, type, temp_forward_decl); lb_set_llvm_metadata(m, type, temp_forward_decl);
return temp_forward_decl;
LLVMMetadataRef dummy = nullptr;
switch (bt->kind) {
case Type_Slice:
dummy = lb_debug_type(m, bt->Slice.elem);
dummy = lb_debug_type(m, alloc_type_pointer(bt->Slice.elem));
dummy = lb_debug_type(m, t_int);
break;
case Type_DynamicArray:
dummy = lb_debug_type(m, bt->DynamicArray.elem);
dummy = lb_debug_type(m, alloc_type_pointer(bt->DynamicArray.elem));
dummy = lb_debug_type(m, t_int);
dummy = lb_debug_type(m, t_allocator);
break;
case Type_Map:
dummy = lb_debug_type(m, bt->Map.key);
dummy = lb_debug_type(m, bt->Map.value);
dummy = lb_debug_type(m, t_int);
dummy = lb_debug_type(m, t_allocator);
dummy = lb_debug_type(m, t_uintptr);
break;
case Type_BitSet:
if (bt->BitSet.elem) dummy = lb_debug_type(m, bt->BitSet.elem);
if (bt->BitSet.underlying) dummy = lb_debug_type(m, bt->BitSet.underlying);
break;
}
return temp_forward_decl;
}
} }
} }
@@ -657,20 +688,33 @@ void lb_debug_complete_types(lbModule *m) {
case Type_Slice: case Type_Slice:
element_count = 2; element_count = 2;
elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count); elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->Slice.elem), 0*word_bits); #if defined(GB_SYSTEM_WINDOWS)
elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->Slice.elem), 0*word_bits);
#else
// FIX HACK TODO(bill): For some reason this causes a crash in *nix systems due to the reference counting
// of the debug type information
elements[0] = lb_debug_struct_field(m, str_lit("data"), t_rawptr, 0*word_bits);
#endif
elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*word_bits); elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*word_bits);
break; break;
case Type_DynamicArray: case Type_DynamicArray:
element_count = 4; element_count = 4;
elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count); elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->DynamicArray.elem), 0*word_bits); #if defined(GB_SYSTEM_WINDOWS)
elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->DynamicArray.elem), 0*word_bits);
#else
// FIX HACK TODO(bill): For some reason this causes a crash in *nix systems due to the reference counting
// of the debug type information
elements[0] = lb_debug_struct_field(m, str_lit("data"), t_rawptr, 0*word_bits);
#endif
elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*word_bits); elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*word_bits);
elements[2] = lb_debug_struct_field(m, str_lit("cap"), t_int, 2*word_bits); elements[2] = lb_debug_struct_field(m, str_lit("cap"), t_int, 2*word_bits);
elements[3] = lb_debug_struct_field(m, str_lit("allocator"), t_allocator, 3*word_bits); elements[3] = lb_debug_struct_field(m, str_lit("allocator"), t_allocator, 3*word_bits);
break; break;
case Type_Map: case Type_Map:
bt = bt->Map.internal_type; GB_ASSERT(t_raw_map != nullptr);
bt = base_type(t_raw_map);
/*fallthrough*/ /*fallthrough*/
case Type_Struct: case Type_Struct:
if (file == nullptr) { if (file == nullptr) {
@@ -1079,3 +1123,116 @@ void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
lb_add_debug_local_variable(p, ptr, t_context, token); lb_add_debug_local_variable(p, ptr, t_context, token);
} }
String debug_info_mangle_constant_name(Entity *e, bool *did_allocate_) {
String name = e->token.string;
if (e->pkg && e->pkg->name.len > 0) {
// NOTE(bill): C++ NONSENSE FOR DEBUG SHITE!
name = concatenate3_strings(heap_allocator(), e->pkg->name, str_lit("::"), name);
if (did_allocate_) *did_allocate_ = true;
}
return name;
}
void add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMetadataRef dtype, LLVMMetadataRef expr) {
LLVMMetadataRef scope = nullptr;
LLVMMetadataRef file = nullptr;
unsigned line = 0;
LLVMMetadataRef decl = nullptr;
LLVMDIBuilderCreateGlobalVariableExpression(
m->debug_builder, scope,
cast(char const *)name.text, cast(size_t)name.len,
"", 0, // Linkage
file, line, dtype,
false, // local to unit
expr, decl, 8/*AlignInBits*/);
}
void add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLVMMetadataRef dtype, i64 v) {
LLVMMetadataRef expr = LLVMDIBuilderCreateConstantValueExpression(m->debug_builder, v);
bool did_allocate = false;
String name = debug_info_mangle_constant_name(e, &did_allocate);
defer (if (did_allocate) {
gb_free(heap_allocator(), name.text);
});
add_debug_info_global_variable_expr(m, name, dtype, expr);
if ((e->pkg && e->pkg->kind == Package_Init) ||
(e->scope && (e->scope->flags & ScopeFlag_Global))) {
add_debug_info_global_variable_expr(m, e->token.string, dtype, expr);
}
}
void add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e) {
if (e == nullptr || e->kind != Entity_Constant) {
return;
}
if (is_blank_ident(e->token)) {
return;
}
lbModule *m = &gen->default_module;
if (USE_SEPARATE_MODULES) {
m = lb_pkg_module(gen, e->pkg);
}
if (is_type_integer(e->type)) {
ExactValue const &value = e->Constant.value;
if (value.kind == ExactValue_Integer) {
LLVMMetadataRef dtype = nullptr;
i64 v = 0;
bool is_signed = false;
if (big_int_is_neg(&value.value_integer)) {
v = exact_value_to_i64(value);
is_signed = true;
} else {
v = cast(i64)exact_value_to_u64(value);
}
if (is_type_untyped(e->type)) {
dtype = lb_debug_type(m, is_signed ? t_i64 : t_u64);
} else {
dtype = lb_debug_type(m, e->type);
}
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_rune(e->type)) {
ExactValue const &value = e->Constant.value;
if (value.kind == ExactValue_Integer) {
LLVMMetadataRef dtype = lb_debug_type(m, t_rune);
i64 v = exact_value_to_i64(value);
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_boolean(e->type)) {
ExactValue const &value = e->Constant.value;
if (value.kind == ExactValue_Bool) {
LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
i64 v = cast(i64)value.value_bool;
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_enum(e->type)) {
ExactValue const &value = e->Constant.value;
if (value.kind == ExactValue_Integer) {
LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
i64 v = 0;
if (big_int_is_neg(&value.value_integer)) {
v = exact_value_to_i64(value);
} else {
v = cast(i64)exact_value_to_u64(value);
}
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
} else if (is_type_pointer(e->type)) {
ExactValue const &value = e->Constant.value;
if (value.kind == ExactValue_Integer) {
LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
i64 v = cast(i64)exact_value_to_u64(value);
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
}
}
}
+34 -39
View File
@@ -1952,34 +1952,33 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
Type *dt = t; Type *dt = t;
GB_ASSERT(is_type_struct(st) || is_type_raw_union(st)); GB_ASSERT(is_type_struct(st) || is_type_raw_union(st));
String field_name = lookup_subtype_polymorphic_field(t, src_type); Selection sel = {};
if (field_name.len > 0) { sel.index.allocator = heap_allocator();
// NOTE(bill): It can be casted defer (array_free(&sel.index));
Selection sel = lookup_field(st, field_name, false, true); if (lookup_subtype_polymorphic_selection(t, src_type, &sel)) {
if (sel.entity != nullptr) { if (sel.entity == nullptr) {
if (st_is_ptr) { GB_PANIC("invalid subtype cast %s -> ", type_to_string(src_type), type_to_string(t));
lbValue res = lb_emit_deep_field_gep(p, value, sel); }
Type *rt = res.type; if (st_is_ptr) {
if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) { lbValue res = lb_emit_deep_field_gep(p, value, sel);
res = lb_emit_load(p, res); Type *rt = res.type;
} if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
return res; res = lb_emit_load(p, res);
} else {
if (is_type_pointer(value.type)) {
Type *rt = value.type;
if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
value = lb_emit_load(p, value);
} else {
value = lb_emit_deep_field_gep(p, value, sel);
return lb_emit_load(p, value);
}
}
return lb_emit_deep_field_ev(p, value, sel);
} }
return res;
} else { } else {
GB_PANIC("invalid subtype cast %s.%.*s", type_to_string(src_type), LIT(field_name)); if (is_type_pointer(value.type)) {
Type *rt = value.type;
if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
value = lb_emit_load(p, value);
} else {
value = lb_emit_deep_field_gep(p, value, sel);
return lb_emit_load(p, value);
}
}
return lb_emit_deep_field_ev(p, value, sel);
} }
} }
} }
@@ -2216,7 +2215,7 @@ lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbVa
args[2] = lb_const_int(p->module, t_int, type_size_of(type)); args[2] = lb_const_int(p->module, t_int, type_size_of(type));
res = lb_emit_runtime_call(p, "memory_equal", args); res = lb_emit_runtime_call(p, "memory_equal", args);
} else { } else {
lbValue value = lb_get_equal_proc_for_type(p->module, type); lbValue value = lb_equal_proc_for_type(p->module, type);
auto args = array_make<lbValue>(permanent_allocator(), 2); auto args = array_make<lbValue>(permanent_allocator(), 2);
args[0] = lb_emit_conv(p, left_ptr, t_rawptr); args[0] = lb_emit_conv(p, left_ptr, t_rawptr);
args[1] = lb_emit_conv(p, right_ptr, t_rawptr); args[1] = lb_emit_conv(p, right_ptr, t_rawptr);
@@ -2725,18 +2724,13 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
case Type_Map: case Type_Map:
{ {
lbValue map_ptr = lb_address_from_load_or_generate_local(p, x); lbValue data_ptr = lb_emit_struct_ev(p, x, 0);
unsigned indices[2] = {0, 0};
lbValue hashes_data = lb_emit_struct_ep(p, map_ptr, 0);
lbValue hashes_data_ptr_ptr = lb_emit_struct_ep(p, hashes_data, 0);
LLVMValueRef hashes_data_ptr = LLVMBuildLoad2(p->builder, llvm_addr_type(p->module, hashes_data_ptr_ptr), hashes_data_ptr_ptr.value, "");
if (op_kind == Token_CmpEq) { if (op_kind == Token_CmpEq) {
res.value = LLVMBuildIsNull(p->builder, hashes_data_ptr, ""); res.value = LLVMBuildIsNull(p->builder, data_ptr.value, "");
return res; return res;
} else { } else {
res.value = LLVMBuildIsNotNull(p->builder, hashes_data_ptr, ""); res.value = LLVMBuildIsNotNull(p->builder, data_ptr.value, "");
return res; return res;
} }
} }
@@ -4132,7 +4126,8 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
} }
GB_ASSERT(!build_context.no_dynamic_literals); GB_ASSERT(!build_context.no_dynamic_literals);
lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos); lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
gb_unused(err);
for_array(field_index, cl->elems) { for_array(field_index, cl->elems) {
Ast *elem = cl->elems[field_index]; Ast *elem = cl->elems[field_index];
@@ -4140,7 +4135,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
lbValue key = lb_build_expr(p, fv->field); lbValue key = lb_build_expr(p, fv->field);
lbValue value = lb_build_expr(p, fv->value); lbValue value = lb_build_expr(p, fv->value);
lb_insert_dynamic_map_key_and_value(p, v.addr, type, key, value, elem); lb_internal_dynamic_map_set(p, v.addr, type, key, value, elem);
} }
break; break;
} }
@@ -4232,7 +4227,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
args[1] = size; args[1] = size;
args[2] = align; args[2] = align;
args[3] = lb_const_int(p->module, t_int, item_count); args[3] = lb_const_int(p->module, t_int, item_count);
args[4] = lb_emit_source_code_location(p, proc_name, pos); args[4] = lb_emit_source_code_location_as_global(p, proc_name, pos);
lb_emit_runtime_call(p, "__dynamic_array_reserve", args); lb_emit_runtime_call(p, "__dynamic_array_reserve", args);
} }
@@ -4253,7 +4248,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
args[2] = align; args[2] = align;
args[3] = lb_emit_conv(p, items, t_rawptr); args[3] = lb_emit_conv(p, items, t_rawptr);
args[4] = lb_const_int(p->module, t_int, item_count); args[4] = lb_const_int(p->module, t_int, item_count);
args[5] = lb_emit_source_code_location(p, proc_name, pos); args[5] = lb_emit_source_code_location_as_global(p, proc_name, pos);
lb_emit_runtime_call(p, "__dynamic_array_append", args); lb_emit_runtime_call(p, "__dynamic_array_append", args);
} }
break; break;
+47 -43
View File
@@ -67,6 +67,8 @@ void lb_init_module(lbModule *m, Checker *c) {
map_init(&m->function_type_map, a); map_init(&m->function_type_map, a);
map_init(&m->equal_procs, a); map_init(&m->equal_procs, a);
map_init(&m->hasher_procs, a); map_init(&m->hasher_procs, a);
map_init(&m->map_get_procs, a);
map_init(&m->map_set_procs, a);
array_init(&m->procedures_to_generate, a, 0, 1024); array_init(&m->procedures_to_generate, a, 0, 1024);
array_init(&m->missing_procedures_to_check, a, 0, 16); array_init(&m->missing_procedures_to_check, a, 0, 16);
map_init(&m->debug_values, a); map_init(&m->debug_values, a);
@@ -75,7 +77,8 @@ void lb_init_module(lbModule *m, Checker *c) {
string_map_init(&m->objc_classes, a); string_map_init(&m->objc_classes, a);
string_map_init(&m->objc_selectors, a); string_map_init(&m->objc_selectors, a);
map_init(&m->map_header_table_map, a, 0); map_init(&m->map_info_map, a, 0);
map_init(&m->map_cell_info_map, a, 0);
} }
@@ -383,16 +386,27 @@ Type *lb_addr_type(lbAddr const &addr) {
if (addr.addr.value == nullptr) { if (addr.addr.value == nullptr) {
return nullptr; return nullptr;
} }
if (addr.kind == lbAddr_Map) { switch (addr.kind) {
Type *t = base_type(addr.map.type); case lbAddr_Map:
GB_ASSERT(is_type_map(t)); {
return t->Map.value; Type *t = base_type(addr.map.type);
} GB_ASSERT(is_type_map(t));
if (addr.kind == lbAddr_Swizzle) { return t->Map.value;
}
case lbAddr_Swizzle:
return addr.swizzle.type; return addr.swizzle.type;
} case lbAddr_SwizzleLarge:
if (addr.kind == lbAddr_SwizzleLarge) {
return addr.swizzle_large.type; return addr.swizzle_large.type;
case lbAddr_Context:
if (addr.ctx.sel.index.count > 0) {
Type *t = t_context;
for_array(i, addr.ctx.sel.index) {
GB_ASSERT(is_type_struct(t));
t = base_type(t)->Struct.fields[addr.ctx.sel.index[i]]->type;
}
return t;
}
break;
} }
return type_deref(addr.addr.type); return type_deref(addr.addr.type);
} }
@@ -714,7 +728,7 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
return; return;
} else if (addr.kind == lbAddr_Map) { } else if (addr.kind == lbAddr_Map) {
lb_insert_dynamic_map_key_and_value(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt); lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
return; return;
} else if (addr.kind == lbAddr_Context) { } else if (addr.kind == lbAddr_Context) {
lbAddr old_addr = lb_find_or_generate_context_ptr(p); lbAddr old_addr = lb_find_or_generate_context_ptr(p);
@@ -1510,6 +1524,9 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
bool *params_by_ptr = gb_alloc_array(permanent_allocator(), bool, param_count); bool *params_by_ptr = gb_alloc_array(permanent_allocator(), bool, param_count);
if (type->Proc.result_count != 0) { if (type->Proc.result_count != 0) {
Type *single_ret = reduce_tuple_to_single_type(type->Proc.results); Type *single_ret = reduce_tuple_to_single_type(type->Proc.results);
if (is_type_proc(single_ret)) {
single_ret = t_rawptr;
}
ret = lb_type(m, single_ret); ret = lb_type(m, single_ret);
if (ret != nullptr) { if (ret != nullptr) {
if (is_type_boolean(single_ret) && if (is_type_boolean(single_ret) &&
@@ -1920,38 +1937,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_Map: case Type_Map:
init_map_internal_types(type); init_map_internal_types(type);
{ GB_ASSERT(t_raw_map != nullptr);
Type *internal_type = type->Map.internal_type; return lb_type_internal(m, t_raw_map);
GB_ASSERT(internal_type->kind == Type_Struct);
m->internal_type_level -= 1;
defer (m->internal_type_level += 1);
unsigned field_count = cast(unsigned)(internal_type->Struct.fields.count);
GB_ASSERT(field_count == 2);
LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count);
LLVMTypeRef entries_fields[] = {
lb_type(m, t_rawptr), // data
lb_type(m, t_int), // len
lb_type(m, t_int), // cap
lb_type(m, t_allocator), // allocator
};
fields[0] = lb_type(m, internal_type->Struct.fields[0]->type);
fields[1] = LLVMStructTypeInContext(ctx, entries_fields, gb_count_of(entries_fields), false);
{ // Add this to simplify things
lbStructFieldRemapping entries_field_remapping = {};
slice_init(&entries_field_remapping, permanent_allocator(), gb_count_of(entries_fields));
for_array(i, entries_field_remapping) {
entries_field_remapping[i] = cast(i32)i;
}
map_set(&m->struct_field_remapping, cast(void *)fields[1], entries_field_remapping);
}
return LLVMStructTypeInContext(ctx, fields, field_count, false);
}
case Type_Struct: case Type_Struct:
{ {
@@ -2000,7 +1987,15 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
} }
field_remapping[field_index] = cast(i32)fields.count; field_remapping[field_index] = cast(i32)fields.count;
array_add(&fields, lb_type(m, field->type));
Type *field_type = field->type;
if (is_type_proc(field_type)) {
// NOTE(bill, 2022-11-23): Prevent type cycle declaration (e.g. vtable) of procedures
// because LLVM is dumb with procedure types
field_type = t_rawptr;
}
array_add(&fields, lb_type(m, field_type));
if (!type->Struct.is_packed) { if (!type->Struct.is_packed) {
padding_offset = align_formula(padding_offset, type_align_of(field->type)); padding_offset = align_formula(padding_offset, type_align_of(field->type));
@@ -2590,6 +2585,15 @@ lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule *m, String co
lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) { lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) {
if (e->flags & EntityFlag_Param) {
// NOTE(bill): Bypass the stack copied variable for
// direct parameters as there is no need for the direct load
auto *found = map_get(&p->direct_parameters, e);
if (found) {
return *found;
}
}
auto *found = map_get(&m->values, e); auto *found = map_get(&m->values, e);
if (found) { if (found) {
auto v = *found; auto v = *found;

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